Repository: coreos/fleet
Pull request #1646 (2016-07-20 12:34:26)

systemd,agent: unload unit only when TriggerStop() runs successfully
In ``Agent.unloadUnit()``, if ``systemdUnitManager.TriggerStop()`` returns any error, do not unload systemd units. Otherwise the unit could get into a state where the unit cannot be stopped via fleet, because the unit file was already removed.

Fixes https://github.com/coreos/fleet/issues/1216
/cc @patrickbcullen


Patch:
diff --git a/agent/agent.go b/agent/agent.go
index 1b6ad207..e580a9ec 100644
--- a/agent/agent.go
+++ b/agent/agent.go
@@ -98,11 +98,22 @@ func (a *Agent) unloadUnit(unitName string) {
 	a.registry.ClearUnitHeartbeat(unitName)
 	a.cache.dropTargetState(unitName)
 
-	a.um.TriggerStop(unitName)
+	errStop := a.um.TriggerStop(unitName)
+	if errStop != nil {
+		log.Warningf("Failed stopping unit(%s): %v", unitName, errStop)
+	} else {
+		log.Infof("Stopped unit(%s)", unitName)
+	}
 
 	a.uGen.Unsubscribe(unitName)
 
-	a.um.Unload(unitName)
+	// unit should be unloaded and unit file should be removed, only if the unit
+	// could be successfully stopped. Otherwise the unit could get into a state
+	// where the unit cannot be stopped via fleet, because the unit file was
+	// already removed. See also https://github.com/coreos/fleet/issues/1216.
+	if errStop == nil {
+		a.um.Unload(unitName)
+	}
 }
 
 func (a *Agent) startUnit(unitName string) {
diff --git a/agent/unit_state.go b/agent/unit_state.go
index a93f1369..79dc9f6b 100644
--- a/agent/unit_state.go
+++ b/agent/unit_state.go
@@ -207,9 +207,12 @@ func newPublisher(reg registry.Registry, ttl time.Duration) publishFunc {
 			// Sanity check - don't want to publish incomplete UnitStates
 			// TODO(jonboulle): consider teasing apart a separate UnitState-like struct
 			// so we can rely on a UnitState always being fully hydrated?
-			if len(us.UnitHash) == 0 {
-				log.Errorf("Refusing to push UnitState(%s), no UnitHash: %#v", name, us)
-			} else if len(us.MachineID) == 0 {
+
+			// See https://github.com/coreos/fleet/issues/720
+			//if len(us.UnitHash) == 0 {
+			//	log.Errorf("Refusing to push UnitState(%s), no UnitHash: %#v", name, us)
+
+			if len(us.MachineID) == 0 {
 				log.Errorf("Refusing to push UnitState(%s), no MachineID: %#v", name, us)
 			} else {
 				log.Debugf("Pushing UnitState(%s) to Registry: %#v", name, us)
diff --git a/fleetctl/fleetctl.go b/fleetctl/fleetctl.go
index 5fa0d04b..638efa71 100644
--- a/fleetctl/fleetctl.go
+++ b/fleetctl/fleetctl.go
@@ -226,7 +226,7 @@ func main() {
 	log.EnableTimestamps()
 
 	if len(os.Args) == 1 {
-		cmdFleet.HelpFunc()(cmdFleet, os.Args)
+		cmdFleet.Help()
 		os.Exit(0)
 	}
 
diff --git a/glide.lock b/glide.lock
index dbd90bfa..04cffc2b 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,78 +1,78 @@
 hash: 761e443b236a2651920ea60a7ed23e0547842be2e7f61e19fe1fe37ce76486c9
-updated: 2016-07-19T14:10:09.754763774+02:00
+updated: 2016-06-22T11:56:09.50904802+02:00
 imports:
 - name: github.com/beorn7/perks
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
   subpackages:
   - quantile
 - name: github.com/coreos/etcd
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 0b5ea3ec947a649d39b25c96e33ac1c743355be3
   subpackages:
   - client
   - pkg/pathutil
   - pkg/types
 - name: github.com/coreos/go-semver
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 294930c1e79c64e7dbe360054274fdad492c8cf5
   subpackages:
   - semver
 - name: github.com/coreos/go-systemd
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: b32b8467dbea18858bfebf65c1a6a761090f2c31
   subpackages:
   - activation
   - dbus
   - unit
-- name: github.com/glacjay/goini
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
 - name: github.com/godbus/dbus
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 32c6cc29c14570de4cf6d7e7737d68fb2d01ad15
 - name: github.com/golang/protobuf
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 0c1f6d65b5a189c2250d10e71a5506f06f9fa0a0
   subpackages:
   - proto
 - name: github.com/inconshreveable/mousetrap
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 - name: github.com/jonboulle/clockwork
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 2eee05ed794112d45db504eb05aa693efd2b8b09
 - name: github.com/matttproud/golang_protobuf_extensions
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: c12348ce28de40eed0136aa2b644d0ee0650e56c
   subpackages:
   - pbutil
 - name: github.com/pborman/uuid
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: c55201b036063326c5b1b89ccfe45a184973d073
 - name: github.com/prometheus/client_golang
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 488edd04dc224ba64c401747cd0a4b5f05dfb234
   subpackages:
   - prometheus
 - name: github.com/prometheus/client_model
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
   subpackages:
   - go
 - name: github.com/prometheus/common
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 3a184ff7dfd46b9091030bf2e56c71112b0ddb0e
   subpackages:
   - expfmt
   - internal/bitbucket.org/ww/goautoneg
   - model
 - name: github.com/prometheus/procfs
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5
 - name: github.com/rakyll/globalconf
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 415abc325023f1a00cd2d9fa512e0e71745791a2
+- name: github.com/rakyll/goini
+  version: 907cca0f578a5316fb864ec6992dc3d9730ec58c
 - name: github.com/spf13/cobra
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 6a8bd97bdb1fc0d08a83459940498ea49d3e8c93
 - name: github.com/spf13/pflag
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
 - name: github.com/ugorji/go
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: b94837a2404ab90efe9289e77a70694c355739cb
   subpackages:
   - codec
 - name: github.com/vishvananda/netlink
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 734d02c3e202f682c74b71314b2c61eec0170fd4
   subpackages:
   - nl
 - name: github.com/vishvananda/netns
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 8ba1072b58e0c2a240eb5f6120165c7776c3e7b8
 - name: golang.org/x/crypto
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: f3241ce8505855877cc8a9717bd61a0f7c4ea83c
   subpackages:
   - ssh
   - ssh/agent
@@ -81,12 +81,12 @@ imports:
   - ed25519
   - ed25519/internal/edwards25519
 - name: golang.org/x/net
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: bc3663df0ac92f928d419e31e0d2af22e683a5a2
   subpackages:
   - context
 - name: google.golang.org/api
-  version: 4a8dce34cb7176d68b9031857e7a0b94e1413421
+  version: 63ade871fd3aec1225809d496e81ec91ab76ea29
   subpackages:
   - googleapi
   - googleapi/internal/uritemplates
-testImports: []
+devImports: []
diff --git a/registry/unit_state.go b/registry/unit_state.go
index c8a13bf1..fef6590b 100644
--- a/registry/unit_state.go
+++ b/registry/unit_state.go
@@ -224,9 +224,10 @@ func unitStateToModel(us *unit.UnitState) *unitStateModel {
 	}
 
 	// Refuse to create a UnitState without a Hash
-	if len(us.UnitHash) == 0 {
-		return nil
-	}
+	// See https://github.com/coreos/fleet/issues/720
+	//if len(us.UnitHash) == 0 {
+	//	return nil
+	//}
 
 	usm := unitStateModel{
 		LoadState:   us.LoadState,
diff --git a/scripts/glide-update.sh b/scripts/glide-update.sh
index 12212f2d..fe12d33d 100755
--- a/scripts/glide-update.sh
+++ b/scripts/glide-update.sh
@@ -4,8 +4,8 @@
 #
 set -e
 
-if ! [[ "$PWD" = "$GOPATH/src/github.com/coreos/fleet" ]]; then
-	echo "must be run from \$GOPATH/src/github.com/coreos/fleet"
+if ! [[ "$0" =~ "scripts/glide-update.sh" ]]; then
+	echo "must be run from repository root"
 	exit 255
 fi
 
diff --git a/systemd/manager.go b/systemd/manager.go
index 4ca28f6c..a7b4dd54 100644
--- a/systemd/manager.go
+++ b/systemd/manager.go
@@ -123,24 +123,26 @@ func (m *systemdUnitManager) Unload(name string) {
 
 // TriggerStart asynchronously starts the unit identified by the given name.
 // This function does not block for the underlying unit to actually start.
-func (m *systemdUnitManager) TriggerStart(name string) {
+func (m *systemdUnitManager) TriggerStart(name string) error {
 	jobID, err := m.systemd.StartUnit(name, "replace", nil)
-	if err == nil {
-		log.Infof("Triggered systemd unit %s start: job=%d", name, jobID)
-	} else {
+	if err != nil {
 		log.Errorf("Failed to trigger systemd unit %s start: %v", name, err)
+		return err
 	}
+	log.Infof("Triggered systemd unit %s start: job=%d", name, jobID)
+	return nil
 }
 
 // TriggerStop asynchronously starts the unit identified by the given name.
 // This function does not block for the underlying unit to actually stop.
-func (m *systemdUnitManager) TriggerStop(name string) {
+func (m *systemdUnitManager) TriggerStop(name string) error {
 	jobID, err := m.systemd.StopUnit(name, "replace", nil)
-	if err == nil {
-		log.Infof("Triggered systemd unit %s stop: job=%d", name, jobID)
-	} else {
+	if err != nil {
 		log.Errorf("Failed to trigger systemd unit %s stop: %v", name, err)
+		return err
 	}
+	log.Infof("Triggered systemd unit %s stop: job=%d", name, jobID)
+	return nil
 }
 
 // GetUnitState generates a UnitState object representing the
diff --git a/unit/fake.go b/unit/fake.go
index 3d805947..4a075764 100644
--- a/unit/fake.go
+++ b/unit/fake.go
@@ -48,8 +48,8 @@ func (fum *FakeUnitManager) Unload(name string) {
 	delete(fum.u, name)
 }
 
-func (fum *FakeUnitManager) TriggerStart(string) {}
-func (fum *FakeUnitManager) TriggerStop(string)  {}
+func (fum *FakeUnitManager) TriggerStart(string) error { return nil }
+func (fum *FakeUnitManager) TriggerStop(string) error  { return nil }
 
 func (fum *FakeUnitManager) Units() ([]string, error) {
 	fum.RLock()
diff --git a/unit/manager.go b/unit/manager.go
index 145b2c07..40918c9d 100644
--- a/unit/manager.go
+++ b/unit/manager.go
@@ -23,8 +23,8 @@ type UnitManager interface {
 	Unload(string)
 	ReloadUnitFiles() error
 
-	TriggerStart(string)
-	TriggerStop(string)
+	TriggerStart(string) error
+	TriggerStop(string) error
 
 	Units() ([]string, error)
 	GetUnitStates(pkg.Set) (map[string]*UnitState, error)
diff --git a/vendor/github.com/coreos/etcd/client/client.go b/vendor/github.com/coreos/etcd/client/client.go
index 4c4d41eb..fb9c4599 100644
--- a/vendor/github.com/coreos/etcd/client/client.go
+++ b/vendor/github.com/coreos/etcd/client/client.go
@@ -37,10 +37,6 @@ var (
 	ErrClusterUnavailable    = errors.New("client: etcd cluster is unavailable or misconfigured")
 	ErrNoLeaderEndpoint      = errors.New("client: no leader endpoint available")
 	errTooManyRedirectChecks = errors.New("client: too many redirect checks")
-
-	// oneShotCtxValue is set on a context using WithValue(&oneShotValue) so
-	// that Do() will not retry a request
-	oneShotCtxValue interface{}
 )
 
 var DefaultRequestTimeout = 5 * time.Second
@@ -305,7 +301,7 @@ func (c *httpClusterClient) SetEndpoints(eps []string) error {
 		// If endpoints doesn't have the lu, just keep c.pinned = 0.
 		// Forwarding between follower and leader would be required but it works.
 	default:
-		return fmt.Errorf("invalid endpoint selection mode: %d", c.selectionMode)
+		return errors.New(fmt.Sprintf("invalid endpoint selection mode: %d", c.selectionMode))
 	}
 
 	return nil
@@ -339,7 +335,6 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
 	var body []byte
 	var err error
 	cerr := &ClusterError{}
-	isOneShot := ctx.Value(&oneShotCtxValue) != nil
 
 	for i := pinned; i < leps+pinned; i++ {
 		k := i % leps
@@ -353,9 +348,6 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
 			if err == context.Canceled || err == context.DeadlineExceeded {
 				return nil, nil, err
 			}
-			if isOneShot {
-				return nil, nil, err
-			}
 			continue
 		}
 		if resp.StatusCode/100 == 5 {
@@ -366,9 +358,6 @@ func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Respo
 			default:
 				cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s returns server error [%s]", eps[k].String(), http.StatusText(resp.StatusCode)))
 			}
-			if isOneShot {
-				return nil, nil, cerr.Errors[0]
-			}
 			continue
 		}
 		if k != pinned {
@@ -404,7 +393,7 @@ func (c *httpClusterClient) Sync(ctx context.Context) error {
 	c.Lock()
 	defer c.Unlock()
 
-	var eps []string
+	eps := make([]string, 0)
 	for _, m := range ms {
 		eps = append(eps, m.ClientURLs...)
 	}
diff --git a/vendor/github.com/coreos/etcd/client/cluster_error.go b/vendor/github.com/coreos/etcd/client/cluster_error.go
index 34618cdb..aef5bf75 100644
--- a/vendor/github.com/coreos/etcd/client/cluster_error.go
+++ b/vendor/github.com/coreos/etcd/client/cluster_error.go
@@ -21,11 +21,7 @@ type ClusterError struct {
 }
 
 func (ce *ClusterError) Error() string {
-	s := ErrClusterUnavailable.Error()
-	for i, e := range ce.Errors {
-		s += fmt.Sprintf("; error #%d: %s\n", i, e)
-	}
-	return s
+	return ErrClusterUnavailable.Error()
 }
 
 func (ce *ClusterError) Detail() string {
diff --git a/vendor/github.com/coreos/etcd/client/keys.go b/vendor/github.com/coreos/etcd/client/keys.go
index 62d5d506..5c7a3d57 100644
--- a/vendor/github.com/coreos/etcd/client/keys.go
+++ b/vendor/github.com/coreos/etcd/client/keys.go
@@ -337,11 +337,7 @@ func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions
 		act.Dir = opts.Dir
 	}
 
-	doCtx := ctx
-	if act.PrevExist == PrevNoExist {
-		doCtx = context.WithValue(doCtx, &oneShotCtxValue, &oneShotCtxValue)
-	}
-	resp, body, err := k.client.Do(doCtx, act)
+	resp, body, err := k.client.Do(ctx, act)
 	if err != nil {
 		return nil, err
 	}
@@ -389,8 +385,7 @@ func (k *httpKeysAPI) Delete(ctx context.Context, key string, opts *DeleteOption
 		act.Recursive = opts.Recursive
 	}
 
-	doCtx := context.WithValue(ctx, &oneShotCtxValue, &oneShotCtxValue)
-	resp, body, err := k.client.Do(doCtx, act)
+	resp, body, err := k.client.Do(ctx, act)
 	if err != nil {
 		return nil, err
 	}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urls.go b/vendor/github.com/coreos/etcd/pkg/types/urls.go
index 9e5d03ff..e532722a 100644
--- a/vendor/github.com/coreos/etcd/pkg/types/urls.go
+++ b/vendor/github.com/coreos/etcd/pkg/types/urls.go
@@ -36,8 +36,8 @@ func NewURLs(strs []string) (URLs, error) {
 		if err != nil {
 			return nil, err
 		}
-		if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" && u.Scheme != "unixs" {
-			return nil, fmt.Errorf("URL scheme must be http, https, unix, or unixs: %s", in)
+		if u.Scheme != "http" && u.Scheme != "https" {
+			return nil, fmt.Errorf("URL scheme must be http or https: %s", in)
 		}
 		if _, _, err := net.SplitHostPort(u.Host); err != nil {
 			return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
diff --git a/vendor/github.com/coreos/go-semver/semver/semver.go b/vendor/github.com/coreos/go-semver/semver/semver.go
index 110fc23e..000a0205 100644
--- a/vendor/github.com/coreos/go-semver/semver/semver.go
+++ b/vendor/github.com/coreos/go-semver/semver/semver.go
@@ -44,36 +44,16 @@ func splitOff(input *string, delim string) (val string) {
 	return val
 }
 
-func New(version string) *Version {
-	return Must(NewVersion(version))
-}
-
 func NewVersion(version string) (*Version, error) {
 	v := Version{}
 
-	if err := v.Set(version); err != nil {
-		return nil, err
-	}
-
-	return &v, nil
-}
-
-// Must is a helper for wrapping NewVersion and will panic if err is not nil.
-func Must(v *Version, err error) *Version {
-	if err != nil {
-		panic(err)
-	}
-	return v
-}
+	v.Metadata = splitOff(&version, "+")
+	v.PreRelease = PreRelease(splitOff(&version, "-"))
 
-// Set parses and updates v from the given version string. Implements flag.Value
-func (v *Version) Set(version string) error {
-	metadata := splitOff(&version, "+")
-	preRelease := PreRelease(splitOff(&version, "-"))
 	dotParts := strings.SplitN(version, ".", 3)
 
 	if len(dotParts) != 3 {
-		return fmt.Errorf("%s is not in dotted-tri format", version)
+		return nil, errors.New(fmt.Sprintf("%s is not in dotted-tri format", version))
 	}
 
 	parsed := make([]int64, 3, 3)
@@ -82,16 +62,22 @@ func (v *Version) Set(version string) error {
 		val, err := strconv.ParseInt(v, 10, 64)
 		parsed[i] = val
 		if err != nil {
-			return err
+			return nil, err
 		}
 	}
 
-	v.Metadata = metadata
-	v.PreRelease = preRelease
 	v.Major = parsed[0]
 	v.Minor = parsed[1]
 	v.Patch = parsed[2]
-	return nil
+
+	return &v, nil
+}
+
+func Must(v *Version, err error) *Version {
+	if err != nil {
+		panic(err)
+	}
+	return v
 }
 
 func (v Version) String() string {
@@ -115,7 +101,12 @@ func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := unmarshal(&data); err != nil {
 		return err
 	}
-	return v.Set(data)
+	vv, err := NewVersion(data)
+	if err != nil {
+		return err
+	}
+	*v = *vv
+	return nil
 }
 
 func (v Version) MarshalJSON() ([]byte, error) {
@@ -130,29 +121,30 @@ func (v *Version) UnmarshalJSON(data []byte) error {
 	if l < 2 || data[0] != '"' || data[l-1] != '"' {
 		return errors.New("invalid semver string")
 	}
-	return v.Set(string(data[1 : l-1]))
+	vv, err := NewVersion(string(data[1 : l-1]))
+	if err != nil {
+		return err
+	}
+	*v = *vv
+	return nil
 }
 
-// Compare tests if v is less than, equal to, or greater than versionB,
-// returning -1, 0, or +1 respectively.
-func (v Version) Compare(versionB Version) int {
-	if cmp := recursiveCompare(v.Slice(), versionB.Slice()); cmp != 0 {
-		return cmp
+func (v Version) LessThan(versionB Version) bool {
+	versionA := v
+	cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
+
+	if cmp == 0 {
+		cmp = preReleaseCompare(versionA, versionB)
 	}
-	return preReleaseCompare(v, versionB)
-}
 
-// Equal tests if v is equal to versionB.
-func (v Version) Equal(versionB Version) bool {
-	return v.Compare(versionB) == 0
-}
+	if cmp == -1 {
+		return true
+	}
 
-// LessThan tests if v is less than versionB.
-func (v Version) LessThan(versionB Version) bool {
-	return v.Compare(versionB) < 0
+	return false
 }
 
-// Slice converts the comparable parts of the semver into a slice of integers.
+/* Slice converts the comparable parts of the semver into a slice of strings */
 func (v Version) Slice() []int64 {
 	return []int64{v.Major, v.Minor, v.Patch}
 }
@@ -174,7 +166,7 @@ func preReleaseCompare(versionA Version, versionB Version) int {
 		return -1
 	}
 
-	// If there is a prerelease, check and compare each part.
+	// If there is a prelease, check and compare each part.
 	return recursivePreReleaseCompare(a.Slice(), b.Slice())
 }
 
@@ -196,12 +188,9 @@ func recursiveCompare(versionA []int64, versionB []int64) int {
 }
 
 func recursivePreReleaseCompare(versionA []string, versionB []string) int {
-	// A larger set of pre-release fields has a higher precedence than a smaller set,
-	// if all of the preceding identifiers are equal.
+	// Handle slice length disparity.
 	if len(versionA) == 0 {
-		if len(versionB) > 0 {
-			return -1
-		}
+		// Nothing to compare too, so we return 0
 		return 0
 	} else if len(versionB) == 0 {
 		// We're longer than versionB so return 1.
diff --git a/vendor/github.com/glacjay/goini/LICENSE.txt b/vendor/github.com/glacjay/goini/LICENSE.txt
deleted file mode 100644
index 38c6bf23..00000000
--- a/vendor/github.com/glacjay/goini/LICENSE.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) [year] [fullname]
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/golang/protobuf/proto/decode.go
index 04dcb881..07288a25 100644
--- a/vendor/github.com/golang/protobuf/proto/decode.go
+++ b/vendor/github.com/golang/protobuf/proto/decode.go
@@ -378,11 +378,6 @@ func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group
 		wire := int(u & 0x7)
 		if wire == WireEndGroup {
 			if is_group {
-				if required > 0 {
-					// Not enough information to determine the exact field.
-					// (See below.)
-					return &RequiredNotSetError{"{Unknown}"}
-				}
 				return nil // input is satisfied
 			}
 			return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
diff --git a/vendor/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/golang/protobuf/proto/extensions.go
index 482f3e97..9f484f53 100644
--- a/vendor/github.com/golang/protobuf/proto/extensions.go
+++ b/vendor/github.com/golang/protobuf/proto/extensions.go
@@ -489,34 +489,6 @@ func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, e
 	return
 }
 
-// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
-// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
-// just the Field field, which defines the extension's field number.
-func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
-	epb, ok := extendable(pb)
-	if !ok {
-		return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
-	}
-	registeredExtensions := RegisteredExtensions(pb)
-
-	emap, mu := epb.extensionsRead()
-	mu.Lock()
-	defer mu.Unlock()
-	extensions := make([]*ExtensionDesc, 0, len(emap))
-	for extid, e := range emap {
-		desc := e.desc
-		if desc == nil {
-			desc = registeredExtensions[extid]
-			if desc == nil {
-				desc = &ExtensionDesc{Field: extid}
-			}
-		}
-
-		extensions = append(extensions, desc)
-	}
-	return extensions, nil
-}
-
 // SetExtension sets the specified extension of pb to the specified value.
 func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
 	epb, ok := extendable(pb)
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/http.go b/vendor/github.com/prometheus/client_golang/prometheus/http.go
index e078e3ed..c57aa053 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/http.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/http.go
@@ -61,16 +61,16 @@ func nowSeries(t ...time.Time) nower {
 // Note that InstrumentHandler has several issues:
 //
 // - It uses Summaries rather than Histograms. Summaries are not useful if
-// aggregation across multiple instances is required.
+//   aggregation accross multiple instances is required.
 //
-// - It uses microseconds as unit, which is deprecated and should be replaced by
-// seconds.
+// - It uses microseconds as unit, which is deprecated and should be reploced by
+//   seconds.
 //
 // - The size of the request is calculated in a separate goroutine. Since this
-// calculator requires access to the request header, it creates a race with
-// any writes to the header performed during request handling.
-// httputil.ReverseProxy is a prominent example for a handler
-// performing such writes.
+//   calculatior requires access to the request header, it creates a race with
+//   any writes to the header performed during request handling.
+//   httputil.ReverseProxy is a prominent example for a handler
+//   performing such writes.
 //
 // Upcoming versions of this package will provide ways of instrumenting HTTP
 // handlers that are more flexible and have fewer issues. Consider this function
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
index f6ae51be..7e3560ae 100644
--- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go
+++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go
@@ -115,13 +115,11 @@ func Register(m Collector) error {
 }
 
 // MustRegister works like Register but panics where Register would have
-// returned an error. MustRegister is also Variadic, where Register only
-// accepts a single Collector to register.
-func MustRegister(m ...Collector) {
-	for i := range m {
-		if err := Register(m[i]); err != nil {
-			panic(err)
-		}
+// returned an error.
+func MustRegister(m Collector) {
+	err := Register(m)
+	if err != nil {
+		panic(err)
 	}
 }
 
diff --git a/vendor/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/prometheus/common/expfmt/decode.go
index 487fdc6c..be1187de 100644
--- a/vendor/github.com/prometheus/common/expfmt/decode.go
+++ b/vendor/github.com/prometheus/common/expfmt/decode.go
@@ -46,7 +46,10 @@ func ResponseFormat(h http.Header) Format {
 		return FmtUnknown
 	}
 
-	const textType = "text/plain"
+	const (
+		textType = "text/plain"
+		jsonType = "application/json"
+	)
 
 	switch mediatype {
 	case ProtoType:
@@ -63,6 +66,22 @@ func ResponseFormat(h http.Header) Format {
 			return FmtUnknown
 		}
 		return FmtText
+
+	case jsonType:
+		var prometheusAPIVersion string
+
+		if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
+			prometheusAPIVersion = params["version"]
+		} else {
+			prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
+		}
+
+		switch prometheusAPIVersion {
+		case "0.0.2", "":
+			return fmtJSON2
+		default:
+			return FmtUnknown
+		}
 	}
 
 	return FmtUnknown
@@ -74,6 +93,8 @@ func NewDecoder(r io.Reader, format Format) Decoder {
 	switch format {
 	case FmtProtoDelim:
 		return &protoDecoder{r: r}
+	case fmtJSON2:
+		return newJSON2Decoder(r)
 	}
 	return &textDecoder{r: r}
 }
diff --git a/vendor/github.com/prometheus/common/expfmt/json_decode.go b/vendor/github.com/prometheus/common/expfmt/json_decode.go
new file mode 100644
index 00000000..cf865451
--- /dev/null
+++ b/vendor/github.com/prometheus/common/expfmt/json_decode.go
@@ -0,0 +1,174 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"sort"
+
+	"github.com/golang/protobuf/proto"
+	dto "github.com/prometheus/client_model/go"
+
+	"github.com/prometheus/common/model"
+)
+
+type json2Decoder struct {
+	dec  *json.Decoder
+	fams []*dto.MetricFamily
+}
+
+func newJSON2Decoder(r io.Reader) Decoder {
+	return &json2Decoder{
+		dec: json.NewDecoder(r),
+	}
+}
+
+type histogram002 struct {
+	Labels model.LabelSet     `json:"labels"`
+	Values map[string]float64 `json:"value"`
+}
+
+type counter002 struct {
+	Labels model.LabelSet `json:"labels"`
+	Value  float64        `json:"value"`
+}
+
+func protoLabelSet(base, ext model.LabelSet) ([]*dto.LabelPair, error) {
+	labels := base.Clone().Merge(ext)
+	delete(labels, model.MetricNameLabel)
+
+	names := make([]string, 0, len(labels))
+	for ln := range labels {
+		names = append(names, string(ln))
+	}
+	sort.Strings(names)
+
+	pairs := make([]*dto.LabelPair, 0, len(labels))
+
+	for _, ln := range names {
+		if !model.LabelNameRE.MatchString(ln) {
+			return nil, fmt.Errorf("invalid label name %q", ln)
+		}
+		lv := labels[model.LabelName(ln)]
+
+		pairs = append(pairs, &dto.LabelPair{
+			Name:  proto.String(ln),
+			Value: proto.String(string(lv)),
+		})
+	}
+
+	return pairs, nil
+}
+
+func (d *json2Decoder) more() error {
+	var entities []struct {
+		BaseLabels model.LabelSet `json:"baseLabels"`
+		Docstring  string         `json:"docstring"`
+		Metric     struct {
+			Type   string          `json:"type"`
+			Values json.RawMessage `json:"value"`
+		} `json:"metric"`
+	}
+
+	if err := d.dec.Decode(&entities); err != nil {
+		return err
+	}
+	for _, e := range entities {
+		f := &dto.MetricFamily{
+			Name:   proto.String(string(e.BaseLabels[model.MetricNameLabel])),
+			Help:   proto.String(e.Docstring),
+			Type:   dto.MetricType_UNTYPED.Enum(),
+			Metric: []*dto.Metric{},
+		}
+
+		d.fams = append(d.fams, f)
+
+		switch e.Metric.Type {
+		case "counter", "gauge":
+			var values []counter002
+
+			if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
+				return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
+			}
+
+			for _, ctr := range values {
+				labels, err := protoLabelSet(e.BaseLabels, ctr.Labels)
+				if err != nil {
+					return err
+				}
+				f.Metric = append(f.Metric, &dto.Metric{
+					Label: labels,
+					Untyped: &dto.Untyped{
+						Value: proto.Float64(ctr.Value),
+					},
+				})
+			}
+
+		case "histogram":
+			var values []histogram002
+
+			if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
+				return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
+			}
+
+			for _, hist := range values {
+				quants := make([]string, 0, len(values))
+				for q := range hist.Values {
+					quants = append(quants, q)
+				}
+
+				sort.Strings(quants)
+
+				for _, q := range quants {
+					value := hist.Values[q]
+					// The correct label is "quantile" but to not break old expressions
+					// this remains "percentile"
+					hist.Labels["percentile"] = model.LabelValue(q)
+
+					labels, err := protoLabelSet(e.BaseLabels, hist.Labels)
+					if err != nil {
+						return err
+					}
+
+					f.Metric = append(f.Metric, &dto.Metric{
+						Label: labels,
+						Untyped: &dto.Untyped{
+							Value: proto.Float64(value),
+						},
+					})
+				}
+			}
+
+		default:
+			return fmt.Errorf("unknown metric type %q", e.Metric.Type)
+		}
+	}
+	return nil
+}
+
+// Decode implements the Decoder interface.
+func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
+	if len(d.fams) == 0 {
+		if err := d.more(); err != nil {
+			return err
+		}
+	}
+
+	*v = *d.fams[0]
+	d.fams = d.fams[1:]
+
+	return nil
+}
diff --git a/vendor/github.com/rakyll/globalconf/globalconf.go b/vendor/github.com/rakyll/globalconf/globalconf.go
index 354401f4..57d5dd42 100644
--- a/vendor/github.com/rakyll/globalconf/globalconf.go
+++ b/vendor/github.com/rakyll/globalconf/globalconf.go
@@ -8,7 +8,7 @@ import (
 	"path"
 	"strings"
 
-	ini "github.com/glacjay/goini"
+	ini "github.com/rakyll/goini"
 )
 
 const (
diff --git a/vendor/github.com/glacjay/goini/ini.go b/vendor/github.com/rakyll/goini/ini.go
similarity index 88%
rename from vendor/github.com/glacjay/goini/ini.go
rename to vendor/github.com/rakyll/goini/ini.go
index d224f1e6..54b81f18 100644
--- a/vendor/github.com/glacjay/goini/ini.go
+++ b/vendor/github.com/rakyll/goini/ini.go
@@ -23,24 +23,15 @@ var (
 	regNoValue     = regexp.MustCompile("^([^= \t]+)[ \t]*=[ \t]*([#;].*)?")
 )
 
-func MustLoadReader(reader *bufio.Reader) Dict {
-	dict, err := LoadReader(reader)
-	if err != nil {
-		panic(err)
-	}
-	return dict
-}
-
-func MustLoad(filename string) Dict {
-	dict, err := Load(filename)
+func Load(filename string) (dict Dict, err error) {
+	file, err := os.Open(filename)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	return dict
-}
+	defer file.Close()
 
-func LoadReader(reader *bufio.Reader) (dict Dict, err error) {
 	dict = make(map[string]map[string]string)
+	reader := bufio.NewReader(file)
 	lineno := 0
 	section := ""
 	dict[section] = make(map[string]string)
@@ -68,24 +59,13 @@ func LoadReader(reader *bufio.Reader) (dict Dict, err error) {
 		section, err = dict.parseLine(section, line)
 		if err != nil {
 			return nil, newError(
-				err.Error() + fmt.Sprintf("':%d'.", lineno))
+				err.Error() + fmt.Sprintf("'%s:%d'.", filename, lineno))
 		}
 	}
 
 	return
 }
 
-func Load(filename string) (Dict, error) {
-	file, err := os.Open(filename)
-	if err != nil {
-		return nil, err
-	}
-	defer file.Close()
-
-	reader := bufio.NewReader(file)
-	return LoadReader(reader)
-}
-
 func Write(filename string, dict *Dict) error {
 	buffer := dict.format()
 	return ioutil.WriteFile(filename, buffer.Bytes(), 0644)
@@ -244,15 +224,10 @@ func (dict Dict) String() string {
 
 func (dict Dict) format() *bytes.Buffer {
 	var buffer bytes.Buffer
-	for key, val := range dict[""] {
-		buffer.WriteString(fmt.Sprintf("%s = %s\n", key, val))
-	}
-	buffer.WriteString("\n")
 	for section, vals := range dict {
-		if section == "" {
-			continue
+		if section != "" {
+			buffer.WriteString(fmt.Sprintf("[%s]\n", section))
 		}
-		buffer.WriteString(fmt.Sprintf("[%s]\n", section))
 		for key, val := range vals {
 			buffer.WriteString(fmt.Sprintf("%s = %s\n", key, val))
 		}
diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go
index 5b641875..a8b0fa56 100644
--- a/vendor/github.com/spf13/cobra/command.go
+++ b/vendor/github.com/spf13/cobra/command.go
@@ -110,7 +110,7 @@ type Command struct {
 	flagErrorBuf *bytes.Buffer
 
 	args          []string                 // actual args parsed from flags
-	output        *io.Writer               // out writer if set in SetOutput(w)
+	output        *io.Writer               // nil means stderr; use Out() method instead
 	usageFunc     func(*Command) error     // Usage can be defined by application
 	usageTemplate string                   // Can be defined by Application
 	helpTemplate  string                   // Can be defined by Application
@@ -134,6 +134,25 @@ func (c *Command) SetArgs(a []string) {
 	c.args = a
 }
 
+func (c *Command) getOut(def io.Writer) io.Writer {
+	if c.output != nil {
+		return *c.output
+	}
+
+	if c.HasParent() {
+		return c.parent.Out()
+	}
+	return def
+}
+
+func (c *Command) Out() io.Writer {
+	return c.getOut(os.Stderr)
+}
+
+func (c *Command) getOutOrStdout() io.Writer {
+	return c.getOut(os.Stdout)
+}
+
 // SetOutput sets the destination for usage and error messages.
 // If output is nil, os.Stderr is used.
 func (c *Command) SetOutput(output io.Writer) {
@@ -176,26 +195,6 @@ func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string
 	}
 }
 
-func (c *Command) OutOrStdout() io.Writer {
-	return c.getOut(os.Stdout)
-}
-
-func (c *Command) OutOrStderr() io.Writer {
-	return c.getOut(os.Stderr)
-}
-
-func (c *Command) getOut(def io.Writer) io.Writer {
-	if c.output != nil {
-		return *c.output
-	}
-	if c.HasParent() {
-		return c.parent.getOut(def)
-	}
-	return def
-}
-
-// UsageFunc returns either the function set by SetUsageFunc for this command
-// or a parent, or it returns a default usage function
 func (c *Command) UsageFunc() (f func(*Command) error) {
 	if c.usageFunc != nil {
 		return c.usageFunc
@@ -205,17 +204,16 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
 		return c.parent.UsageFunc()
 	}
 	return func(c *Command) error {
-		c.mergePersistentFlags()
-		err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c)
+		err := tmpl(c.Out(), c.UsageTemplate(), c)
 		if err != nil {
-			c.Println(err)
+			fmt.Print(err)
 		}
 		return err
 	}
 }
 
 // HelpFunc returns either the function set by SetHelpFunc for this command
-// or a parent, or it returns a function with default help behavior
+// or a parent, or it returns a function which calls c.Help()
 func (c *Command) HelpFunc() func(*Command, []string) {
 	cmd := c
 	for cmd != nil {
@@ -225,23 +223,13 @@ func (c *Command) HelpFunc() func(*Command, []string) {
 		cmd = cmd.parent
 	}
 	return func(*Command, []string) {
-		c.mergePersistentFlags()
-		err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c)
+		err := c.Help()
 		if err != nil {
 			c.Println(err)
 		}
 	}
 }
 
-func (c *Command) UsageString() string {
-	tmpOutput := c.output
-	bb := new(bytes.Buffer)
-	c.SetOutput(bb)
-	c.UsageFunc()(c)
-	c.output = tmpOutput
-	return bb.String()
-}
-
 var minUsagePadding = 25
 
 func (c *Command) UsagePadding() int {
@@ -549,7 +537,6 @@ func (c *Command) execute(a []string) (err error) {
 		c.Println("\"help\" flag declared as non-bool. Please correct your code")
 		return err
 	}
-
 	if helpVal || !c.Runnable() {
 		return flag.ErrHelp
 	}
@@ -720,9 +707,10 @@ func (c *Command) initHelpCmd() {
 				cmd, _, e := c.Root().Find(args)
 				if cmd == nil || e != nil {
 					c.Printf("Unknown help topic %#q.", args)
-					c.Root().UsageFunc()(cmd)
+					c.Root().Usage()
 				} else {
-					cmd.HelpFunc()(cmd, args)
+					helpFunc := cmd.HelpFunc()
+					helpFunc(cmd, args)
 				}
 			},
 		}
@@ -746,7 +734,7 @@ func (c commandSorterByName) Less(i, j int) bool { return c[i].Name() < c[j].Nam
 // Commands returns a sorted slice of child commands.
 func (c *Command) Commands() []*Command {
 	// do not sort commands if it already sorted or sorting was disabled
-	if EnableCommandSorting && !c.commandsAreSorted {
+	if EnableCommandSorting && !c.commandsAreSorted{
 		sort.Sort(commandSorterByName(c.commands))
 		c.commandsAreSorted = true
 	}
@@ -816,23 +804,50 @@ main:
 	}
 }
 
-// Print is a convenience method to Print to the defined output, fallback to Stderr if not set
+// Print is a convenience method to Print to the defined output
 func (c *Command) Print(i ...interface{}) {
-	fmt.Fprint(c.OutOrStderr(), i...)
+	fmt.Fprint(c.Out(), i...)
 }
 
-// Println is a convenience method to Println to the defined output, fallback to Stderr if not set
+// Println is a convenience method to Println to the defined output
 func (c *Command) Println(i ...interface{}) {
 	str := fmt.Sprintln(i...)
 	c.Print(str)
 }
 
-// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set
+// Printf is a convenience method to Printf to the defined output
 func (c *Command) Printf(format string, i ...interface{}) {
 	str := fmt.Sprintf(format, i...)
 	c.Print(str)
 }
 
+// Output the usage for the command
+// Used when a user provides invalid input
+// Can be defined by user by overriding UsageFunc
+func (c *Command) Usage() error {
+	c.mergePersistentFlags()
+	err := c.UsageFunc()(c)
+	return err
+}
+
+// Output the help for the command
+// Used when a user calls help [command]
+// by the default HelpFunc in the commander
+func (c *Command) Help() error {
+	c.mergePersistentFlags()
+	err := tmpl(c.getOutOrStdout(), c.HelpTemplate(), c)
+	return err
+}
+
+func (c *Command) UsageString() string {
+	tmpOutput := c.output
+	bb := new(bytes.Buffer)
+	c.SetOutput(bb)
+	c.Usage()
+	c.output = tmpOutput
+	return bb.String()
+}
+
 // CommandPath returns the full path to this command.
 func (c *Command) CommandPath() string {
 	str := c.Name()
diff --git a/vendor/github.com/vishvananda/netlink/addr_linux.go b/vendor/github.com/vishvananda/netlink/addr_linux.go
index 8820c020..b5eec650 100644
--- a/vendor/github.com/vishvananda/netlink/addr_linux.go
+++ b/vendor/github.com/vishvananda/netlink/addr_linux.go
@@ -8,7 +8,6 @@ import (
 	"syscall"
 
 	"github.com/vishvananda/netlink/nl"
-	"github.com/vishvananda/netns"
 )
 
 // IFA_FLAGS is a u32 attribute.
@@ -193,17 +192,7 @@ type AddrUpdate struct {
 // AddrSubscribe takes a chan down which notifications will be sent
 // when addresses change.  Close the 'done' chan to stop subscription.
 func AddrSubscribe(ch chan<- AddrUpdate, done <-chan struct{}) error {
-	return addrSubscribe(netns.None(), netns.None(), ch, done)
-}
-
-// AddrSubscribeAt works like AddrSubscribe plus it allows the caller
-// to choose the network namespace in which to subscribe (ns).
-func AddrSubscribeAt(ns netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
-	return addrSubscribe(ns, netns.None(), ch, done)
-}
-
-func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-chan struct{}) error {
-	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
+	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_IFADDR, syscall.RTNLGRP_IPV6_IFADDR)
 	if err != nil {
 		return err
 	}
diff --git a/vendor/github.com/vishvananda/netlink/filter_linux.go b/vendor/github.com/vishvananda/netlink/filter_linux.go
index d9aedca7..b403dad2 100644
--- a/vendor/github.com/vishvananda/netlink/filter_linux.go
+++ b/vendor/github.com/vishvananda/netlink/filter_linux.go
@@ -143,7 +143,7 @@ func (h *Handle) FilterAdd(filter Filter) error {
 		if u32.RedirIndex != 0 {
 			u32.Actions = append([]Action{NewMirredAction(u32.RedirIndex)}, u32.Actions...)
 		}
-		if err := EncodeActions(actionsAttr, u32.Actions); err != nil {
+		if err := encodeActions(actionsAttr, u32.Actions); err != nil {
 			return err
 		}
 	} else if fw, ok := filter.(*Fw); ok {
@@ -309,7 +309,7 @@ func toAttrs(tcgen *nl.TcGen, attrs *ActionAttrs) {
 	attrs.Bindcnt = int(tcgen.Bindcnt)
 }
 
-func EncodeActions(attr *nl.RtAttr, actions []Action) error {
+func encodeActions(attr *nl.RtAttr, actions []Action) error {
 	tabIndex := int(nl.TCA_ACT_TAB)
 
 	for _, action := range actions {
diff --git a/vendor/github.com/vishvananda/netlink/link_linux.go b/vendor/github.com/vishvananda/netlink/link_linux.go
index cfa5bf3e..08a93843 100644
--- a/vendor/github.com/vishvananda/netlink/link_linux.go
+++ b/vendor/github.com/vishvananda/netlink/link_linux.go
@@ -10,7 +10,6 @@ import (
 	"unsafe"
 
 	"github.com/vishvananda/netlink/nl"
-	"github.com/vishvananda/netns"
 )
 
 const SizeofLinkStats = 0x5c
@@ -426,7 +425,7 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
 		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_UDP_CSUM, boolAttr(vxlan.UDPCSum))
 	}
 	if vxlan.GBP {
-		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{})
+		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, boolAttr(vxlan.GBP))
 	}
 	if vxlan.NoAge {
 		nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
@@ -1012,17 +1011,7 @@ type LinkUpdate struct {
 // LinkSubscribe takes a chan down which notifications will be sent
 // when links change.  Close the 'done' chan to stop subscription.
 func LinkSubscribe(ch chan<- LinkUpdate, done <-chan struct{}) error {
-	return linkSubscribe(netns.None(), netns.None(), ch, done)
-}
-
-// LinkSubscribeAt works like LinkSubscribe plus it allows the caller
-// to choose the network namespace in which to subscribe (ns).
-func LinkSubscribeAt(ns netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error {
-	return linkSubscribe(ns, netns.None(), ch, done)
-}
-
-func linkSubscribe(newNs, curNs netns.NsHandle, ch chan<- LinkUpdate, done <-chan struct{}) error {
-	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
+	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_LINK)
 	if err != nil {
 		return err
 	}
@@ -1163,7 +1152,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
 		case nl.IFLA_VXLAN_UDP_CSUM:
 			vxlan.UDPCSum = int8(datum.Value[0]) != 0
 		case nl.IFLA_VXLAN_GBP:
-			vxlan.GBP = true
+			vxlan.GBP = int8(datum.Value[0]) != 0
 		case nl.IFLA_VXLAN_AGEING:
 			vxlan.Age = int(native.Uint32(datum.Value[0:4]))
 			vxlan.NoAge = vxlan.Age == 0
diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
index 17683a75..fd5550a3 100644
--- a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
+++ b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go
@@ -331,63 +331,24 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
 // moves back into it when done. If newNs is close, the socket will be opened
 // in the current network namespace.
 func GetNetlinkSocketAt(newNs, curNs netns.NsHandle, protocol int) (*NetlinkSocket, error) {
-	c, err := executeInNetns(newNs, curNs)
-	if err != nil {
-		return nil, err
-	}
-	defer c()
-	return getNetlinkSocket(protocol)
-}
+	var err error
 
-// executeInNetns sets execution of the code following this call to the
-// network namespace newNs, then moves the thread back to curNs if open,
-// otherwise to the current netns at the time the function was invoked
-// In case of success, the caller is expected to execute the returned function
-// at the end of the code that needs to be executed in the network namespace.
-// Example:
-// func jobAt(...) error {
-//      d, err := executeInNetns(...)
-//      if err != nil { return err}
-//      defer d()
-//      < code which needs to be executed in specific netns>
-//  }
-// TODO: his function probably belongs to netns pkg.
-func executeInNetns(newNs, curNs netns.NsHandle) (func(), error) {
-	var (
-		err       error
-		moveBack  func(netns.NsHandle) error
-		closeNs   func() error
-		unlockThd func()
-	)
-	restore := func() {
-		// order matters
-		if moveBack != nil {
-			moveBack(curNs)
-		}
-		if closeNs != nil {
-			closeNs()
-		}
-		if unlockThd != nil {
-			unlockThd()
-		}
-	}
 	if newNs.IsOpen() {
 		runtime.LockOSThread()
-		unlockThd = runtime.UnlockOSThread
+		defer runtime.UnlockOSThread()
 		if !curNs.IsOpen() {
 			if curNs, err = netns.Get(); err != nil {
-				restore()
 				return nil, fmt.Errorf("could not get current namespace while creating netlink socket: %v", err)
 			}
-			closeNs = curNs.Close
+			defer curNs.Close()
 		}
 		if err := netns.Set(newNs); err != nil {
-			restore()
 			return nil, fmt.Errorf("failed to set into network namespace %d while creating netlink socket: %v", newNs, err)
 		}
-		moveBack = netns.Set
+		defer netns.Set(curNs)
 	}
-	return restore, nil
+
+	return getNetlinkSocket(protocol)
 }
 
 // Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
@@ -416,18 +377,6 @@ func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
 	return s, nil
 }
 
-// SubscribeAt works like Subscribe plus let's the caller choose the network
-// namespace in which the socket would be opened (newNs). Then control goes back
-// to curNs if open, otherwise to the netns at the time this function was called.
-func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*NetlinkSocket, error) {
-	c, err := executeInNetns(newNs, curNs)
-	if err != nil {
-		return nil, err
-	}
-	defer c()
-	return Subscribe(protocol, groups...)
-}
-
 func (s *NetlinkSocket) Close() {
 	syscall.Close(s.fd)
 	s.fd = -1
diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
index 856db3da..482b4f37 100644
--- a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
+++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux.go
@@ -10,7 +10,6 @@ const (
 	SizeofXfrmUsersaInfo  = 0xe0
 	SizeofXfrmAlgo        = 0x44
 	SizeofXfrmAlgoAuth    = 0x48
-	SizeofXfrmAlgoAEAD    = 0x48
 	SizeofXfrmEncapTmpl   = 0x18
 	SizeofXfrmUsersaFlush = 0x8
 )
@@ -195,35 +194,6 @@ func (msg *XfrmAlgoAuth) Serialize() []byte {
 //   char    alg_key[0];
 // }
 
-type XfrmAlgoAEAD struct {
-	AlgName   [64]byte
-	AlgKeyLen uint32
-	AlgICVLen uint32
-	AlgKey    []byte
-}
-
-func (msg *XfrmAlgoAEAD) Len() int {
-	return SizeofXfrmAlgoAEAD + int(msg.AlgKeyLen/8)
-}
-
-func DeserializeXfrmAlgoAEAD(b []byte) *XfrmAlgoAEAD {
-	ret := XfrmAlgoAEAD{}
-	copy(ret.AlgName[:], b[0:64])
-	ret.AlgKeyLen = *(*uint32)(unsafe.Pointer(&b[64]))
-	ret.AlgICVLen = *(*uint32)(unsafe.Pointer(&b[68]))
-	ret.AlgKey = b[72:ret.Len()]
-	return &ret
-}
-
-func (msg *XfrmAlgoAEAD) Serialize() []byte {
-	b := make([]byte, msg.Len())
-	copy(b[0:64], msg.AlgName[:])
-	copy(b[64:68], (*(*[4]byte)(unsafe.Pointer(&msg.AlgKeyLen)))[:])
-	copy(b[68:72], (*(*[4]byte)(unsafe.Pointer(&msg.AlgICVLen)))[:])
-	copy(b[72:msg.Len()], msg.AlgKey[:])
-	return b
-}
-
 // struct xfrm_encap_tmpl {
 //   __u16   encap_type;
 //   __be16    encap_sport;
diff --git a/vendor/github.com/vishvananda/netlink/route_linux.go b/vendor/github.com/vishvananda/netlink/route_linux.go
index 6ba3ea12..5d684ad7 100644
--- a/vendor/github.com/vishvananda/netlink/route_linux.go
+++ b/vendor/github.com/vishvananda/netlink/route_linux.go
@@ -6,7 +6,6 @@ import (
 	"syscall"
 
 	"github.com/vishvananda/netlink/nl"
-	"github.com/vishvananda/netns"
 )
 
 // RtAttr is shared so it is in netlink_linux.go
@@ -422,17 +421,7 @@ func (h *Handle) RouteGet(destination net.IP) ([]Route, error) {
 // RouteSubscribe takes a chan down which notifications will be sent
 // when routes are added or deleted. Close the 'done' chan to stop subscription.
 func RouteSubscribe(ch chan<- RouteUpdate, done <-chan struct{}) error {
-	return routeSubscribeAt(netns.None(), netns.None(), ch, done)
-}
-
-// RouteSubscribeAt works like RouteSubscribe plus it allows the caller
-// to choose the network namespace in which to subscribe (ns).
-func RouteSubscribeAt(ns netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
-	return routeSubscribeAt(ns, netns.None(), ch, done)
-}
-
-func routeSubscribeAt(newNs, curNs netns.NsHandle, ch chan<- RouteUpdate, done <-chan struct{}) error {
-	s, err := nl.SubscribeAt(newNs, curNs, syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
+	s, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_IPV4_ROUTE, syscall.RTNLGRP_IPV6_ROUTE)
 	if err != nil {
 		return err
 	}
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state.go b/vendor/github.com/vishvananda/netlink/xfrm_state.go
index e25cde26..6ffbcdb4 100644
--- a/vendor/github.com/vishvananda/netlink/xfrm_state.go
+++ b/vendor/github.com/vishvananda/netlink/xfrm_state.go
@@ -10,18 +10,10 @@ type XfrmStateAlgo struct {
 	Name        string
 	Key         []byte
 	TruncateLen int // Auth only
-	ICVLen      int // AEAD only
 }
 
 func (a XfrmStateAlgo) String() string {
-	base := fmt.Sprintf("{Name: %s, Key: 0x%x", a.Name, a.Key)
-	if a.TruncateLen != 0 {
-		base = fmt.Sprintf("%s, Truncate length: %d", base, a.TruncateLen)
-	}
-	if a.ICVLen != 0 {
-		base = fmt.Sprintf("%s, ICV length: %d", base, a.ICVLen)
-	}
-	return fmt.Sprintf("%s}", base)
+	return fmt.Sprintf("{Name: %s, Key: 0x%x, TruncateLen: %d}", a.Name, a.Key, a.TruncateLen)
 }
 
 // EncapType is an enum representing the optional packet encapsulation.
@@ -81,13 +73,12 @@ type XfrmState struct {
 	Mark         *XfrmMark
 	Auth         *XfrmStateAlgo
 	Crypt        *XfrmStateAlgo
-	Aead         *XfrmStateAlgo
 	Encap        *XfrmStateEncap
 }
 
 func (sa XfrmState) String() string {
-	return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Aead: %v,Encap: %v",
-		sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Aead, sa.Encap)
+	return fmt.Sprintf("Dst: %v, Src: %v, Proto: %s, Mode: %s, SPI: 0x%x, ReqID: 0x%x, ReplayWindow: %d, Mark: %v, Auth: %v, Crypt: %v, Encap: %v",
+		sa.Dst, sa.Src, sa.Proto, sa.Mode, sa.Spi, sa.Reqid, sa.ReplayWindow, sa.Mark, sa.Auth, sa.Crypt, sa.Encap)
 }
 func (sa XfrmState) Print(stats bool) string {
 	if !stats {
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
index b100f71b..5f294c71 100644
--- a/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
+++ b/vendor/github.com/vishvananda/netlink/xfrm_state_linux.go
@@ -35,20 +35,6 @@ func writeStateAlgoAuth(a *XfrmStateAlgo) []byte {
 	return algo.Serialize()
 }
 
-func writeStateAlgoAead(a *XfrmStateAlgo) []byte {
-	algo := nl.XfrmAlgoAEAD{
-		AlgKeyLen: uint32(len(a.Key) * 8),
-		AlgICVLen: uint32(a.ICVLen),
-		AlgKey:    a.Key,
-	}
-	end := len(a.Name)
-	if end > 64 {
-		end = 64
-	}
-	copy(algo.AlgName[:end], a.Name)
-	return algo.Serialize()
-}
-
 func writeMark(m *XfrmMark) []byte {
 	mark := &nl.XfrmMark{
 		Value: m.Value,
@@ -111,10 +97,6 @@ func (h *Handle) xfrmStateAddOrUpdate(state *XfrmState, nlProto int) error {
 		out := nl.NewRtAttr(nl.XFRMA_ALG_CRYPT, writeStateAlgo(state.Crypt))
 		req.AddData(out)
 	}
-	if state.Aead != nil {
-		out := nl.NewRtAttr(nl.XFRMA_ALG_AEAD, writeStateAlgoAead(state.Aead))
-		req.AddData(out)
-	}
 	if state.Encap != nil {
 		encapData := make([]byte, nl.SizeofXfrmEncapTmpl)
 		encap := nl.DeserializeXfrmEncapTmpl(encapData)
@@ -289,12 +271,6 @@ func parseXfrmState(m []byte, family int) (*XfrmState, error) {
 			state.Auth.Name = nl.BytesToString(algo.AlgName[:])
 			state.Auth.Key = algo.AlgKey
 			state.Auth.TruncateLen = int(algo.AlgTruncLen)
-		case nl.XFRMA_ALG_AEAD:
-			state.Aead = new(XfrmStateAlgo)
-			algo := nl.DeserializeXfrmAlgoAEAD(attr.Value[:])
-			state.Aead.Name = nl.BytesToString(algo.AlgName[:])
-			state.Aead.Key = algo.AlgKey
-			state.Aead.ICVLen = int(algo.AlgICVLen)
 		case nl.XFRMA_ENCAP:
 			encap := nl.DeserializeXfrmEncapTmpl(attr.Value[:])
 			state.Encap = new(XfrmStateEncap)
diff --git a/vendor/golang.org/x/crypto/ssh/agent/client.go b/vendor/golang.org/x/crypto/ssh/agent/client.go
index ecfd7c58..a75668d1 100644
--- a/vendor/golang.org/x/crypto/ssh/agent/client.go
+++ b/vendor/golang.org/x/crypto/ssh/agent/client.go
@@ -594,12 +594,12 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
 			Comments:    comment,
 			Constraints: constraints,
 		})
-	case *ed25519.PrivateKey:
+	case ed25519.PrivateKey:
 		req = ssh.Marshal(ed25519CertMsg{
 			Type:        cert.Type(),
 			CertBytes:   cert.Marshal(),
-			Pub:         []byte(*k)[32:],
-			Priv:        []byte(*k),
+			Pub:         []byte(k)[32:],
+			Priv:        []byte(k),
 			Comments:    comment,
 			Constraints: constraints,
 		})
diff --git a/vendor/golang.org/x/crypto/ssh/agent/server.go b/vendor/golang.org/x/crypto/ssh/agent/server.go
index 68a333fa..c562fa6e 100644
--- a/vendor/golang.org/x/crypto/ssh/agent/server.go
+++ b/vendor/golang.org/x/crypto/ssh/agent/server.go
@@ -16,7 +16,6 @@ import (
 	"log"
 	"math/big"
 
-	"golang.org/x/crypto/ed25519"
 	"golang.org/x/crypto/ssh"
 )
 
@@ -176,15 +175,6 @@ func parseRSAKey(req []byte) (*AddedKey, error) {
 	return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
 }
 
-func parseEd25519Key(req []byte) (*AddedKey, error) {
-	var k ed25519KeyMsg
-	if err := ssh.Unmarshal(req, &k); err != nil {
-		return nil, err
-	}
-	priv := ed25519.PrivateKey(k.Priv)
-	return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
-}
-
 func parseDSAKey(req []byte) (*AddedKey, error) {
 	var k dsaKeyMsg
 	if err := ssh.Unmarshal(req, &k); err != nil {
@@ -229,23 +219,6 @@ func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (pri
 	return priv, nil
 }
 
-func parseEd25519Cert(req []byte) (*AddedKey, error) {
-	var k ed25519CertMsg
-	if err := ssh.Unmarshal(req, &k); err != nil {
-		return nil, err
-	}
-	pubKey, err := ssh.ParsePublicKey(k.CertBytes)
-	if err != nil {
-		return nil, err
-	}
-	priv := ed25519.PrivateKey(k.Priv)
-	cert, ok := pubKey.(*ssh.Certificate)
-	if !ok {
-		return nil, errors.New("agent: bad ED25519 certificate")
-	}
-	return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
-}
-
 func parseECDSAKey(req []byte) (*AddedKey, error) {
 	var k ecdsaKeyMsg
 	if err := ssh.Unmarshal(req, &k); err != nil {
@@ -257,7 +230,7 @@ func parseECDSAKey(req []byte) (*AddedKey, error) {
 		return nil, err
 	}
 
-	return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
+	return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
 }
 
 func parseRSACert(req []byte) (*AddedKey, error) {
@@ -393,17 +366,13 @@ func (s *server) insertIdentity(req []byte) error {
 	case ssh.KeyAlgoDSA:
 		addedKey, err = parseDSAKey(req)
 	case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
-		addedKey, err = parseECDSAKey(req)
-	case ssh.KeyAlgoED25519:
-		addedKey, err = parseEd25519Key(req)
+		addedKey, err = parseECDSACert(req)
 	case ssh.CertAlgoRSAv01:
 		addedKey, err = parseRSACert(req)
 	case ssh.CertAlgoDSAv01:
 		addedKey, err = parseDSACert(req)
 	case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
 		addedKey, err = parseECDSACert(req)
-	case ssh.CertAlgoED25519v01:
-		addedKey, err = parseEd25519Cert(req)
 	default:
 		return fmt.Errorf("agent: not implemented: %q", record.Type)
 	}
diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go
index 294af0d4..6956ce45 100644
--- a/vendor/golang.org/x/crypto/ssh/client_auth.go
+++ b/vendor/golang.org/x/crypto/ssh/client_auth.go
@@ -437,37 +437,3 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
 		}
 	}
 }
-
-type retryableAuthMethod struct {
-	authMethod AuthMethod
-	maxTries   int
-}
-
-func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
-	for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
-		ok, methods, err = r.authMethod.auth(session, user, c, rand)
-		if ok || err != nil { // either success or error terminate
-			return ok, methods, err
-		}
-	}
-	return ok, methods, err
-}
-
-func (r *retryableAuthMethod) method() string {
-	return r.authMethod.method()
-}
-
-// RetryableAuthMethod is a decorator for other auth methods enabling them to
-// be retried up to maxTries before considering that AuthMethod itself failed.
-// If maxTries is <= 0, will retry indefinitely
-//
-// This is useful for interactive clients using challenge/response type
-// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
-// could mistype their response resulting in the server issuing a
-// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
-// [keyboard-interactive]); Without this decorator, the non-retryable
-// AuthMethod would be removed from future consideration, and never tried again
-// (and so the user would never be able to retry their entry).
-func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
-	return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
-}
diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go
index 2c72ab54..de029d6d 100644
--- a/vendor/golang.org/x/crypto/ssh/common.go
+++ b/vendor/golang.org/x/crypto/ssh/common.go
@@ -44,7 +44,7 @@ var supportedKexAlgos = []string{
 // of authenticating servers) in preference order.
 var supportedHostKeyAlgos = []string{
 	CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
-	CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
+	CertAlgoECDSA384v01, CertAlgoECDSA521v01,
 
 	KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
 	KeyAlgoRSA, KeyAlgoDSA,
diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go
index 0324e123..d6167e78 100644
--- a/vendor/golang.org/x/crypto/ssh/keys.go
+++ b/vendor/golang.org/x/crypto/ssh/keys.go
@@ -125,7 +125,7 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
 			continue
 		}
 
-		// Strip out the beginning of the known_host key.
+		// Strip out the begining of the known_host key.
 		// This is either an optional marker or a (set of) hostname(s).
 		keyFields := bytes.Fields(in)
 		if len(keyFields) < 3 || len(keyFields) > 5 {
diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go
index 17e2aa85..09eb0091 100644
--- a/vendor/golang.org/x/crypto/ssh/session.go
+++ b/vendor/golang.org/x/crypto/ssh/session.go
@@ -9,7 +9,6 @@ package ssh
 
 import (
 	"bytes"
-	"encoding/binary"
 	"errors"
 	"fmt"
 	"io"
@@ -282,10 +281,9 @@ func (s *Session) Start(cmd string) error {
 // copying stdin, stdout, and stderr, and exits with a zero exit
 // status.
 //
-// If the remote server does not send an exit status, an error of type
-// *ExitMissingError is returned. If the command completes
-// unsuccessfully or is interrupted by a signal, the error is of type
-// *ExitError. Other error types may be returned for I/O problems.
+// If the command fails to run or doesn't complete successfully, the
+// error is of type *ExitError. Other error types may be
+// returned for I/O problems.
 func (s *Session) Run(cmd string) error {
 	err := s.Start(cmd)
 	if err != nil {
@@ -372,10 +370,9 @@ func (s *Session) start() error {
 // copying stdin, stdout, and stderr, and exits with a zero exit
 // status.
 //
-// If the remote server does not send an exit status, an error of type
-// *ExitMissingError is returned. If the command completes
-// unsuccessfully or is interrupted by a signal, the error is of type
-// *ExitError. Other error types may be returned for I/O problems.
+// If the command fails to run or doesn't complete successfully, the
+// error is of type *ExitError. Other error types may be
+// returned for I/O problems.
 func (s *Session) Wait() error {
 	if !s.started {
 		return errors.New("ssh: session not started")
@@ -403,7 +400,8 @@ func (s *Session) wait(reqs <-chan *Request) error {
 	for msg := range reqs {
 		switch msg.Type {
 		case "exit-status":
-			wm.status = int(binary.BigEndian.Uint32(msg.Payload))
+			d := msg.Payload
+			wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
 		case "exit-signal":
 			var sigval struct {
 				Signal     string
@@ -433,29 +431,16 @@ func (s *Session) wait(reqs <-chan *Request) error {
 	if wm.status == -1 {
 		// exit-status was never sent from server
 		if wm.signal == "" {
-			// signal was not sent either.  RFC 4254
-			// section 6.10 recommends against this
-			// behavior, but it is allowed, so we let
-			// clients handle it.
-			return &ExitMissingError{}
+			return errors.New("wait: remote command exited without exit status or exit signal")
 		}
 		wm.status = 128
 		if _, ok := signals[Signal(wm.signal)]; ok {
 			wm.status += signals[Signal(wm.signal)]
 		}
 	}
-
 	return &ExitError{wm}
 }
 
-// ExitMissingError is returned if a session is torn down cleanly, but
-// the server sends no confirmation of the exit status.
-type ExitMissingError struct{}
-
-func (e *ExitMissingError) Error() string {
-	return "wait: remote command exited without exit status or exit signal"
-}
-
 func (s *Session) stdin() {
 	if s.stdinpipe {
 		return
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util.go b/vendor/golang.org/x/crypto/ssh/terminal/util.go
index c869213e..598e3df7 100644
--- a/vendor/golang.org/x/crypto/ssh/terminal/util.go
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util.go
@@ -44,13 +44,8 @@ func MakeRaw(fd int) (*State, error) {
 	}
 
 	newState := oldState.termios
-	// This attempts to replicate the behaviour documented for cfmakeraw in
-	// the termios(3) manpage.
-	newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
-	newState.Oflag &^= syscall.OPOST
-	newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
-	newState.Cflag &^= syscall.CSIZE | syscall.PARENB
-	newState.Cflag |= syscall.CS8
+	newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
+	newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
 	if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
 		return nil, err
 	}


Test patch:
diff --git a/agent/unit_state_test.go b/agent/unit_state_test.go
index 0a239c62..e773b319 100644
--- a/agent/unit_state_test.go
+++ b/agent/unit_state_test.go
@@ -227,7 +227,6 @@ func TestDefaultPublisher(t *testing.T) {
 				UnitName:    "foo.service",
 				ActiveState: "active",
 				MachineID:   "xyz",
-				UnitHash:    "quickbrownfox",
 			},
 			nil,
 			[]*unit.UnitState{
@@ -235,7 +234,6 @@ func TestDefaultPublisher(t *testing.T) {
 					UnitName:    "foo.service",
 					ActiveState: "active",
 					MachineID:   "xyz",
-					UnitHash:    "quickbrownfox",
 				},
 			},
 		},
@@ -245,7 +243,6 @@ func TestDefaultPublisher(t *testing.T) {
 			&unit.UnitState{
 				UnitName:    "foo.service",
 				ActiveState: "active",
-				UnitHash:    "quickbrownfox",
 			},
 			nil,
 			[]*unit.UnitState{},
@@ -258,7 +255,6 @@ func TestDefaultPublisher(t *testing.T) {
 				unit.UnitState{
 					UnitName:    "foo.service",
 					ActiveState: "active",
-					UnitHash:    "quickbrownfox",
 				},
 			},
 			[]*unit.UnitState{},
diff --git a/functional/systemd_test.go b/functional/systemd_test.go
index f15313cf..408543be 100644
--- a/functional/systemd_test.go
+++ b/functional/systemd_test.go
@@ -78,14 +78,20 @@ ExecStart=/usr/bin/sleep 3000
 		t.Error(err.Error())
 	}
 
-	mgr.TriggerStart(name)
+	err = mgr.TriggerStart(name)
+	if err != nil {
+		t.Error(err.Error())
+	}
 
 	err = waitForUnitState(mgr, name, unit.UnitState{"loaded", "active", "running", "", hash, ""})
 	if err != nil {
 		t.Error(err.Error())
 	}
 
-	mgr.TriggerStop(name)
+	err = mgr.TriggerStop(name)
+	if err != nil {
+		t.Error(err.Error())
+	}
 
 	mgr.Unload(name)
 
diff --git a/registry/unit_state_test.go b/registry/unit_state_test.go
index a97dc24e..12e62421 100644
--- a/registry/unit_state_test.go
+++ b/registry/unit_state_test.go
@@ -111,13 +111,14 @@ func TestSaveUnitState(t *testing.T) {
 		t.Fatalf("SaveUnitState of nil state should fail but acted unexpectedly!")
 	}
 
-	// Saving unit state with no hash should should fail.
-	r.SaveUnitState(j, us, time.Second)
-	if len(e.sets) != 0 || e.deletes != nil {
-		t.Logf("sets: %#v", e.sets)
-		t.Logf("deletes: %#v", e.deletes)
-		t.Fatalf("SaveUnitState on UnitState with no hash acted unexpectedly!")
-	}
+	// Saving unit state with no hash should succeed for now, but should fail
+	// in the future. See https://github.com/coreos/fleet/issues/720.
+	//r.SaveUnitState(j, us, time.Second)
+	//if len(e.sets) != 1 || e.deletes == nil {
+	//	t.Logf("sets: %#v", e.sets)
+	//	t.Logf("deletes: %#v", e.deletes)
+	//	t.Fatalf("SaveUnitState on UnitState with no hash acted unexpectedly!")
+	//}
 
 	us.UnitHash = "quickbrownfox"
 	r.SaveUnitState(j, us, time.Second)
@@ -194,7 +195,8 @@ func TestUnitStateToModel(t *testing.T) {
 			want: nil,
 		},
 		{
-			// Unit state with no hash and no machineID is not OK
+			// Unit state with no hash and no machineID is OK
+			// See https://github.com/coreos/fleet/issues/720
 			in: &unit.UnitState{
 				LoadState:   "foo",
 				ActiveState: "bar",
@@ -203,7 +205,13 @@ func TestUnitStateToModel(t *testing.T) {
 				UnitHash:    "",
 				UnitName:    "name",
 			},
-			want: nil,
+			want: &unitStateModel{
+				LoadState:    "foo",
+				ActiveState:  "bar",
+				SubState:     "baz",
+				MachineState: nil,
+				UnitHash:     "",
+			},
 		},
 		{
 			// Unit state with hash but no machineID is OK