From e6323dc5a1a83fb03fa1c9a66924e7af80cfdee8 Mon Sep 17 00:00:00 2001 From: Madhav Puri Date: Sun, 27 Mar 2016 22:47:14 -0700 Subject: [PATCH 1/2] use contiv/executor to allow better control of ansible execution Signed-off-by: Madhav Puri --- management/src/ansible/ansible.go | 26 ++++++++++++------- management/src/clusterm/manager/events.go | 6 ++--- management/src/configuration/ansible.go | 22 +++++++++------- management/src/configuration/configuration.go | 8 +++--- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/management/src/ansible/ansible.go b/management/src/ansible/ansible.go index 2b29df5..9663d21 100644 --- a/management/src/ansible/ansible.go +++ b/management/src/ansible/ansible.go @@ -5,7 +5,10 @@ import ( "os" "os/exec" + "golang.org/x/net/context" + log "github.com/Sirupsen/logrus" + "github.com/contiv/executor" ) // Runner facillitaes running a playbook on specifed inventory @@ -15,41 +18,44 @@ type Runner struct { user string privKeyFile string extraVars string + ctxt context.Context } -// NewRunner returns an instance of Runner for specified playbook and inventory -func NewRunner(inventory Inventory, playbook, user, privKeyFile, extraVars string) *Runner { +// NewRunner returns an instance of Runner for specified playbook and inventory. +// The caller passes a ctxt that can be used to control runner's state using a +// cancellable context or a timeout based context or a dummy context if no control is desired. +func NewRunner(inventory Inventory, playbook, user, privKeyFile, extraVars string, ctxt context.Context) *Runner { return &Runner{ inventory: inventory, playbook: playbook, user: user, privKeyFile: privKeyFile, extraVars: extraVars, + ctxt: ctxt, } } // Run runs a playbook and return's it's status as well the stdout and // stderr outputs respectively. func (r *Runner) Run(stdout, stderr io.Writer) error { - var ( - hostsFile *os.File - cmd *exec.Cmd - err error - ) - if hostsFile, err = NewInventoryFile(r.inventory); err != nil { + hostsFile, err := NewInventoryFile(r.inventory) + if err != nil { return err } defer os.Remove(hostsFile.Name()) log.Debugf("going to run playbook: %q with hosts file: %q and vars: %s", r.playbook, hostsFile.Name(), r.extraVars) - cmd = exec.Command("ansible-playbook", "-i", hostsFile.Name(), "--user", r.user, + cmd := exec.Command("ansible-playbook", "-i", hostsFile.Name(), "--user", r.user, "--private-key", r.privKeyFile, "--extra-vars", r.extraVars, r.playbook) // turn off host key checking as we are in non-interactive mode cmd.Env = append(cmd.Env, "ANSIBLE_HOST_KEY_CHECKING=false") cmd.Stdout = stdout cmd.Stderr = stderr - if err = cmd.Run(); err != nil { + e := executor.New(cmd) + res, err := e.Run(r.ctxt) + if err != nil { return err } + log.Debugf("executor result: %s", res) return nil } diff --git a/management/src/clusterm/manager/events.go b/management/src/clusterm/manager/events.go index 7f48d95..b1181d3 100644 --- a/management/src/clusterm/manager/events.go +++ b/management/src/clusterm/manager/events.go @@ -332,7 +332,7 @@ func (e *nodeConfigure) process() error { hostInfo.SetGroup(nodeGroup) hostInfo.SetVar(ansibleEtcdMasterAddrHostVar, masterAddr) hostInfo.SetVar(ansibleEtcdMasterNameHostVar, masterName) - outReader, errCh := e.mgr.configuration.Configure( + outReader, _, errCh := e.mgr.configuration.Configure( configuration.SubsysHosts([]*configuration.AnsibleHost{hostInfo}), e.extraVars) if err := logOutputAndReturnStatus(outReader, errCh); err != nil { log.Errorf("configuration failed. Error: %s", err) @@ -379,7 +379,7 @@ func (e *nodeCleanup) process() error { return nodeConfigNotExistsError(e.nodeName) } - outReader, errCh := e.mgr.configuration.Cleanup( + outReader, _, errCh := e.mgr.configuration.Cleanup( configuration.SubsysHosts([]*configuration.AnsibleHost{ e.mgr.nodes[e.nodeName].Cfg.(*configuration.AnsibleHost), }), e.extraVars) @@ -422,7 +422,7 @@ func (e *nodeUpgrade) process() error { return nodeConfigNotExistsError(e.nodeName) } - outReader, errCh := e.mgr.configuration.Upgrade( + outReader, _, errCh := e.mgr.configuration.Upgrade( configuration.SubsysHosts([]*configuration.AnsibleHost{ e.mgr.nodes[e.nodeName].Cfg.(*configuration.AnsibleHost), }), e.extraVars) diff --git a/management/src/configuration/ansible.go b/management/src/configuration/ansible.go index 01f635c..f354c66 100644 --- a/management/src/configuration/ansible.go +++ b/management/src/configuration/ansible.go @@ -5,6 +5,8 @@ import ( "io" "strings" + "golang.org/x/net/context" + "github.com/contiv/cluster/management/src/ansible" "github.com/contiv/errored" "github.com/imdario/mergo" @@ -112,7 +114,7 @@ func mergeExtraVars(dst, src string) (string, error) { return string(o), nil } -func (a *AnsibleSubsys) ansibleRunner(nodes []*AnsibleHost, playbook, extraVars string) (io.Reader, chan error) { +func (a *AnsibleSubsys) ansibleRunner(nodes []*AnsibleHost, playbook, extraVars string) (io.Reader, context.CancelFunc, chan error) { // make error channel buffered, so it doesn't block errCh := make(chan error, 1) @@ -130,20 +132,22 @@ func (a *AnsibleSubsys) ansibleRunner(nodes []*AnsibleHost, playbook, extraVars vars, err := mergeExtraVars(vars, a.config.ExtraVariables) if err != nil { errCh <- err - return nil, errCh + return nil, nil, errCh } vars, err = mergeExtraVars(vars, a.globalExtraVars) if err != nil { errCh <- err - return nil, errCh + return nil, nil, errCh } vars, err = mergeExtraVars(vars, extraVars) if err != nil { errCh <- err - return nil, errCh + return nil, nil, errCh } - runner := ansible.NewRunner(ansible.NewInventory(iNodes), playbook, a.config.User, a.config.PrivKeyFile, vars) + ctxt, cancelFunc := context.WithCancel(context.Background()) + runner := ansible.NewRunner(ansible.NewInventory(iNodes), playbook, a.config.User, + a.config.PrivKeyFile, vars, ctxt) r, w := io.Pipe() go func(outStream io.Writer, errCh chan error) { defer r.Close() @@ -154,23 +158,23 @@ func (a *AnsibleSubsys) ansibleRunner(nodes []*AnsibleHost, playbook, extraVars errCh <- nil return }(w, errCh) - return r, errCh + return r, cancelFunc, errCh } // Configure triggers the ansible playbook for configuration on specified nodes -func (a *AnsibleSubsys) Configure(nodes SubsysHosts, extraVars string) (io.Reader, chan error) { +func (a *AnsibleSubsys) Configure(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) { return a.ansibleRunner(nodes.([]*AnsibleHost), strings.Join([]string{a.config.PlaybookLocation, a.config.ConfigurePlaybook}, "/"), extraVars) } // Cleanup triggers the ansible playbook for cleanup on specified nodes -func (a *AnsibleSubsys) Cleanup(nodes SubsysHosts, extraVars string) (io.Reader, chan error) { +func (a *AnsibleSubsys) Cleanup(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) { return a.ansibleRunner(nodes.([]*AnsibleHost), strings.Join([]string{a.config.PlaybookLocation, a.config.CleanupPlaybook}, "/"), extraVars) } // Upgrade triggers the ansible playbook for upgrade on specified nodes -func (a *AnsibleSubsys) Upgrade(nodes SubsysHosts, extraVars string) (io.Reader, chan error) { +func (a *AnsibleSubsys) Upgrade(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) { return a.ansibleRunner(nodes.([]*AnsibleHost), strings.Join([]string{a.config.PlaybookLocation, a.config.UpgradePlaybook}, "/"), extraVars) } diff --git a/management/src/configuration/configuration.go b/management/src/configuration/configuration.go index bee2e71..4e3df24 100644 --- a/management/src/configuration/configuration.go +++ b/management/src/configuration/configuration.go @@ -3,6 +3,8 @@ package configuration import ( "encoding/json" "io" + + "golang.org/x/net/context" ) // Subsys provides the following services to the cluster manager: @@ -11,13 +13,13 @@ import ( type Subsys interface { // Configure triggers the configuration logic on specified set of nodes. // It return a error channel that the caller can wait on to get completion status. - Configure(nodes SubsysHosts, extraVars string) (io.Reader, chan error) + Configure(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) // Cleanup triggers the configuration cleanup on specified set of nodes. // It return a error channel that the caller can wait on to get completion status. - Cleanup(nodes SubsysHosts, extraVars string) (io.Reader, chan error) + Cleanup(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) // Cleanup triggers the configuration upgrade on specified set of nodes. // It return a error channel that the caller can wait on to get completion status. - Upgrade(nodes SubsysHosts, extraVars string) (io.Reader, chan error) + Upgrade(nodes SubsysHosts, extraVars string) (io.Reader, context.CancelFunc, chan error) // SetGlobals sets the extra vars at a configuration subsys level SetGlobals(extraVars string) error // GetGlobals return the value of extra vars at a configuration subsys level From 1d6033624d9cc9686951d27944aabbcef44c61cc Mon Sep 17 00:00:00 2001 From: Madhav Puri Date: Sun, 27 Mar 2016 22:48:20 -0700 Subject: [PATCH 2/2] update godeps to pull contiv/executor and net/context Signed-off-by: Madhav Puri --- management/src/Godeps/Godeps.json | 9 + .../github.com/contiv/executor/.gitignore | 1 + .../github.com/contiv/executor/.travis.yml | 5 + .../github.com/contiv/executor/README.md | 47 ++ .../github.com/contiv/executor/executor.go | 263 +++++++++++ .../src/vendor/golang.org/x/net/LICENSE | 27 ++ .../src/vendor/golang.org/x/net/PATENTS | 22 + .../golang.org/x/net/context/context.go | 447 ++++++++++++++++++ 8 files changed, 821 insertions(+) create mode 100644 management/src/vendor/github.com/contiv/executor/.gitignore create mode 100644 management/src/vendor/github.com/contiv/executor/.travis.yml create mode 100644 management/src/vendor/github.com/contiv/executor/README.md create mode 100644 management/src/vendor/github.com/contiv/executor/executor.go create mode 100644 management/src/vendor/golang.org/x/net/LICENSE create mode 100644 management/src/vendor/golang.org/x/net/PATENTS create mode 100644 management/src/vendor/golang.org/x/net/context/context.go diff --git a/management/src/Godeps/Godeps.json b/management/src/Godeps/Godeps.json index fdf1aef..2c9e14e 100644 --- a/management/src/Godeps/Godeps.json +++ b/management/src/Godeps/Godeps.json @@ -10,6 +10,7 @@ "github.com/contiv/cluster/management/src/collins", "github.com/contiv/cluster/management/src/configuration", "github.com/contiv/cluster/management/src/inventory", + "github.com/contiv/cluster/management/src/mock", "github.com/contiv/cluster/management/src/monitor", "github.com/contiv/cluster/management/src/systemtests" ], @@ -36,6 +37,10 @@ "ImportPath": "github.com/contiv/errored", "Rev": "01a98ff0a680ae5702f3e07e03b11cf31ca69108" }, + { + "ImportPath": "github.com/contiv/executor", + "Rev": "1c3e497e2b9da2c15caccfb77b52393fe6c101b5" + }, { "ImportPath": "github.com/contiv/systemtests-utils", "Rev": "c8687a787fd4e395fa2285abb2c03ccef80402c7" @@ -141,6 +146,10 @@ "ImportPath": "golang.org/x/crypto/ssh/terminal", "Rev": "7b85b097bf7527677d54d3220065e966a0e3b613" }, + { + "ImportPath": "golang.org/x/net/context", + "Rev": "76365a41624aa608f888301bf60fabecaf928b5a" + }, { "ImportPath": "gopkg.in/check.v1", "Rev": "11d3bc7aa68e238947792f30573146a3231fc0f1" diff --git a/management/src/vendor/github.com/contiv/executor/.gitignore b/management/src/vendor/github.com/contiv/executor/.gitignore new file mode 100644 index 0000000..b51c70b --- /dev/null +++ b/management/src/vendor/github.com/contiv/executor/.gitignore @@ -0,0 +1 @@ +cover.out diff --git a/management/src/vendor/github.com/contiv/executor/.travis.yml b/management/src/vendor/github.com/contiv/executor/.travis.yml new file mode 100644 index 0000000..e7efe16 --- /dev/null +++ b/management/src/vendor/github.com/contiv/executor/.travis.yml @@ -0,0 +1,5 @@ +language: go + +go: + - 1.6 + - tip diff --git a/management/src/vendor/github.com/contiv/executor/README.md b/management/src/vendor/github.com/contiv/executor/README.md new file mode 100644 index 0000000..879a381 --- /dev/null +++ b/management/src/vendor/github.com/contiv/executor/README.md @@ -0,0 +1,47 @@ +[![ReportCard][ReportCard-Image]][ReportCard-URL] [![Build][Build-Status-Image]][Build-Status-URL] [![GoDoc][GoDoc-Image]][GoDoc-URL] + +## Executor: flexible, high-level exec.Cmd for golang + +Package executor implements a high level execution context with monitoring, +control, and logging features. It is made for services which execute lots of +small programs and need to carefully control i/o and processes. + +Executor can: + + * Terminate on signal or after a timeout via /x/net/context + * Output a message on an interval if the program is still running. + * Capture split-stream stdio, and make it easier to get at io pipes. + +Example: + +```go + e := executor.New(exec.Command("/bin/sh", "echo hello")) + e.Start() // start + fmt.Println(e.PID()) // get the pid + fmt.Printf("%v\n", e) // pretty string output + er, err := e.Wait(context.Background()) // wait for termination + fmt.Println(er.ExitStatus) // => 0 + + // lets capture some io, and timeout after a while + e := executor.NewCapture(exec.Command("/bin/sh", "yes")) + e.Start() + ctx, _ := context.WithTimeout(context.Background(), 10 * time.Second) + er, err := e.Wait(ctx) // wait for only 10 seconds + fmt.Println(err == context.DeadlineExceeded) + fmt.Println(er.Stdout) // yes\nyes\nyes\n... +``` + +## Authors: + +* Erik Hollensbe + +## Sponsorship + +Project Contiv is sponsored by Cisco Systems, Inc. + +[ReportCard-URL]: https://goreportcard.com/report/github.com/contiv/executor +[ReportCard-Image]: http://goreportcard.com/badge/contiv/executor +[Build-Status-URL]: http://travis-ci.org/contiv/executor +[Build-Status-Image]: https://travis-ci.org/contiv/executor.svg?branch=master +[GoDoc-URL]: https://godoc.org/github.com/contiv/executor +[GoDoc-Image]: https://godoc.org/github.com/contiv/executor?status.svg diff --git a/management/src/vendor/github.com/contiv/executor/executor.go b/management/src/vendor/github.com/contiv/executor/executor.go new file mode 100644 index 0000000..1f2487c --- /dev/null +++ b/management/src/vendor/github.com/contiv/executor/executor.go @@ -0,0 +1,263 @@ +// Package executor implements a high level execution context with monitoring, +// control, and logging features. It is made for services which execute lots of +// small programs and need to carefully control i/o and processes. +// +// Executor can: +// +// * Terminate on signal or after a timeout via /x/net/context +// * Output a message on an interval if the program is still running. +// * Capture split-stream stdio, and make it easier to get at io pipes. +// +// Example: +// +// e := executor.New(exec.Command("/bin/sh", "echo hello")) +// e.Start() // start +// fmt.Println(e.PID()) // get the pid +// fmt.Printf("%v\n", e) // pretty string output +// er, err := e.Wait(context.Background()) // wait for termination +// fmt.Println(er.ExitStatus) // => 0 +// +// // lets capture some io, and timeout after a while +// e := executor.NewCapture(exec.Command("/bin/sh", "yes")) +// e.Start() +// ctx, _ := context.WithTimeout(context.Background(), 10 * time.Second) +// er, err := e.Wait(ctx) // wait for only 10 seconds +// fmt.Println(err == context.DeadlineExceeded) +// fmt.Println(er.Stdout) // yes\nyes\nyes\n... +// +package executor + +import ( + "bytes" + "fmt" + "io" + "os/exec" + "syscall" + "time" + + "golang.org/x/net/context" + + "github.com/Sirupsen/logrus" +) + +// ExecResult is the result of a Wait() operation and contains various fields +// related to the post-mortem state of the process such as output and exit +// status. +type ExecResult struct { + Stdout string + Stderr string + ExitStatus uint32 + Runtime time.Duration + + executor *Executor +} + +// Executor is the context used to execute a process. The runtime state is kept +// here. Please see the struct fields for more information. +// +// New(), NewIO(), or NewCapture() are the appropriate ways to initialize this type. +// +// No attempt is made to manage concurrent requests to this struct after +// the program has started. +type Executor struct { + // The interval at which we will log that we are still running. + LogInterval time.Duration + + // The function used for logging. Expects a format-style string and trailing args. + LogFunc func(string, ...interface{}) + + // The stdin as passed to the process. + Stdin io.Reader + + io bool + capture bool + command *exec.Cmd + stdout io.ReadCloser + stderr io.ReadCloser + stdoutBuf *bytes.Buffer + stderrBuf *bytes.Buffer + startTime time.Time + terminateLogger chan struct{} +} + +// New creates a new executor from an *exec.Cmd. You may modify the values +// before calling Start(). See Executor for more information. Use NewCapture if +// you want executor to capture output for you. +func New(cmd *exec.Cmd) *Executor { + return newExecutor(false, false, cmd) +} + +// NewIO creates a new executor but allows the Out() and Err() methods to provide +// a io.ReadCloser as a pipe from the stdout and error respectively. If you +// wish to read large volumes of output this is the way to go. +func NewIO(cmd *exec.Cmd) *Executor { + return newExecutor(true, false, cmd) +} + +// NewCapture creates an instance of executor suitable for capturing output. +// The Wait() call will automatically yield the stdout and stderr of the +// program. NOTE: this can potentially use unbounded amounts of ram; use carefully. +func NewCapture(cmd *exec.Cmd) *Executor { + return newExecutor(true, true, cmd) +} + +func newExecutor(useIO, useCapture bool, cmd *exec.Cmd) *Executor { + return &Executor{ + io: useIO, + capture: useCapture, + LogInterval: 1 * time.Minute, + LogFunc: logrus.Debugf, + command: cmd, + stdout: nil, + stderr: nil, + stdoutBuf: nil, + stderrBuf: nil, + terminateLogger: make(chan struct{}), + } +} + +func (e *Executor) String() string { + return fmt.Sprintf("%v (%v) (pid: %v)", e.command.Args, e.command.Path, e.PID()) +} + +// Start starts the command in the Executor context. It returns any error upon +// starting the process, but does not wait for it to complete. You may control +// it in a variety of ways (see Executor for more information). +func (e *Executor) Start() error { + e.command.Stdin = e.Stdin + + e.startTime = time.Now() + + var err error + + if e.io { + e.stdout, err = e.command.StdoutPipe() + if err != nil { + return err + } + + e.stderr, err = e.command.StderrPipe() + if err != nil { + return err + } + + if e.capture { + e.stdoutBuf = new(bytes.Buffer) + go io.Copy(e.stdoutBuf, e.stdout) + + e.stderrBuf = new(bytes.Buffer) + go io.Copy(e.stderrBuf, e.stderr) + } + } + + if err := e.command.Start(); err != nil { + e.LogFunc("Error executing %v: %v", e, err) + return err + } + + go e.logInterval() + + return nil +} + +// TimeRunning returns the amount of time the program is or was running. Also +// see ExecResult.Runtime. +func (e *Executor) TimeRunning() time.Duration { + return time.Now().Sub(e.startTime) +} + +func (e *Executor) logInterval() { + for { + select { + case <-e.terminateLogger: + return + case <-time.After(e.LogInterval): + e.LogFunc("%v has been running for %v", e, e.TimeRunning()) + } + } +} + +// PID yields the pid of the process (dead or alive), or 0 if the process has +// not been run yet. +func (e *Executor) PID() uint32 { + if e.command.Process != nil { + return uint32(e.command.Process.Pid) + } + + return 0 +} + +// Wait waits for the process and return an ExecResult and any error it +// encountered along the way. While the error may or may not be nil, the +// ExecResult will always exist with as much information as we could get. +// +// Context is from https://godoc.org/golang.org/x/net/context (see +// https://blog.golang.org/context for usage). You can use it to set timeouts +// and cancel executions. +func (e *Executor) Wait(ctx context.Context) (*ExecResult, error) { + defer close(e.terminateLogger) + + var err error + errChan := make(chan error, 1) + + go func() { errChan <- e.command.Wait() }() + + select { + case <-ctx.Done(): + if e.command.Process == nil { + e.LogFunc("Could not terminate non-running command %v", e) + } else { + e.LogFunc("Command %v terminated due to timeout or cancellation. It may not have finished!", e) + e.command.Process.Kill() + } + err = ctx.Err() + case err = <-errChan: + } + + res := &ExecResult{executor: e} + exitErr := err + + if err != nil { + if exit, ok := err.(*exec.ExitError); ok { + res.ExitStatus = uint32(exit.Sys().(syscall.WaitStatus)) + } + } + + if e.capture { + res.Stdout = string(e.stdoutBuf.Bytes()) + res.Stderr = string(e.stderrBuf.Bytes()) + } + + res.Runtime = e.TimeRunning() + + return res, exitErr +} + +// Run calls Start(), then Wait(), and returns an ExecResult and error (if +// any). If an error is returned, ExecResult will be nil. +func (e *Executor) Run(ctx context.Context) (*ExecResult, error) { + if err := e.Start(); err != nil { + return nil, err + } + + er, err := e.Wait(ctx) + if err != nil { + return nil, err + } + + return er, nil +} + +// Out returns an *os.File which is the stream of the standard output stream. +func (e *Executor) Out() io.ReadCloser { + return e.stdout +} + +// Err returns an io.ReadCloser which is the stream of the standard error stream. +func (e *Executor) Err() io.ReadCloser { + return e.stderr +} + +func (er *ExecResult) String() string { + return fmt.Sprintf("Command: %v, Exit status %v, Runtime %v", er.executor.command.Args, er.ExitStatus, er.Runtime) +} diff --git a/management/src/vendor/golang.org/x/net/LICENSE b/management/src/vendor/golang.org/x/net/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/management/src/vendor/golang.org/x/net/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/management/src/vendor/golang.org/x/net/PATENTS b/management/src/vendor/golang.org/x/net/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/management/src/vendor/golang.org/x/net/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/management/src/vendor/golang.org/x/net/context/context.go b/management/src/vendor/golang.org/x/net/context/context.go new file mode 100644 index 0000000..11bd8d3 --- /dev/null +++ b/management/src/vendor/golang.org/x/net/context/context.go @@ -0,0 +1,447 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out <-chan Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) cancelCtx { + return cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +}