summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/kubernetes/pkg/util
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2017-11-01 11:24:59 -0400
committerMatthew Heon <matthew.heon@gmail.com>2017-11-01 11:24:59 -0400
commita031b83a09a8628435317a03f199cdc18b78262f (patch)
treebc017a96769ce6de33745b8b0b1304ccf38e9df0 /vendor/k8s.io/kubernetes/pkg/util
parent2b74391cd5281f6fdf391ff8ad50fd1490f6bf89 (diff)
downloadpodman-a031b83a09a8628435317a03f199cdc18b78262f.tar.gz
podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.bz2
podman-a031b83a09a8628435317a03f199cdc18b78262f.zip
Initial checkin from CRI-O repo
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/util')
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go239
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/async/runner.go58
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go133
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go135
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/doc.go20
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/exec.go200
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go145
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/hash/hash.go37
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go104
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/io/io.go61
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/io/writer.go80
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go652
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go93
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go32
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go110
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/fake.go173
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount.go245
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go432
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go67
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go241
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go63
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/net/sets/doc.go28
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go119
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go54
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go78
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/template.go48
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/resize.go132
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/resizeevents.go61
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/resizeevents_windows.go62
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/setsize.go29
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/setsize_unsupported.go28
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/term.go110
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/term/term_writer.go124
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/umask.go27
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/umask_windows.go27
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/util.go140
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/version/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/version/version.go236
42 files changed, 4713 insertions, 0 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go b/vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go
new file mode 100644
index 000000000..da6fc2a4f
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/async/bounded_frequency_runner.go
@@ -0,0 +1,239 @@
+/*
+Copyright 2017 The Kubernetes 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 async
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "k8s.io/client-go/util/flowcontrol"
+
+ "github.com/golang/glog"
+)
+
+// BoundedFrequencyRunner manages runs of a user-provided function.
+// See NewBoundedFrequencyRunner for examples.
+type BoundedFrequencyRunner struct {
+ name string // the name of this instance
+ minInterval time.Duration // the min time between runs, modulo bursts
+ maxInterval time.Duration // the max time between runs
+
+ run chan struct{} // try an async run
+
+ mu sync.Mutex // guards runs of fn and all mutations
+ fn func() // function to run
+ lastRun time.Time // time of last run
+ timer timer // timer for deferred runs
+ limiter rateLimiter // rate limiter for on-demand runs
+}
+
+// designed so that flowcontrol.RateLimiter satisfies
+type rateLimiter interface {
+ TryAccept() bool
+ Stop()
+}
+
+type nullLimiter struct{}
+
+func (nullLimiter) TryAccept() bool {
+ return true
+}
+
+func (nullLimiter) Stop() {}
+
+var _ rateLimiter = nullLimiter{}
+
+// for testing
+type timer interface {
+ // C returns the timer's selectable channel.
+ C() <-chan time.Time
+
+ // See time.Timer.Reset.
+ Reset(d time.Duration) bool
+
+ // See time.Timer.Stop.
+ Stop() bool
+
+ // See time.Now.
+ Now() time.Time
+
+ // See time.Since.
+ Since(t time.Time) time.Duration
+
+ // See time.Sleep.
+ Sleep(d time.Duration)
+}
+
+// implement our timer in terms of std time.Timer.
+type realTimer struct {
+ *time.Timer
+}
+
+func (rt realTimer) C() <-chan time.Time {
+ return rt.Timer.C
+}
+
+func (rt realTimer) Now() time.Time {
+ return time.Now()
+}
+
+func (rt realTimer) Since(t time.Time) time.Duration {
+ return time.Since(t)
+}
+
+func (rt realTimer) Sleep(d time.Duration) {
+ time.Sleep(d)
+}
+
+var _ timer = realTimer{}
+
+// NewBoundedFrequencyRunner creates a new BoundedFrequencyRunner instance,
+// which will manage runs of the specified function.
+//
+// All runs will be async to the caller of BoundedFrequencyRunner.Run, but
+// multiple runs are serialized. If the function needs to hold locks, it must
+// take them internally.
+//
+// Runs of the funtion will have at least minInterval between them (from
+// completion to next start), except that up to bursts may be allowed. Burst
+// runs are "accumulated" over time, one per minInterval up to burstRuns total.
+// This can be used, for example, to mitigate the impact of expensive operations
+// being called in response to user-initiated operations. Run requests that
+// would violate the minInterval are coallesced and run at the next opportunity.
+//
+// The function will be run at least once per maxInterval. For example, this can
+// force periodic refreshes of state in the absence of anyone calling Run.
+//
+// Examples:
+//
+// NewBoundedFrequencyRunner("name", fn, time.Second, 5*time.Second, 1)
+// - fn will have at least 1 second between runs
+// - fn will have no more than 5 seconds between runs
+//
+// NewBoundedFrequencyRunner("name", fn, 3*time.Second, 10*time.Second, 3)
+// - fn will have at least 3 seconds between runs, with up to 3 burst runs
+// - fn will have no more than 10 seconds between runs
+//
+// The maxInterval must be greater than or equal to the minInterval, If the
+// caller passes a maxInterval less than minInterval, this function will panic.
+func NewBoundedFrequencyRunner(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int) *BoundedFrequencyRunner {
+ timer := realTimer{Timer: time.NewTimer(0)} // will tick immediately
+ <-timer.C() // consume the first tick
+ return construct(name, fn, minInterval, maxInterval, burstRuns, timer)
+}
+
+// Make an instance with dependencies injected.
+func construct(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int, timer timer) *BoundedFrequencyRunner {
+ if maxInterval < minInterval {
+ panic(fmt.Sprintf("%s: maxInterval (%v) must be >= minInterval (%v)", name, minInterval, maxInterval))
+ }
+ if timer == nil {
+ panic(fmt.Sprintf("%s: timer must be non-nil", name))
+ }
+
+ bfr := &BoundedFrequencyRunner{
+ name: name,
+ fn: fn,
+ minInterval: minInterval,
+ maxInterval: maxInterval,
+ run: make(chan struct{}, 1),
+ timer: timer,
+ }
+ if minInterval == 0 {
+ bfr.limiter = nullLimiter{}
+ } else {
+ // allow burst updates in short succession
+ qps := float32(time.Second) / float32(minInterval)
+ bfr.limiter = flowcontrol.NewTokenBucketRateLimiterWithClock(qps, burstRuns, timer)
+ }
+ return bfr
+}
+
+// Loop handles the periodic timer and run requests. This is expected to be
+// called as a goroutine.
+func (bfr *BoundedFrequencyRunner) Loop(stop <-chan struct{}) {
+ glog.V(3).Infof("%s Loop running", bfr.name)
+ bfr.timer.Reset(bfr.maxInterval)
+ for {
+ select {
+ case <-stop:
+ bfr.stop()
+ glog.V(3).Infof("%s Loop stopping", bfr.name)
+ return
+ case <-bfr.timer.C():
+ bfr.tryRun()
+ case <-bfr.run:
+ bfr.tryRun()
+ }
+ }
+}
+
+// Run the function as soon as possible. If this is called while Loop is not
+// running, the call may be deferred indefinitely.
+// If there is already a queued request to call the underlying function, it
+// may be dropped - it is just guaranteed that we will try calling the
+// underlying function as soon as possible starting from now.
+func (bfr *BoundedFrequencyRunner) Run() {
+ // If it takes a lot of time to run the underlying function, noone is really
+ // processing elements from <run> channel. So to avoid blocking here on the
+ // putting element to it, we simply skip it if there is already an element
+ // in it.
+ select {
+ case bfr.run <- struct{}{}:
+ default:
+ }
+}
+
+// assumes the lock is not held
+func (bfr *BoundedFrequencyRunner) stop() {
+ bfr.mu.Lock()
+ defer bfr.mu.Unlock()
+ bfr.limiter.Stop()
+ bfr.timer.Stop()
+}
+
+// assumes the lock is not held
+func (bfr *BoundedFrequencyRunner) tryRun() {
+ bfr.mu.Lock()
+ defer bfr.mu.Unlock()
+
+ if bfr.limiter.TryAccept() {
+ // We're allowed to run the function right now.
+ bfr.fn()
+ bfr.lastRun = bfr.timer.Now()
+ bfr.timer.Stop()
+ bfr.timer.Reset(bfr.maxInterval)
+ glog.V(3).Infof("%s: ran, next possible in %v, periodic in %v", bfr.name, bfr.minInterval, bfr.maxInterval)
+ return
+ }
+
+ // It can't run right now, figure out when it can run next.
+
+ elapsed := bfr.timer.Since(bfr.lastRun) // how long since last run
+ nextPossible := bfr.minInterval - elapsed // time to next possible run
+ nextScheduled := bfr.maxInterval - elapsed // time to next periodic run
+ glog.V(4).Infof("%s: %v since last run, possible in %v, scheduled in %v", bfr.name, elapsed, nextPossible, nextScheduled)
+
+ if nextPossible < nextScheduled {
+ // Set the timer for ASAP, but don't drain here. Assuming Loop is running,
+ // it might get a delivery in the mean time, but that is OK.
+ bfr.timer.Stop()
+ bfr.timer.Reset(nextPossible)
+ glog.V(3).Infof("%s: throttled, scheduling run in %v", bfr.name, nextPossible)
+ }
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/async/runner.go b/vendor/k8s.io/kubernetes/pkg/util/async/runner.go
new file mode 100644
index 000000000..924f1d168
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/async/runner.go
@@ -0,0 +1,58 @@
+/*
+Copyright 2014 The Kubernetes 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 async
+
+import (
+ "sync"
+)
+
+// Runner is an abstraction to make it easy to start and stop groups of things that can be
+// described by a single function which waits on a channel close to exit.
+type Runner struct {
+ lock sync.Mutex
+ loopFuncs []func(stop chan struct{})
+ stop *chan struct{}
+}
+
+// NewRunner makes a runner for the given function(s). The function(s) should loop until
+// the channel is closed.
+func NewRunner(f ...func(stop chan struct{})) *Runner {
+ return &Runner{loopFuncs: f}
+}
+
+// Start begins running.
+func (r *Runner) Start() {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ if r.stop == nil {
+ c := make(chan struct{})
+ r.stop = &c
+ for i := range r.loopFuncs {
+ go r.loopFuncs[i](*r.stop)
+ }
+ }
+}
+
+// Stop stops running.
+func (r *Runner) Stop() {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ if r.stop != nil {
+ close(*r.stop)
+ r.stop = nil
+ }
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go b/vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go
new file mode 100644
index 000000000..702d16e5d
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/dbus/dbus.go
@@ -0,0 +1,133 @@
+/*
+Copyright 2015 The Kubernetes 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 dbus
+
+import (
+ godbus "github.com/godbus/dbus"
+)
+
+// Interface is an interface that presents a subset of the godbus/dbus API. Use this
+// when you want to inject fakeable/mockable D-Bus behavior.
+type Interface interface {
+ // SystemBus returns a connection to the system bus, connecting to it
+ // first if necessary
+ SystemBus() (Connection, error)
+ // SessionBus returns a connection to the session bus, connecting to it
+ // first if necessary
+ SessionBus() (Connection, error)
+}
+
+// Connection represents a D-Bus connection
+type Connection interface {
+ // Returns an Object representing the bus itself
+ BusObject() Object
+
+ // Object creates a representation of a remote D-Bus object
+ Object(name, path string) Object
+
+ // Signal registers or unregisters a channel to receive D-Bus signals
+ Signal(ch chan<- *godbus.Signal)
+}
+
+// Object represents a remote D-Bus object
+type Object interface {
+ // Call synchronously calls a D-Bus method
+ Call(method string, flags godbus.Flags, args ...interface{}) Call
+}
+
+// Call represents a pending or completed D-Bus method call
+type Call interface {
+ // Store returns a completed call's return values, or an error
+ Store(retvalues ...interface{}) error
+}
+
+// Implements Interface in terms of actually talking to D-Bus
+type dbusImpl struct {
+ systemBus *connImpl
+ sessionBus *connImpl
+}
+
+// Implements Connection as a godbus.Conn
+type connImpl struct {
+ conn *godbus.Conn
+}
+
+// Implements Object as a godbus.Object
+type objectImpl struct {
+ object godbus.BusObject
+}
+
+// Implements Call as a godbus.Call
+type callImpl struct {
+ call *godbus.Call
+}
+
+// New returns a new Interface which will use godbus to talk to D-Bus
+func New() Interface {
+ return &dbusImpl{}
+}
+
+// SystemBus is part of Interface
+func (db *dbusImpl) SystemBus() (Connection, error) {
+ if db.systemBus == nil {
+ bus, err := godbus.SystemBus()
+ if err != nil {
+ return nil, err
+ }
+ db.systemBus = &connImpl{bus}
+ }
+
+ return db.systemBus, nil
+}
+
+// SessionBus is part of Interface
+func (db *dbusImpl) SessionBus() (Connection, error) {
+ if db.sessionBus == nil {
+ bus, err := godbus.SessionBus()
+ if err != nil {
+ return nil, err
+ }
+ db.sessionBus = &connImpl{bus}
+ }
+
+ return db.sessionBus, nil
+}
+
+// BusObject is part of the Connection interface
+func (conn *connImpl) BusObject() Object {
+ return &objectImpl{conn.conn.BusObject()}
+}
+
+// Object is part of the Connection interface
+func (conn *connImpl) Object(name, path string) Object {
+ return &objectImpl{conn.conn.Object(name, godbus.ObjectPath(path))}
+}
+
+// Signal is part of the Connection interface
+func (conn *connImpl) Signal(ch chan<- *godbus.Signal) {
+ conn.conn.Signal(ch)
+}
+
+// Call is part of the Object interface
+func (obj *objectImpl) Call(method string, flags godbus.Flags, args ...interface{}) Call {
+ return &callImpl{obj.object.Call(method, flags, args...)}
+}
+
+// Store is part of the Call interface
+func (call *callImpl) Store(retvalues ...interface{}) error {
+ return call.call.Store(retvalues...)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go b/vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go
new file mode 100644
index 000000000..b07da628d
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/dbus/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2015 The Kubernetes 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 dbus provides an injectable interface and implementations for D-Bus communication
+package dbus // import "k8s.io/kubernetes/pkg/util/dbus"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go b/vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
new file mode 100644
index 000000000..44131272e
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/dbus/fake_dbus.go
@@ -0,0 +1,135 @@
+/*
+Copyright 2015 The Kubernetes 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 dbus
+
+import (
+ "fmt"
+
+ godbus "github.com/godbus/dbus"
+)
+
+// DBusFake is a simple fake Interface type.
+type DBusFake struct {
+ systemBus *DBusFakeConnection
+ sessionBus *DBusFakeConnection
+}
+
+// DBusFakeConnection represents a fake D-Bus connection
+type DBusFakeConnection struct {
+ busObject *fakeObject
+ objects map[string]*fakeObject
+ signalHandlers []chan<- *godbus.Signal
+}
+
+// DBusFakeHandler is used to handle fake D-Bus method calls
+type DBusFakeHandler func(method string, args ...interface{}) ([]interface{}, error)
+
+type fakeObject struct {
+ handler DBusFakeHandler
+}
+
+type fakeCall struct {
+ ret []interface{}
+ err error
+}
+
+// NewFake returns a new Interface which will fake talking to D-Bus
+func NewFake(systemBus *DBusFakeConnection, sessionBus *DBusFakeConnection) *DBusFake {
+ return &DBusFake{systemBus, sessionBus}
+}
+
+func NewFakeConnection() *DBusFakeConnection {
+ return &DBusFakeConnection{
+ objects: make(map[string]*fakeObject),
+ }
+}
+
+// SystemBus is part of Interface
+func (db *DBusFake) SystemBus() (Connection, error) {
+ if db.systemBus != nil {
+ return db.systemBus, nil
+ } else {
+ return nil, fmt.Errorf("DBus is not running")
+ }
+}
+
+// SessionBus is part of Interface
+func (db *DBusFake) SessionBus() (Connection, error) {
+ if db.sessionBus != nil {
+ return db.sessionBus, nil
+ } else {
+ return nil, fmt.Errorf("DBus is not running")
+ }
+}
+
+// BusObject is part of the Connection interface
+func (conn *DBusFakeConnection) BusObject() Object {
+ return conn.busObject
+}
+
+// Object is part of the Connection interface
+func (conn *DBusFakeConnection) Object(name, path string) Object {
+ return conn.objects[name+path]
+}
+
+// Signal is part of the Connection interface
+func (conn *DBusFakeConnection) Signal(ch chan<- *godbus.Signal) {
+ for i := range conn.signalHandlers {
+ if conn.signalHandlers[i] == ch {
+ conn.signalHandlers = append(conn.signalHandlers[:i], conn.signalHandlers[i+1:]...)
+ return
+ }
+ }
+ conn.signalHandlers = append(conn.signalHandlers, ch)
+}
+
+// SetBusObject sets the handler for the BusObject of conn
+func (conn *DBusFakeConnection) SetBusObject(handler DBusFakeHandler) {
+ conn.busObject = &fakeObject{handler}
+}
+
+// AddObject adds a handler for the Object at name and path
+func (conn *DBusFakeConnection) AddObject(name, path string, handler DBusFakeHandler) {
+ conn.objects[name+path] = &fakeObject{handler}
+}
+
+// EmitSignal emits a signal on conn
+func (conn *DBusFakeConnection) EmitSignal(name, path, iface, signal string, args ...interface{}) {
+ sig := &godbus.Signal{
+ Sender: name,
+ Path: godbus.ObjectPath(path),
+ Name: iface + "." + signal,
+ Body: args,
+ }
+ for _, ch := range conn.signalHandlers {
+ ch <- sig
+ }
+}
+
+// Call is part of the Object interface
+func (obj *fakeObject) Call(method string, flags godbus.Flags, args ...interface{}) Call {
+ ret, err := obj.handler(method, args...)
+ return &fakeCall{ret, err}
+}
+
+// Store is part of the Call interface
+func (call *fakeCall) Store(retvalues ...interface{}) error {
+ if call.err != nil {
+ return call.err
+ }
+ return godbus.Store(call.ret, retvalues...)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/doc.go b/vendor/k8s.io/kubernetes/pkg/util/doc.go
new file mode 100644
index 000000000..f7e214f31
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright 2014 The Kubernetes 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 util implements various utility functions used in both testing and implementation
+// of Kubernetes. Package util may not depend on any other package in the Kubernetes
+// package tree.
+package util // import "k8s.io/kubernetes/pkg/util"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go b/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
new file mode 100644
index 000000000..de7301c8d
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/exec/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2014 The Kubernetes 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 exec provides an injectable interface and implementations for running commands.
+package exec // import "k8s.io/kubernetes/pkg/util/exec"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go b/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
new file mode 100644
index 000000000..f43bfa7a1
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/exec/exec.go
@@ -0,0 +1,200 @@
+/*
+Copyright 2014 The Kubernetes 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 exec
+
+import (
+ "io"
+ osexec "os/exec"
+ "syscall"
+ "time"
+)
+
+// ErrExecutableNotFound is returned if the executable is not found.
+var ErrExecutableNotFound = osexec.ErrNotFound
+
+// Interface is an interface that presents a subset of the os/exec API. Use this
+// when you want to inject fakeable/mockable exec behavior.
+type Interface interface {
+ // Command returns a Cmd instance which can be used to run a single command.
+ // This follows the pattern of package os/exec.
+ Command(cmd string, args ...string) Cmd
+
+ // LookPath wraps os/exec.LookPath
+ LookPath(file string) (string, error)
+}
+
+// Cmd is an interface that presents an API that is very similar to Cmd from os/exec.
+// As more functionality is needed, this can grow. Since Cmd is a struct, we will have
+// to replace fields with get/set method pairs.
+type Cmd interface {
+ // Run runs the command to the completion.
+ Run() error
+ // CombinedOutput runs the command and returns its combined standard output
+ // and standard error. This follows the pattern of package os/exec.
+ CombinedOutput() ([]byte, error)
+ // Output runs the command and returns standard output, but not standard err
+ Output() ([]byte, error)
+ SetDir(dir string)
+ SetStdin(in io.Reader)
+ SetStdout(out io.Writer)
+ SetStderr(out io.Writer)
+ // Stops the command by sending SIGTERM. It is not guaranteed the
+ // process will stop before this function returns. If the process is not
+ // responding, an internal timer function will send a SIGKILL to force
+ // terminate after 10 seconds.
+ Stop()
+}
+
+// ExitError is an interface that presents an API similar to os.ProcessState, which is
+// what ExitError from os/exec is. This is designed to make testing a bit easier and
+// probably loses some of the cross-platform properties of the underlying library.
+type ExitError interface {
+ String() string
+ Error() string
+ Exited() bool
+ ExitStatus() int
+}
+
+// Implements Interface in terms of really exec()ing.
+type executor struct{}
+
+// New returns a new Interface which will os/exec to run commands.
+func New() Interface {
+ return &executor{}
+}
+
+// Command is part of the Interface interface.
+func (executor *executor) Command(cmd string, args ...string) Cmd {
+ return (*cmdWrapper)(osexec.Command(cmd, args...))
+}
+
+// LookPath is part of the Interface interface
+func (executor *executor) LookPath(file string) (string, error) {
+ return osexec.LookPath(file)
+}
+
+// Wraps exec.Cmd so we can capture errors.
+type cmdWrapper osexec.Cmd
+
+func (cmd *cmdWrapper) SetDir(dir string) {
+ cmd.Dir = dir
+}
+
+func (cmd *cmdWrapper) SetStdin(in io.Reader) {
+ cmd.Stdin = in
+}
+
+func (cmd *cmdWrapper) SetStdout(out io.Writer) {
+ cmd.Stdout = out
+}
+
+func (cmd *cmdWrapper) SetStderr(out io.Writer) {
+ cmd.Stderr = out
+}
+
+// Run is part of the Cmd interface.
+func (cmd *cmdWrapper) Run() error {
+ return (*osexec.Cmd)(cmd).Run()
+}
+
+// CombinedOutput is part of the Cmd interface.
+func (cmd *cmdWrapper) CombinedOutput() ([]byte, error) {
+ out, err := (*osexec.Cmd)(cmd).CombinedOutput()
+ if err != nil {
+ return out, handleError(err)
+ }
+ return out, nil
+}
+
+func (cmd *cmdWrapper) Output() ([]byte, error) {
+ out, err := (*osexec.Cmd)(cmd).Output()
+ if err != nil {
+ return out, handleError(err)
+ }
+ return out, nil
+}
+
+// Stop is part of the Cmd interface.
+func (cmd *cmdWrapper) Stop() {
+ c := (*osexec.Cmd)(cmd)
+ if c.ProcessState.Exited() {
+ return
+ }
+ c.Process.Signal(syscall.SIGTERM)
+ time.AfterFunc(10*time.Second, func() {
+ if c.ProcessState.Exited() {
+ return
+ }
+ c.Process.Signal(syscall.SIGKILL)
+ })
+}
+
+func handleError(err error) error {
+ if ee, ok := err.(*osexec.ExitError); ok {
+ // Force a compile fail if exitErrorWrapper can't convert to ExitError.
+ var x ExitError = &ExitErrorWrapper{ee}
+ return x
+ }
+ if ee, ok := err.(*osexec.Error); ok {
+ if ee.Err == osexec.ErrNotFound {
+ return ErrExecutableNotFound
+ }
+ }
+ return err
+}
+
+// ExitErrorWrapper is an implementation of ExitError in terms of os/exec ExitError.
+// Note: standard exec.ExitError is type *os.ProcessState, which already implements Exited().
+type ExitErrorWrapper struct {
+ *osexec.ExitError
+}
+
+var _ ExitError = ExitErrorWrapper{}
+
+// ExitStatus is part of the ExitError interface.
+func (eew ExitErrorWrapper) ExitStatus() int {
+ ws, ok := eew.Sys().(syscall.WaitStatus)
+ if !ok {
+ panic("can't call ExitStatus() on a non-WaitStatus exitErrorWrapper")
+ }
+ return ws.ExitStatus()
+}
+
+// CodeExitError is an implementation of ExitError consisting of an error object
+// and an exit code (the upper bits of os.exec.ExitStatus).
+type CodeExitError struct {
+ Err error
+ Code int
+}
+
+var _ ExitError = CodeExitError{}
+
+func (e CodeExitError) Error() string {
+ return e.Err.Error()
+}
+
+func (e CodeExitError) String() string {
+ return e.Err.Error()
+}
+
+func (e CodeExitError) Exited() bool {
+ return true
+}
+
+func (e CodeExitError) ExitStatus() int {
+ return e.Code
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go b/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
new file mode 100644
index 000000000..c7fcd6cec
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/exec/fake_exec.go
@@ -0,0 +1,145 @@
+/*
+Copyright 2014 The Kubernetes 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 exec
+
+import (
+ "fmt"
+ "io"
+)
+
+// A simple scripted Interface type.
+type FakeExec struct {
+ CommandScript []FakeCommandAction
+ CommandCalls int
+ LookPathFunc func(string) (string, error)
+}
+
+type FakeCommandAction func(cmd string, args ...string) Cmd
+
+func (fake *FakeExec) Command(cmd string, args ...string) Cmd {
+ if fake.CommandCalls > len(fake.CommandScript)-1 {
+ panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
+ }
+ i := fake.CommandCalls
+ fake.CommandCalls++
+ return fake.CommandScript[i](cmd, args...)
+}
+
+func (fake *FakeExec) LookPath(file string) (string, error) {
+ return fake.LookPathFunc(file)
+}
+
+// A simple scripted Cmd type.
+type FakeCmd struct {
+ Argv []string
+ CombinedOutputScript []FakeCombinedOutputAction
+ CombinedOutputCalls int
+ CombinedOutputLog [][]string
+ RunScript []FakeRunAction
+ RunCalls int
+ RunLog [][]string
+ Dirs []string
+ Stdin io.Reader
+ Stdout io.Writer
+ Stderr io.Writer
+}
+
+func InitFakeCmd(fake *FakeCmd, cmd string, args ...string) Cmd {
+ fake.Argv = append([]string{cmd}, args...)
+ return fake
+}
+
+type FakeCombinedOutputAction func() ([]byte, error)
+type FakeRunAction func() ([]byte, []byte, error)
+
+func (fake *FakeCmd) SetDir(dir string) {
+ fake.Dirs = append(fake.Dirs, dir)
+}
+
+func (fake *FakeCmd) SetStdin(in io.Reader) {
+ fake.Stdin = in
+}
+
+func (fake *FakeCmd) SetStdout(out io.Writer) {
+ fake.Stdout = out
+}
+
+func (fake *FakeCmd) SetStderr(out io.Writer) {
+ fake.Stderr = out
+}
+
+func (fake *FakeCmd) Run() error {
+ if fake.RunCalls > len(fake.RunScript)-1 {
+ panic("ran out of Run() actions")
+ }
+ if fake.RunLog == nil {
+ fake.RunLog = [][]string{}
+ }
+ i := fake.RunCalls
+ fake.RunLog = append(fake.RunLog, append([]string{}, fake.Argv...))
+ fake.RunCalls++
+ stdout, stderr, err := fake.RunScript[i]()
+ if stdout != nil {
+ fake.Stdout.Write(stdout)
+ }
+ if stderr != nil {
+ fake.Stderr.Write(stderr)
+ }
+ return err
+}
+
+func (fake *FakeCmd) CombinedOutput() ([]byte, error) {
+ if fake.CombinedOutputCalls > len(fake.CombinedOutputScript)-1 {
+ panic("ran out of CombinedOutput() actions")
+ }
+ if fake.CombinedOutputLog == nil {
+ fake.CombinedOutputLog = [][]string{}
+ }
+ i := fake.CombinedOutputCalls
+ fake.CombinedOutputLog = append(fake.CombinedOutputLog, append([]string{}, fake.Argv...))
+ fake.CombinedOutputCalls++
+ return fake.CombinedOutputScript[i]()
+}
+
+func (fake *FakeCmd) Output() ([]byte, error) {
+ return nil, fmt.Errorf("unimplemented")
+}
+
+func (fake *FakeCmd) Stop() {
+ // no-op
+}
+
+// A simple fake ExitError type.
+type FakeExitError struct {
+ Status int
+}
+
+func (fake *FakeExitError) String() string {
+ return fmt.Sprintf("exit %d", fake.Status)
+}
+
+func (fake *FakeExitError) Error() string {
+ return fake.String()
+}
+
+func (fake *FakeExitError) Exited() bool {
+ return true
+}
+
+func (fake *FakeExitError) ExitStatus() int {
+ return fake.Status
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/hash/hash.go b/vendor/k8s.io/kubernetes/pkg/util/hash/hash.go
new file mode 100644
index 000000000..803f066a4
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/hash/hash.go
@@ -0,0 +1,37 @@
+/*
+Copyright 2015 The Kubernetes 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 hash
+
+import (
+ "hash"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// DeepHashObject writes specified object to hash using the spew library
+// which follows pointers and prints actual values of the nested objects
+// ensuring the hash does not change when a pointer changes.
+func DeepHashObject(hasher hash.Hash, objectToWrite interface{}) {
+ hasher.Reset()
+ printer := spew.ConfigState{
+ Indent: " ",
+ SortKeys: true,
+ DisableMethods: true,
+ SpewKeys: true,
+ }
+ printer.Fprintf(hasher, "%#v", objectToWrite)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go b/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go
new file mode 100644
index 000000000..0265b9fb1
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/interrupt/interrupt.go
@@ -0,0 +1,104 @@
+/*
+Copyright 2016 The Kubernetes 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 interrupt
+
+import (
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+)
+
+// terminationSignals are signals that cause the program to exit in the
+// supported platforms (linux, darwin, windows).
+var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT}
+
+// Handler guarantees execution of notifications after a critical section (the function passed
+// to a Run method), even in the presence of process termination. It guarantees exactly once
+// invocation of the provided notify functions.
+type Handler struct {
+ notify []func()
+ final func(os.Signal)
+ once sync.Once
+}
+
+// Chain creates a new handler that invokes all notify functions when the critical section exits
+// and then invokes the optional handler's notifications. This allows critical sections to be
+// nested without losing exactly once invocations. Notify functions can invoke any cleanup needed
+// but should not exit (which is the responsibility of the parent handler).
+func Chain(handler *Handler, notify ...func()) *Handler {
+ if handler == nil {
+ return New(nil, notify...)
+ }
+ return New(handler.Signal, append(notify, handler.Close)...)
+}
+
+// New creates a new handler that guarantees all notify functions are run after the critical
+// section exits (or is interrupted by the OS), then invokes the final handler. If no final
+// handler is specified, the default final is `os.Exit(1)`. A handler can only be used for
+// one critical section.
+func New(final func(os.Signal), notify ...func()) *Handler {
+ return &Handler{
+ final: final,
+ notify: notify,
+ }
+}
+
+// Close executes all the notification handlers if they have not yet been executed.
+func (h *Handler) Close() {
+ h.once.Do(func() {
+ for _, fn := range h.notify {
+ fn()
+ }
+ })
+}
+
+// Signal is called when an os.Signal is received, and guarantees that all notifications
+// are executed, then the final handler is executed. This function should only be called once
+// per Handler instance.
+func (h *Handler) Signal(s os.Signal) {
+ h.once.Do(func() {
+ for _, fn := range h.notify {
+ fn()
+ }
+ if h.final == nil {
+ os.Exit(1)
+ }
+ h.final(s)
+ })
+}
+
+// Run ensures that any notifications are invoked after the provided fn exits (even if the
+// process is interrupted by an OS termination signal). Notifications are only invoked once
+// per Handler instance, so calling Run more than once will not behave as the user expects.
+func (h *Handler) Run(fn func() error) error {
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, terminationSignals...)
+ defer func() {
+ signal.Stop(ch)
+ close(ch)
+ }()
+ go func() {
+ sig, ok := <-ch
+ if !ok {
+ return
+ }
+ h.Signal(sig)
+ }()
+ defer h.Close()
+ return fn()
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/io/io.go b/vendor/k8s.io/kubernetes/pkg/util/io/io.go
new file mode 100644
index 000000000..0be8e1272
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/io/io.go
@@ -0,0 +1,61 @@
+/*
+Copyright 2015 The Kubernetes 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 io
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/kubernetes/pkg/api"
+ "k8s.io/kubernetes/pkg/api/v1"
+)
+
+// LoadPodFromFile will read, decode, and return a Pod from a file.
+func LoadPodFromFile(filePath string) (*v1.Pod, error) {
+ if filePath == "" {
+ return nil, fmt.Errorf("file path not specified")
+ }
+ podDef, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read file path %s: %+v", filePath, err)
+ }
+ if len(podDef) == 0 {
+ return nil, fmt.Errorf("file was empty: %s", filePath)
+ }
+ pod := &v1.Pod{}
+
+ codec := api.Codecs.LegacyCodec(api.Registry.GroupOrDie(v1.GroupName).GroupVersion)
+ if err := runtime.DecodeInto(codec, podDef, pod); err != nil {
+ return nil, fmt.Errorf("failed decoding file: %v", err)
+ }
+ return pod, nil
+}
+
+// SavePodToFile will encode and save a pod to a given path & permissions
+func SavePodToFile(pod *v1.Pod, filePath string, perm os.FileMode) error {
+ if filePath == "" {
+ return fmt.Errorf("file path not specified")
+ }
+ codec := api.Codecs.LegacyCodec(api.Registry.GroupOrDie(v1.GroupName).GroupVersion)
+ data, err := runtime.Encode(codec, pod)
+ if err != nil {
+ return fmt.Errorf("failed encoding pod: %v", err)
+ }
+ return ioutil.WriteFile(filePath, data, perm)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/io/writer.go b/vendor/k8s.io/kubernetes/pkg/util/io/writer.go
new file mode 100644
index 000000000..086508c90
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/io/writer.go
@@ -0,0 +1,80 @@
+/*
+Copyright 2015 The Kubernetes 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 io
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+
+ "github.com/golang/glog"
+)
+
+// Writer is an interface which allows to write data to a file.
+type Writer interface {
+ WriteFile(filename string, data []byte, perm os.FileMode) error
+}
+
+// StdWriter implements Writer interface and uses standard libraries
+// for writing data to files.
+type StdWriter struct {
+}
+
+func (writer *StdWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
+ return ioutil.WriteFile(filename, data, perm)
+}
+
+// Alternative implementation of Writer interface that allows writing data to file
+// using nsenter command.
+// If a program (e.g. kubelet) runs in a container it may want to write data to
+// a mounted device. Since in Docker, mount propagation mode is set to private,
+// it will not see the mounted device in its own namespace. To work around this
+// limitaion one has to first enter hosts namespace (by using 'nsenter') and only
+// then write data.
+type NsenterWriter struct {
+}
+
+// TODO: should take a writer, not []byte
+func (writer *NsenterWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
+ cmd := "nsenter"
+ base_args := []string{
+ "--mount=/rootfs/proc/1/ns/mnt",
+ "--",
+ }
+
+ echo_args := append(base_args, "sh", "-c", fmt.Sprintf("cat > %s", filename))
+ glog.V(5).Infof("Command to write data to file: %v %v", cmd, echo_args)
+ command := exec.Command(cmd, echo_args...)
+ command.Stdin = bytes.NewBuffer(data)
+ outputBytes, err := command.CombinedOutput()
+ if err != nil {
+ glog.Errorf("Output from writing to %q: %v", filename, string(outputBytes))
+ return err
+ }
+
+ chmod_args := append(base_args, "chmod", fmt.Sprintf("%o", perm), filename)
+ glog.V(5).Infof("Command to change permissions to file: %v %v", cmd, chmod_args)
+ outputBytes, err = exec.Command(cmd, chmod_args...).CombinedOutput()
+ if err != nil {
+ glog.Errorf("Output from chmod command: %v", string(outputBytes))
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go b/vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go
new file mode 100644
index 000000000..f26498293
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/iptables/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2014 The Kubernetes 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 iptables provides an interface and implementations for running iptables commands.
+package iptables // import "k8s.io/kubernetes/pkg/util/iptables"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go
new file mode 100644
index 000000000..b6c08fa37
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables.go
@@ -0,0 +1,652 @@
+/*
+Copyright 2014 The Kubernetes 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 iptables
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "strings"
+ "sync"
+
+ godbus "github.com/godbus/dbus"
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/util/sets"
+ utildbus "k8s.io/kubernetes/pkg/util/dbus"
+ utilexec "k8s.io/kubernetes/pkg/util/exec"
+ utilversion "k8s.io/kubernetes/pkg/util/version"
+)
+
+type RulePosition string
+
+const (
+ Prepend RulePosition = "-I"
+ Append RulePosition = "-A"
+)
+
+// An injectable interface for running iptables commands. Implementations must be goroutine-safe.
+type Interface interface {
+ // GetVersion returns the "X.Y.Z" version string for iptables.
+ GetVersion() (string, error)
+ // EnsureChain checks if the specified chain exists and, if not, creates it. If the chain existed, return true.
+ EnsureChain(table Table, chain Chain) (bool, error)
+ // FlushChain clears the specified chain. If the chain did not exist, return error.
+ FlushChain(table Table, chain Chain) error
+ // DeleteChain deletes the specified chain. If the chain did not exist, return error.
+ DeleteChain(table Table, chain Chain) error
+ // EnsureRule checks if the specified rule is present and, if not, creates it. If the rule existed, return true.
+ EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error)
+ // DeleteRule checks if the specified rule is present and, if so, deletes it.
+ DeleteRule(table Table, chain Chain, args ...string) error
+ // IsIpv6 returns true if this is managing ipv6 tables
+ IsIpv6() bool
+ // SaveInto calls `iptables-save` for table and stores result in a given buffer.
+ SaveInto(table Table, buffer *bytes.Buffer) error
+ // Restore runs `iptables-restore` passing data through []byte.
+ // table is the Table to restore
+ // data should be formatted like the output of SaveInto()
+ // flush sets the presence of the "--noflush" flag. see: FlushFlag
+ // counters sets the "--counters" flag. see: RestoreCountersFlag
+ Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error
+ // RestoreAll is the same as Restore except that no table is specified.
+ RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error
+ // AddReloadFunc adds a function to call on iptables reload
+ AddReloadFunc(reloadFunc func())
+ // Destroy cleans up resources used by the Interface
+ Destroy()
+}
+
+type Protocol byte
+
+const (
+ ProtocolIpv4 Protocol = iota + 1
+ ProtocolIpv6
+)
+
+type Table string
+
+const (
+ TableNAT Table = "nat"
+ TableFilter Table = "filter"
+)
+
+type Chain string
+
+const (
+ ChainPostrouting Chain = "POSTROUTING"
+ ChainPrerouting Chain = "PREROUTING"
+ ChainOutput Chain = "OUTPUT"
+ ChainInput Chain = "INPUT"
+)
+
+const (
+ cmdIPTablesSave string = "iptables-save"
+ cmdIPTablesRestore string = "iptables-restore"
+ cmdIPTables string = "iptables"
+ cmdIp6tables string = "ip6tables"
+)
+
+// Option flag for Restore
+type RestoreCountersFlag bool
+
+const RestoreCounters RestoreCountersFlag = true
+const NoRestoreCounters RestoreCountersFlag = false
+
+// Option flag for Flush
+type FlushFlag bool
+
+const FlushTables FlushFlag = true
+const NoFlushTables FlushFlag = false
+
+// Versions of iptables less than this do not support the -C / --check flag
+// (test whether a rule exists).
+const MinCheckVersion = "1.4.11"
+
+// Minimum iptables versions supporting the -w and -w2 flags
+const MinWaitVersion = "1.4.20"
+const MinWait2Version = "1.4.22"
+
+const LockfilePath16x = "/run/xtables.lock"
+
+// runner implements Interface in terms of exec("iptables").
+type runner struct {
+ mu sync.Mutex
+ exec utilexec.Interface
+ dbus utildbus.Interface
+ protocol Protocol
+ hasCheck bool
+ waitFlag []string
+ restoreWaitFlag []string
+ lockfilePath string
+
+ reloadFuncs []func()
+ signal chan *godbus.Signal
+}
+
+// newInternal returns a new Interface which will exec iptables, and allows the
+// caller to change the iptables-restore lockfile path
+func newInternal(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol, lockfilePath string) Interface {
+ vstring, err := getIPTablesVersionString(exec)
+ if err != nil {
+ glog.Warningf("Error checking iptables version, assuming version at least %s: %v", MinCheckVersion, err)
+ vstring = MinCheckVersion
+ }
+
+ if lockfilePath == "" {
+ lockfilePath = LockfilePath16x
+ }
+
+ runner := &runner{
+ exec: exec,
+ dbus: dbus,
+ protocol: protocol,
+ hasCheck: getIPTablesHasCheckCommand(vstring),
+ waitFlag: getIPTablesWaitFlag(vstring),
+ restoreWaitFlag: getIPTablesRestoreWaitFlag(exec),
+ lockfilePath: lockfilePath,
+ }
+ // TODO this needs to be moved to a separate Start() or Run() function so that New() has zero side
+ // effects.
+ runner.connectToFirewallD()
+ return runner
+}
+
+// New returns a new Interface which will exec iptables.
+func New(exec utilexec.Interface, dbus utildbus.Interface, protocol Protocol) Interface {
+ return newInternal(exec, dbus, protocol, "")
+}
+
+// Destroy is part of Interface.
+func (runner *runner) Destroy() {
+ if runner.signal != nil {
+ runner.signal <- nil
+ }
+}
+
+const (
+ firewalldName = "org.fedoraproject.FirewallD1"
+ firewalldPath = "/org/fedoraproject/FirewallD1"
+ firewalldInterface = "org.fedoraproject.FirewallD1"
+)
+
+// Connects to D-Bus and listens for FirewallD start/restart. (On non-FirewallD-using
+// systems, this is effectively a no-op; we listen for the signals, but they will never be
+// emitted, so reload() will never be called.)
+func (runner *runner) connectToFirewallD() {
+ bus, err := runner.dbus.SystemBus()
+ if err != nil {
+ glog.V(1).Infof("Could not connect to D-Bus system bus: %s", err)
+ return
+ }
+
+ rule := fmt.Sprintf("type='signal',sender='%s',path='%s',interface='%s',member='Reloaded'", firewalldName, firewalldPath, firewalldInterface)
+ bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+ rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", firewalldName)
+ bus.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+ runner.signal = make(chan *godbus.Signal, 10)
+ bus.Signal(runner.signal)
+
+ go runner.dbusSignalHandler(bus)
+}
+
+// GetVersion returns the version string.
+func (runner *runner) GetVersion() (string, error) {
+ return getIPTablesVersionString(runner.exec)
+}
+
+// EnsureChain is part of Interface.
+func (runner *runner) EnsureChain(table Table, chain Chain) (bool, error) {
+ fullArgs := makeFullArgs(table, chain)
+
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ out, err := runner.run(opCreateChain, fullArgs)
+ if err != nil {
+ if ee, ok := err.(utilexec.ExitError); ok {
+ if ee.Exited() && ee.ExitStatus() == 1 {
+ return true, nil
+ }
+ }
+ return false, fmt.Errorf("error creating chain %q: %v: %s", chain, err, out)
+ }
+ return false, nil
+}
+
+// FlushChain is part of Interface.
+func (runner *runner) FlushChain(table Table, chain Chain) error {
+ fullArgs := makeFullArgs(table, chain)
+
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ out, err := runner.run(opFlushChain, fullArgs)
+ if err != nil {
+ return fmt.Errorf("error flushing chain %q: %v: %s", chain, err, out)
+ }
+ return nil
+}
+
+// DeleteChain is part of Interface.
+func (runner *runner) DeleteChain(table Table, chain Chain) error {
+ fullArgs := makeFullArgs(table, chain)
+
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ // TODO: we could call iptables -S first, ignore the output and check for non-zero return (more like DeleteRule)
+ out, err := runner.run(opDeleteChain, fullArgs)
+ if err != nil {
+ return fmt.Errorf("error deleting chain %q: %v: %s", chain, err, out)
+ }
+ return nil
+}
+
+// EnsureRule is part of Interface.
+func (runner *runner) EnsureRule(position RulePosition, table Table, chain Chain, args ...string) (bool, error) {
+ fullArgs := makeFullArgs(table, chain, args...)
+
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ exists, err := runner.checkRule(table, chain, args...)
+ if err != nil {
+ return false, err
+ }
+ if exists {
+ return true, nil
+ }
+ out, err := runner.run(operation(position), fullArgs)
+ if err != nil {
+ return false, fmt.Errorf("error appending rule: %v: %s", err, out)
+ }
+ return false, nil
+}
+
+// DeleteRule is part of Interface.
+func (runner *runner) DeleteRule(table Table, chain Chain, args ...string) error {
+ fullArgs := makeFullArgs(table, chain, args...)
+
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ exists, err := runner.checkRule(table, chain, args...)
+ if err != nil {
+ return err
+ }
+ if !exists {
+ return nil
+ }
+ out, err := runner.run(opDeleteRule, fullArgs)
+ if err != nil {
+ return fmt.Errorf("error deleting rule: %v: %s", err, out)
+ }
+ return nil
+}
+
+func (runner *runner) IsIpv6() bool {
+ return runner.protocol == ProtocolIpv6
+}
+
+// SaveInto is part of Interface.
+func (runner *runner) SaveInto(table Table, buffer *bytes.Buffer) error {
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ // run and return
+ args := []string{"-t", string(table)}
+ glog.V(4).Infof("running iptables-save %v", args)
+ cmd := runner.exec.Command(cmdIPTablesSave, args...)
+ // Since CombinedOutput() doesn't support redirecting it to a buffer,
+ // we need to workaround it by redirecting stdout and stderr to buffer
+ // and explicitly calling Run() [CombinedOutput() underneath itself
+ // creates a new buffer, redirects stdout and stderr to it and also
+ // calls Run()].
+ cmd.SetStdout(buffer)
+ cmd.SetStderr(buffer)
+ return cmd.Run()
+}
+
+// Restore is part of Interface.
+func (runner *runner) Restore(table Table, data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
+ // setup args
+ args := []string{"-T", string(table)}
+ return runner.restoreInternal(args, data, flush, counters)
+}
+
+// RestoreAll is part of Interface.
+func (runner *runner) RestoreAll(data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
+ // setup args
+ args := make([]string, 0)
+ return runner.restoreInternal(args, data, flush, counters)
+}
+
+type iptablesLocker interface {
+ Close()
+}
+
+// restoreInternal is the shared part of Restore/RestoreAll
+func (runner *runner) restoreInternal(args []string, data []byte, flush FlushFlag, counters RestoreCountersFlag) error {
+ runner.mu.Lock()
+ defer runner.mu.Unlock()
+
+ if !flush {
+ args = append(args, "--noflush")
+ }
+ if counters {
+ args = append(args, "--counters")
+ }
+
+ // Grab the iptables lock to prevent iptables-restore and iptables
+ // from stepping on each other. iptables-restore 1.6.2 will have
+ // a --wait option like iptables itself, but that's not widely deployed.
+ if len(runner.restoreWaitFlag) == 0 {
+ locker, err := grabIptablesLocks(runner.lockfilePath)
+ if err != nil {
+ return err
+ }
+ defer locker.Close()
+ }
+
+ // run the command and return the output or an error including the output and error
+ fullArgs := append(runner.restoreWaitFlag, args...)
+ glog.V(4).Infof("running iptables-restore %v", fullArgs)
+ cmd := runner.exec.Command(cmdIPTablesRestore, fullArgs...)
+ cmd.SetStdin(bytes.NewBuffer(data))
+ b, err := cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("%v (%s)", err, b)
+ }
+ return nil
+}
+
+func (runner *runner) iptablesCommand() string {
+ if runner.IsIpv6() {
+ return cmdIp6tables
+ } else {
+ return cmdIPTables
+ }
+}
+
+func (runner *runner) run(op operation, args []string) ([]byte, error) {
+ iptablesCmd := runner.iptablesCommand()
+
+ fullArgs := append(runner.waitFlag, string(op))
+ fullArgs = append(fullArgs, args...)
+ glog.V(5).Infof("running iptables %s %v", string(op), args)
+ return runner.exec.Command(iptablesCmd, fullArgs...).CombinedOutput()
+ // Don't log err here - callers might not think it is an error.
+}
+
+// Returns (bool, nil) if it was able to check the existence of the rule, or
+// (<undefined>, error) if the process of checking failed.
+func (runner *runner) checkRule(table Table, chain Chain, args ...string) (bool, error) {
+ if runner.hasCheck {
+ return runner.checkRuleUsingCheck(makeFullArgs(table, chain, args...))
+ } else {
+ return runner.checkRuleWithoutCheck(table, chain, args...)
+ }
+}
+
+var hexnumRE = regexp.MustCompile("0x0+([0-9])")
+
+func trimhex(s string) string {
+ return hexnumRE.ReplaceAllString(s, "0x$1")
+}
+
+// Executes the rule check without using the "-C" flag, instead parsing iptables-save.
+// Present for compatibility with <1.4.11 versions of iptables. This is full
+// of hack and half-measures. We should nix this ASAP.
+func (runner *runner) checkRuleWithoutCheck(table Table, chain Chain, args ...string) (bool, error) {
+ glog.V(1).Infof("running iptables-save -t %s", string(table))
+ out, err := runner.exec.Command(cmdIPTablesSave, "-t", string(table)).CombinedOutput()
+ if err != nil {
+ return false, fmt.Errorf("error checking rule: %v", err)
+ }
+
+ // Sadly, iptables has inconsistent quoting rules for comments. Just remove all quotes.
+ // Also, quoted multi-word comments (which are counted as a single arg)
+ // will be unpacked into multiple args,
+ // in order to compare against iptables-save output (which will be split at whitespace boundary)
+ // e.g. a single arg('"this must be before the NodePort rules"') will be unquoted and unpacked into 7 args.
+ var argsCopy []string
+ for i := range args {
+ tmpField := strings.Trim(args[i], "\"")
+ tmpField = trimhex(tmpField)
+ argsCopy = append(argsCopy, strings.Fields(tmpField)...)
+ }
+ argset := sets.NewString(argsCopy...)
+
+ for _, line := range strings.Split(string(out), "\n") {
+ var fields = strings.Fields(line)
+
+ // Check that this is a rule for the correct chain, and that it has
+ // the correct number of argument (+2 for "-A <chain name>")
+ if !strings.HasPrefix(line, fmt.Sprintf("-A %s", string(chain))) || len(fields) != len(argsCopy)+2 {
+ continue
+ }
+
+ // Sadly, iptables has inconsistent quoting rules for comments.
+ // Just remove all quotes.
+ for i := range fields {
+ fields[i] = strings.Trim(fields[i], "\"")
+ fields[i] = trimhex(fields[i])
+ }
+
+ // TODO: This misses reorderings e.g. "-x foo ! -y bar" will match "! -x foo -y bar"
+ if sets.NewString(fields...).IsSuperset(argset) {
+ return true, nil
+ }
+ glog.V(5).Infof("DBG: fields is not a superset of args: fields=%v args=%v", fields, args)
+ }
+
+ return false, nil
+}
+
+// Executes the rule check using the "-C" flag
+func (runner *runner) checkRuleUsingCheck(args []string) (bool, error) {
+ out, err := runner.run(opCheckRule, args)
+ if err == nil {
+ return true, nil
+ }
+ if ee, ok := err.(utilexec.ExitError); ok {
+ // iptables uses exit(1) to indicate a failure of the operation,
+ // as compared to a malformed commandline, for example.
+ if ee.Exited() && ee.ExitStatus() == 1 {
+ return false, nil
+ }
+ }
+ return false, fmt.Errorf("error checking rule: %v: %s", err, out)
+}
+
+type operation string
+
+const (
+ opCreateChain operation = "-N"
+ opFlushChain operation = "-F"
+ opDeleteChain operation = "-X"
+ opAppendRule operation = "-A"
+ opCheckRule operation = "-C"
+ opDeleteRule operation = "-D"
+)
+
+func makeFullArgs(table Table, chain Chain, args ...string) []string {
+ return append([]string{string(chain), "-t", string(table)}, args...)
+}
+
+// Checks if iptables has the "-C" flag
+func getIPTablesHasCheckCommand(vstring string) bool {
+ minVersion, err := utilversion.ParseGeneric(MinCheckVersion)
+ if err != nil {
+ glog.Errorf("MinCheckVersion (%s) is not a valid version string: %v", MinCheckVersion, err)
+ return true
+ }
+ version, err := utilversion.ParseGeneric(vstring)
+ if err != nil {
+ glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
+ return true
+ }
+ return version.AtLeast(minVersion)
+}
+
+// Checks if iptables version has a "wait" flag
+func getIPTablesWaitFlag(vstring string) []string {
+ version, err := utilversion.ParseGeneric(vstring)
+ if err != nil {
+ glog.Errorf("vstring (%s) is not a valid version string: %v", vstring, err)
+ return nil
+ }
+
+ minVersion, err := utilversion.ParseGeneric(MinWaitVersion)
+ if err != nil {
+ glog.Errorf("MinWaitVersion (%s) is not a valid version string: %v", MinWaitVersion, err)
+ return nil
+ }
+ if version.LessThan(minVersion) {
+ return nil
+ }
+
+ minVersion, err = utilversion.ParseGeneric(MinWait2Version)
+ if err != nil {
+ glog.Errorf("MinWait2Version (%s) is not a valid version string: %v", MinWait2Version, err)
+ return nil
+ }
+ if version.LessThan(minVersion) {
+ return []string{"-w"}
+ } else {
+ return []string{"-w2"}
+ }
+}
+
+// getIPTablesVersionString runs "iptables --version" to get the version string
+// in the form "X.X.X"
+func getIPTablesVersionString(exec utilexec.Interface) (string, error) {
+ // this doesn't access mutable state so we don't need to use the interface / runner
+ bytes, err := exec.Command(cmdIPTables, "--version").CombinedOutput()
+ if err != nil {
+ return "", err
+ }
+ versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
+ match := versionMatcher.FindStringSubmatch(string(bytes))
+ if match == nil {
+ return "", fmt.Errorf("no iptables version found in string: %s", bytes)
+ }
+ return match[1], nil
+}
+
+// Checks if iptables-restore has a "wait" flag
+// --wait support landed in v1.6.1+ right before --version support, so
+// any version of iptables-restore that supports --version will also
+// support --wait
+func getIPTablesRestoreWaitFlag(exec utilexec.Interface) []string {
+ vstring, err := getIPTablesRestoreVersionString(exec)
+ if err != nil || vstring == "" {
+ glog.V(3).Infof("couldn't get iptables-restore version; assuming it doesn't support --wait")
+ return nil
+ }
+ if _, err := utilversion.ParseGeneric(vstring); err != nil {
+ glog.V(3).Infof("couldn't parse iptables-restore version; assuming it doesn't support --wait")
+ return nil
+ }
+
+ return []string{"--wait=2"}
+}
+
+// getIPTablesRestoreVersionString runs "iptables-restore --version" to get the version string
+// in the form "X.X.X"
+func getIPTablesRestoreVersionString(exec utilexec.Interface) (string, error) {
+ // this doesn't access mutable state so we don't need to use the interface / runner
+
+ // iptables-restore hasn't always had --version, and worse complains
+ // about unrecognized commands but doesn't exit when it gets them.
+ // Work around that by setting stdin to nothing so it exits immediately.
+ cmd := exec.Command(cmdIPTablesRestore, "--version")
+ cmd.SetStdin(bytes.NewReader([]byte{}))
+ bytes, err := cmd.CombinedOutput()
+ if err != nil {
+ return "", err
+ }
+ versionMatcher := regexp.MustCompile("v([0-9]+(\\.[0-9]+)+)")
+ match := versionMatcher.FindStringSubmatch(string(bytes))
+ if match == nil {
+ return "", fmt.Errorf("no iptables version found in string: %s", bytes)
+ }
+ return match[1], nil
+}
+
+// goroutine to listen for D-Bus signals
+func (runner *runner) dbusSignalHandler(bus utildbus.Connection) {
+ firewalld := bus.Object(firewalldName, firewalldPath)
+
+ for s := range runner.signal {
+ if s == nil {
+ // Unregister
+ bus.Signal(runner.signal)
+ return
+ }
+
+ switch s.Name {
+ case "org.freedesktop.DBus.NameOwnerChanged":
+ name := s.Body[0].(string)
+ new_owner := s.Body[2].(string)
+
+ if name != firewalldName || len(new_owner) == 0 {
+ continue
+ }
+
+ // FirewallD startup (specifically the part where it deletes
+ // all existing iptables rules) may not yet be complete when
+ // we get this signal, so make a dummy request to it to
+ // synchronize.
+ firewalld.Call(firewalldInterface+".getDefaultZone", 0)
+
+ runner.reload()
+ case firewalldInterface + ".Reloaded":
+ runner.reload()
+ }
+ }
+}
+
+// AddReloadFunc is part of Interface
+func (runner *runner) AddReloadFunc(reloadFunc func()) {
+ runner.reloadFuncs = append(runner.reloadFuncs, reloadFunc)
+}
+
+// runs all reload funcs to re-sync iptables rules
+func (runner *runner) reload() {
+ glog.V(1).Infof("reloading iptables rules")
+
+ for _, f := range runner.reloadFuncs {
+ f()
+ }
+}
+
+// IsNotFoundError returns true if the error indicates "not found". It parses
+// the error string looking for known values, which is imperfect but works in
+// practice.
+func IsNotFoundError(err error) bool {
+ es := err.Error()
+ if strings.Contains(es, "No such file or directory") {
+ return true
+ }
+ if strings.Contains(es, "No chain/target/match by that name") {
+ return true
+ }
+ return false
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go
new file mode 100644
index 000000000..4f614cb52
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_linux.go
@@ -0,0 +1,93 @@
+// +build linux
+
+/*
+Copyright 2017 The Kubernetes 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 iptables
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "k8s.io/apimachinery/pkg/util/wait"
+)
+
+type locker struct {
+ lock16 *os.File
+ lock14 *net.UnixListener
+}
+
+func (l *locker) Close() {
+ if l.lock16 != nil {
+ l.lock16.Close()
+ }
+ if l.lock14 != nil {
+ l.lock14.Close()
+ }
+}
+
+func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) {
+ var err error
+ var success bool
+
+ l := &locker{}
+ defer func(l *locker) {
+ // Clean up immediately on failure
+ if !success {
+ l.Close()
+ }
+ }(l)
+
+ // Grab both 1.6.x and 1.4.x-style locks; we don't know what the
+ // iptables-restore version is if it doesn't support --wait, so we
+ // can't assume which lock method it'll use.
+
+ // Roughly duplicate iptables 1.6.x xtables_lock() function.
+ l.lock16, err = os.OpenFile(lockfilePath, os.O_CREATE, 0600)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open iptables lock %s: %v", lockfilePath, err)
+ }
+
+ if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
+ if err := grabIptablesFileLock(l.lock16); err != nil {
+ return false, nil
+ }
+ return true, nil
+ }); err != nil {
+ return nil, fmt.Errorf("failed to acquire new iptables lock: %v", err)
+ }
+
+ // Roughly duplicate iptables 1.4.x xtables_lock() function.
+ if err := wait.PollImmediate(200*time.Millisecond, 2*time.Second, func() (bool, error) {
+ l.lock14, err = net.ListenUnix("unix", &net.UnixAddr{Name: "@xtables", Net: "unix"})
+ if err != nil {
+ return false, nil
+ }
+ return true, nil
+ }); err != nil {
+ return nil, fmt.Errorf("failed to acquire old iptables lock: %v", err)
+ }
+
+ success = true
+ return l, nil
+}
+
+func grabIptablesFileLock(f *os.File) error {
+ return unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go
new file mode 100644
index 000000000..c6a5f0d7d
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/iptables/iptables_unsupported.go
@@ -0,0 +1,32 @@
+// +build !linux
+
+/*
+Copyright 2017 The Kubernetes 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 iptables
+
+import (
+ "fmt"
+ "os"
+)
+
+func grabIptablesLocks(lockfilePath string) (iptablesLocker, error) {
+ return nil, fmt.Errorf("iptables unsupported on this platform")
+}
+
+func grabIptablesFileLock(f *os.File) error {
+ return fmt.Errorf("iptables unsupported on this platform")
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go b/vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go
new file mode 100644
index 000000000..6f4eacaca
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/iptables/save_restore.go
@@ -0,0 +1,110 @@
+/*
+Copyright 2014 The Kubernetes 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 iptables
+
+import (
+ "fmt"
+ "strings"
+)
+
+// MakeChainLine return an iptables-save/restore formatted chain line given a Chain
+func MakeChainLine(chain Chain) string {
+ return fmt.Sprintf(":%s - [0:0]", chain)
+}
+
+// GetChainLines parses a table's iptables-save data to find chains in the table.
+// It returns a map of iptables.Chain to string where the string is the chain line from the save (with counters etc).
+func GetChainLines(table Table, save []byte) map[Chain]string {
+ chainsMap := make(map[Chain]string)
+ tablePrefix := "*" + string(table)
+ readIndex := 0
+ // find beginning of table
+ for readIndex < len(save) {
+ line, n := ReadLine(readIndex, save)
+ readIndex = n
+ if strings.HasPrefix(line, tablePrefix) {
+ break
+ }
+ }
+ // parse table lines
+ for readIndex < len(save) {
+ line, n := ReadLine(readIndex, save)
+ readIndex = n
+ if len(line) == 0 {
+ continue
+ }
+ if strings.HasPrefix(line, "COMMIT") || strings.HasPrefix(line, "*") {
+ break
+ } else if strings.HasPrefix(line, "#") {
+ continue
+ } else if strings.HasPrefix(line, ":") && len(line) > 1 {
+ // We assume that the <line> contains space - chain lines have 3 fields,
+ // space delimited. If there is no space, this line will panic.
+ chain := Chain(line[1:strings.Index(line, " ")])
+ chainsMap[chain] = line
+ }
+ }
+ return chainsMap
+}
+
+func ReadLine(readIndex int, byteArray []byte) (string, int) {
+ currentReadIndex := readIndex
+
+ // consume left spaces
+ for currentReadIndex < len(byteArray) {
+ if byteArray[currentReadIndex] == ' ' {
+ currentReadIndex++
+ } else {
+ break
+ }
+ }
+
+ // leftTrimIndex stores the left index of the line after the line is left-trimmed
+ leftTrimIndex := currentReadIndex
+
+ // rightTrimIndex stores the right index of the line after the line is right-trimmed
+ // it is set to -1 since the correct value has not yet been determined.
+ rightTrimIndex := -1
+
+ for ; currentReadIndex < len(byteArray); currentReadIndex++ {
+ if byteArray[currentReadIndex] == ' ' {
+ // set rightTrimIndex
+ if rightTrimIndex == -1 {
+ rightTrimIndex = currentReadIndex
+ }
+ } else if (byteArray[currentReadIndex] == '\n') || (currentReadIndex == (len(byteArray) - 1)) {
+ // end of line or byte buffer is reached
+ if currentReadIndex <= leftTrimIndex {
+ return "", currentReadIndex + 1
+ }
+ // set the rightTrimIndex
+ if rightTrimIndex == -1 {
+ rightTrimIndex = currentReadIndex
+ if currentReadIndex == (len(byteArray)-1) && (byteArray[currentReadIndex] != '\n') {
+ // ensure that the last character is part of the returned string,
+ // unless the last character is '\n'
+ rightTrimIndex = currentReadIndex + 1
+ }
+ }
+ return string(byteArray[leftTrimIndex:rightTrimIndex]), currentReadIndex + 1
+ } else {
+ // unset rightTrimIndex
+ rightTrimIndex = -1
+ }
+ }
+ return "", currentReadIndex
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go b/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
new file mode 100644
index 000000000..15179e53f
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2014 The Kubernetes 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 mount defines an interface to mounting filesystems.
+package mount // import "k8s.io/kubernetes/pkg/util/mount"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
new file mode 100644
index 000000000..2b71fa0a7
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
@@ -0,0 +1,173 @@
+/*
+Copyright 2015 The Kubernetes 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 mount
+
+import (
+ "path/filepath"
+ "sync"
+
+ "github.com/golang/glog"
+)
+
+// FakeMounter implements mount.Interface for tests.
+type FakeMounter struct {
+ MountPoints []MountPoint
+ Log []FakeAction
+ // Some tests run things in parallel, make sure the mounter does not produce
+ // any golang's DATA RACE warnings.
+ mutex sync.Mutex
+}
+
+var _ Interface = &FakeMounter{}
+
+// Values for FakeAction.Action
+const FakeActionMount = "mount"
+const FakeActionUnmount = "unmount"
+
+// FakeAction objects are logged every time a fake mount or unmount is called.
+type FakeAction struct {
+ Action string // "mount" or "unmount"
+ Target string // applies to both mount and unmount actions
+ Source string // applies only to "mount" actions
+ FSType string // applies only to "mount" actions
+}
+
+func (f *FakeMounter) ResetLog() {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ f.Log = []FakeAction{}
+}
+
+func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // find 'bind' option
+ for _, option := range options {
+ if option == "bind" {
+ // This is a bind-mount. In order to mimic linux behaviour, we must
+ // use the original device of the bind-mount as the real source.
+ // E.g. when mounted /dev/sda like this:
+ // $ mount /dev/sda /mnt/test
+ // $ mount -o bind /mnt/test /mnt/bound
+ // then /proc/mount contains:
+ // /dev/sda /mnt/test
+ // /dev/sda /mnt/bound
+ // (and not /mnt/test /mnt/bound)
+ // I.e. we must use /dev/sda as source instead of /mnt/test in the
+ // bind mount.
+ for _, mnt := range f.MountPoints {
+ if source == mnt.Path {
+ source = mnt.Device
+ break
+ }
+ }
+ break
+ }
+ }
+
+ // If target is a symlink, get its absolute path
+ absTarget, err := filepath.EvalSymlinks(target)
+ if err != nil {
+ absTarget = target
+ }
+
+ f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype})
+ glog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
+ f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
+ return nil
+}
+
+func (f *FakeMounter) Unmount(target string) error {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // If target is a symlink, get its absolute path
+ absTarget, err := filepath.EvalSymlinks(target)
+ if err != nil {
+ absTarget = target
+ }
+
+ newMountpoints := []MountPoint{}
+ for _, mp := range f.MountPoints {
+ if mp.Path == absTarget {
+ glog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
+ // Don't copy it to newMountpoints
+ continue
+ }
+ newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
+ }
+ f.MountPoints = newMountpoints
+ f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: absTarget})
+ return nil
+}
+
+func (f *FakeMounter) List() ([]MountPoint, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ return f.MountPoints, nil
+}
+
+func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (f *FakeMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(f, dir)
+}
+
+func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // If file is a symlink, get its absolute path
+ absFile, err := filepath.EvalSymlinks(file)
+ if err != nil {
+ absFile = file
+ }
+
+ for _, mp := range f.MountPoints {
+ if mp.Path == absFile {
+ glog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
+ return false, nil
+ }
+ }
+ glog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
+ return true, nil
+}
+
+func (f *FakeMounter) DeviceOpened(pathname string) (bool, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ for _, mp := range f.MountPoints {
+ if mp.Device == pathname {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(f, mountPath, pluginDir)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
new file mode 100644
index 000000000..0c458d64b
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
@@ -0,0 +1,245 @@
+/*
+Copyright 2014 The Kubernetes 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.
+*/
+
+// TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have
+// an alternate platform, we will need to abstract further.
+package mount
+
+import (
+ "fmt"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/kubernetes/pkg/util/exec"
+)
+
+const (
+ // Default mount command if mounter path is not specified
+ defaultMountCommand = "mount"
+ MountsInGlobalPDPath = "mounts"
+)
+
+type Interface interface {
+ // Mount mounts source to target as fstype with given options.
+ Mount(source string, target string, fstype string, options []string) error
+ // Unmount unmounts given target.
+ Unmount(target string) error
+ // List returns a list of all mounted filesystems. This can be large.
+ // On some platforms, reading mounts is not guaranteed consistent (i.e.
+ // it could change between chunked reads). This is guaranteed to be
+ // consistent.
+ List() ([]MountPoint, error)
+ // IsMountPointMatch determines if the mountpoint matches the dir
+ IsMountPointMatch(mp MountPoint, dir string) bool
+ // IsNotMountPoint determines if a directory is a mountpoint.
+ // It should return ErrNotExist when the directory does not exist.
+ // IsNotMountPoint is more expensive than IsLikelyNotMountPoint.
+ // IsNotMountPoint detects bind mounts in linux.
+ // IsNotMountPoint enumerates all the mountpoints using List() and
+ // the list of mountpoints may be large, then it uses
+ // IsMountPointMatch to evaluate whether the directory is a mountpoint
+ IsNotMountPoint(file string) (bool, error)
+ // IsLikelyNotMountPoint uses heuristics to determine if a directory
+ // is a mountpoint.
+ // It should return ErrNotExist when the directory does not exist.
+ // IsLikelyNotMountPoint does NOT properly detect all mountpoint types
+ // most notably linux bind mounts.
+ IsLikelyNotMountPoint(file string) (bool, error)
+ // DeviceOpened determines if the device is in use elsewhere
+ // on the system, i.e. still mounted.
+ DeviceOpened(pathname string) (bool, error)
+ // PathIsDevice determines if a path is a device.
+ PathIsDevice(pathname string) (bool, error)
+ // GetDeviceNameFromMount finds the device name by checking the mount path
+ // to get the global mount path which matches its plugin directory
+ GetDeviceNameFromMount(mountPath, pluginDir string) (string, error)
+}
+
+// Compile-time check to ensure all Mounter implementations satisfy
+// the mount interface
+var _ Interface = &Mounter{}
+
+// This represents a single line in /proc/mounts or /etc/fstab.
+type MountPoint struct {
+ Device string
+ Path string
+ Type string
+ Opts []string
+ Freq int
+ Pass int
+}
+
+// SafeFormatAndMount probes a device to see if it is formatted.
+// Namely it checks to see if a file system is present. If so it
+// mounts it otherwise the device is formatted first then mounted.
+type SafeFormatAndMount struct {
+ Interface
+ Runner exec.Interface
+}
+
+// FormatAndMount formats the given disk, if needed, and mounts it.
+// That is if the disk is not formatted and it is not being mounted as
+// read-only it will format it first then mount it. Otherwise, if the
+// disk is already formatted or it is being mounted as read-only, it
+// will be mounted without formatting.
+func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
+ // Don't attempt to format if mounting as readonly. Go straight to mounting.
+ for _, option := range options {
+ if option == "ro" {
+ return mounter.Interface.Mount(source, target, fstype, options)
+ }
+ }
+ return mounter.formatAndMount(source, target, fstype, options)
+}
+
+// New returns a mount.Interface for the current system.
+// It provides options to override the default mounter behavior.
+// mounterPath allows using an alternative to `/bin/mount` for mounting.
+func New(mounterPath string) Interface {
+ return &Mounter{
+ mounterPath: mounterPath,
+ }
+}
+
+// GetMountRefs finds all other references to the device referenced
+// by mountPath; returns a list of paths.
+func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+ mps, err := mounter.List()
+ if err != nil {
+ return nil, err
+ }
+
+ // Find the device name.
+ deviceName := ""
+ // If mountPath is symlink, need get its target path.
+ slTarget, err := filepath.EvalSymlinks(mountPath)
+ if err != nil {
+ slTarget = mountPath
+ }
+ for i := range mps {
+ if mps[i].Path == slTarget {
+ deviceName = mps[i].Device
+ break
+ }
+ }
+
+ // Find all references to the device.
+ var refs []string
+ if deviceName == "" {
+ glog.Warningf("could not determine device for path: %q", mountPath)
+ } else {
+ for i := range mps {
+ if mps[i].Device == deviceName && mps[i].Path != slTarget {
+ refs = append(refs, mps[i].Path)
+ }
+ }
+ }
+ return refs, nil
+}
+
+// GetDeviceNameFromMount: given a mnt point, find the device from /proc/mounts
+// returns the device name, reference count, and error code
+func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) {
+ mps, err := mounter.List()
+ if err != nil {
+ return "", 0, err
+ }
+
+ // Find the device name.
+ // FIXME if multiple devices mounted on the same mount path, only the first one is returned
+ device := ""
+ // If mountPath is symlink, need get its target path.
+ slTarget, err := filepath.EvalSymlinks(mountPath)
+ if err != nil {
+ slTarget = mountPath
+ }
+ for i := range mps {
+ if mps[i].Path == slTarget {
+ device = mps[i].Device
+ break
+ }
+ }
+
+ // Find all references to the device.
+ refCount := 0
+ for i := range mps {
+ if mps[i].Device == device {
+ refCount++
+ }
+ }
+ return device, refCount, nil
+}
+
+// getDeviceNameFromMount find the device name from /proc/mounts in which
+// the mount path reference should match the given plugin directory. In case no mount path reference
+// matches, returns the volume name taken from its given mountPath
+func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
+ refs, err := GetMountRefs(mounter, mountPath)
+ if err != nil {
+ glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
+ return "", err
+ }
+ if len(refs) == 0 {
+ glog.V(4).Infof("Directory %s is not mounted", mountPath)
+ return "", fmt.Errorf("directory %s is not mounted", mountPath)
+ }
+ basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
+ for _, ref := range refs {
+ if strings.HasPrefix(ref, basemountPath) {
+ volumeID, err := filepath.Rel(basemountPath, ref)
+ if err != nil {
+ glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
+ return "", err
+ }
+ return volumeID, nil
+ }
+ }
+
+ return path.Base(mountPath), nil
+}
+
+// IsNotMountPoint determines if a directory is a mountpoint.
+// It should return ErrNotExist when the directory does not exist.
+// This method uses the List() of all mountpoints
+// It is more extensive than IsLikelyNotMountPoint
+// and it detects bind mounts in linux
+func IsNotMountPoint(mounter Interface, file string) (bool, error) {
+ // IsLikelyNotMountPoint provides a quick check
+ // to determine whether file IS A mountpoint
+ notMnt, notMntErr := mounter.IsLikelyNotMountPoint(file)
+ if notMntErr != nil {
+ return notMnt, notMntErr
+ }
+ // identified as mountpoint, so return this fact
+ if notMnt == false {
+ return notMnt, nil
+ }
+ // check all mountpoints since IsLikelyNotMountPoint
+ // is not reliable for some mountpoint types
+ mountPoints, mountPointsErr := mounter.List()
+ if mountPointsErr != nil {
+ return notMnt, mountPointsErr
+ }
+ for _, mp := range mountPoints {
+ if mounter.IsMountPointMatch(mp, file) {
+ notMnt = false
+ break
+ }
+ }
+ return notMnt, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
new file mode 100644
index 000000000..4c141ad5b
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
@@ -0,0 +1,432 @@
+// +build linux
+
+/*
+Copyright 2014 The Kubernetes 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 mount
+
+import (
+ "bufio"
+ "fmt"
+ "hash/fnv"
+ "io"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/util/sets"
+ utilexec "k8s.io/kubernetes/pkg/util/exec"
+)
+
+const (
+ // How many times to retry for a consistent read of /proc/mounts.
+ maxListTries = 3
+ // Number of fields per line in /proc/mounts as per the fstab man page.
+ expectedNumFieldsPerLine = 6
+ // Location of the mount file to use
+ procMountsPath = "/proc/mounts"
+)
+
+const (
+ // 'fsck' found errors and corrected them
+ fsckErrorsCorrected = 1
+ // 'fsck' found errors but exited without correcting them
+ fsckErrorsUncorrected = 4
+)
+
+// Mounter provides the default implementation of mount.Interface
+// for the linux platform. This implementation assumes that the
+// kubelet is running in the host's root mount namespace.
+type Mounter struct {
+ mounterPath string
+}
+
+// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
+// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
+// type, where kernel handles fs type for you. The mount 'options' is a list of options,
+// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
+// required, call Mount with an empty string list or nil.
+func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
+ // Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
+ // All Linux distros are expected to be shipped with a mount utility that an support bind mounts.
+ mounterPath := ""
+ bind, bindRemountOpts := isBind(options)
+ if bind {
+ err := doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"})
+ if err != nil {
+ return err
+ }
+ return doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
+ }
+ // The list of filesystems that require containerized mounter on GCI image cluster
+ fsTypesNeedMounter := sets.NewString("nfs", "glusterfs", "ceph", "cifs")
+ if fsTypesNeedMounter.Has(fstype) {
+ mounterPath = mounter.mounterPath
+ }
+ return doMount(mounterPath, defaultMountCommand, source, target, fstype, options)
+}
+
+// isBind detects whether a bind mount is being requested and makes the remount options to
+// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
+// The list equals:
+// options - 'bind' + 'remount' (no duplicate)
+func isBind(options []string) (bool, []string) {
+ bindRemountOpts := []string{"remount"}
+ bind := false
+
+ if len(options) != 0 {
+ for _, option := range options {
+ switch option {
+ case "bind":
+ bind = true
+ break
+ case "remount":
+ break
+ default:
+ bindRemountOpts = append(bindRemountOpts, option)
+ }
+ }
+ }
+
+ return bind, bindRemountOpts
+}
+
+// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
+func doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error {
+ mountArgs := makeMountArgs(source, target, fstype, options)
+ if len(mounterPath) > 0 {
+ mountArgs = append([]string{mountCmd}, mountArgs...)
+ mountCmd = mounterPath
+ }
+
+ glog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
+ command := exec.Command(mountCmd, mountArgs...)
+ output, err := command.CombinedOutput()
+ if err != nil {
+ glog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n", err, mountCmd, source, target, fstype, options, string(output))
+ return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n",
+ err, mountCmd, source, target, fstype, options, string(output))
+ }
+ return err
+}
+
+// makeMountArgs makes the arguments to the mount(8) command.
+func makeMountArgs(source, target, fstype string, options []string) []string {
+ // Build mount command as follows:
+ // mount [-t $fstype] [-o $options] [$source] $target
+ mountArgs := []string{}
+ if len(fstype) > 0 {
+ mountArgs = append(mountArgs, "-t", fstype)
+ }
+ if len(options) > 0 {
+ mountArgs = append(mountArgs, "-o", strings.Join(options, ","))
+ }
+ if len(source) > 0 {
+ mountArgs = append(mountArgs, source)
+ }
+ mountArgs = append(mountArgs, target)
+
+ return mountArgs
+}
+
+// Unmount unmounts the target.
+func (mounter *Mounter) Unmount(target string) error {
+ glog.V(4).Infof("Unmounting %s", target)
+ command := exec.Command("umount", target)
+ output, err := command.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output))
+ }
+ return nil
+}
+
+// List returns a list of all mounted filesystems.
+func (*Mounter) List() ([]MountPoint, error) {
+ return listProcMounts(procMountsPath)
+}
+
+func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
+ return ((mp.Path == dir) || (mp.Path == deletedDir))
+}
+
+func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, dir)
+}
+
+// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
+// It is fast but not necessarily ALWAYS correct. If the path is in fact
+// a bind mount from one part of a mount to another it will not be detected.
+// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
+// will return true. When in fact /tmp/b is a mount point. If this situation
+// if of interest to you, don't use this function...
+func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ stat, err := os.Stat(file)
+ if err != nil {
+ return true, err
+ }
+ rootStat, err := os.Lstat(file + "/..")
+ if err != nil {
+ return true, err
+ }
+ // If the directory has a different device as parent, then it is a mountpoint.
+ if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
+// If pathname is not a device, log and return false with nil error.
+// If open returns errno EBUSY, return true with nil error.
+// If open returns nil, return false with nil error.
+// Otherwise, return false with error
+func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
+ return exclusiveOpenFailsOnDevice(pathname)
+}
+
+// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
+// to a device.
+func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
+ return pathIsDevice(pathname)
+}
+
+func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
+ isDevice, err := pathIsDevice(pathname)
+ if err != nil {
+ return false, fmt.Errorf(
+ "PathIsDevice failed for path %q: %v",
+ pathname,
+ err)
+ }
+ if !isDevice {
+ glog.Errorf("Path %q is not refering to a device.", pathname)
+ return false, nil
+ }
+ fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
+ // If the device is in use, open will return an invalid fd.
+ // When this happens, it is expected that Close will fail and throw an error.
+ defer syscall.Close(fd)
+ if errno == nil {
+ // device not in use
+ return false, nil
+ } else if errno == syscall.EBUSY {
+ // device is in use
+ return true, nil
+ }
+ // error during call to Open
+ return false, errno
+}
+
+func pathIsDevice(pathname string) (bool, error) {
+ finfo, err := os.Stat(pathname)
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ // err in call to os.Stat
+ if err != nil {
+ return false, err
+ }
+ // path refers to a device
+ if finfo.Mode()&os.ModeDevice != 0 {
+ return true, nil
+ }
+ // path does not refer to device
+ return false, nil
+}
+
+//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
+func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(mounter, mountPath, pluginDir)
+}
+
+func listProcMounts(mountFilePath string) ([]MountPoint, error) {
+ hash1, err := readProcMounts(mountFilePath, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < maxListTries; i++ {
+ mps := []MountPoint{}
+ hash2, err := readProcMounts(mountFilePath, &mps)
+ if err != nil {
+ return nil, err
+ }
+ if hash1 == hash2 {
+ // Success
+ return mps, nil
+ }
+ hash1 = hash2
+ }
+ return nil, fmt.Errorf("failed to get a consistent snapshot of %v after %d tries", mountFilePath, maxListTries)
+}
+
+// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash
+// of the contents. If the out argument is not nil, this fills it with MountPoint structs.
+func readProcMounts(mountFilePath string, out *[]MountPoint) (uint32, error) {
+ file, err := os.Open(mountFilePath)
+ if err != nil {
+ return 0, err
+ }
+ defer file.Close()
+ return readProcMountsFrom(file, out)
+}
+
+func readProcMountsFrom(file io.Reader, out *[]MountPoint) (uint32, error) {
+ hash := fnv.New32a()
+ scanner := bufio.NewReader(file)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ fields := strings.Fields(line)
+ if len(fields) != expectedNumFieldsPerLine {
+ return 0, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
+ }
+
+ fmt.Fprintf(hash, "%s", line)
+
+ if out != nil {
+ mp := MountPoint{
+ Device: fields[0],
+ Path: fields[1],
+ Type: fields[2],
+ Opts: strings.Split(fields[3], ","),
+ }
+
+ freq, err := strconv.Atoi(fields[4])
+ if err != nil {
+ return 0, err
+ }
+ mp.Freq = freq
+
+ pass, err := strconv.Atoi(fields[5])
+ if err != nil {
+ return 0, err
+ }
+ mp.Pass = pass
+
+ *out = append(*out, mp)
+ }
+ }
+ return hash.Sum32(), nil
+}
+
+// formatAndMount uses unix utils to format and mount the given disk
+func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+ options = append(options, "defaults")
+
+ // Run fsck on the disk to fix repairable issues
+ glog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
+ args := []string{"-a", source}
+ cmd := mounter.Runner.Command("fsck", args...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ ee, isExitError := err.(utilexec.ExitError)
+ switch {
+ case err == utilexec.ErrExecutableNotFound:
+ glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
+ case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
+ glog.Infof("Device %s has errors which were corrected by fsck.", source)
+ case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
+ return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out))
+ case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
+ glog.Infof("`fsck` error %s", string(out))
+ }
+ }
+
+ // Try to mount the disk
+ glog.V(4).Infof("Attempting to mount disk: %s %s %s", fstype, source, target)
+ mountErr := mounter.Interface.Mount(source, target, fstype, options)
+ if mountErr != nil {
+ // Mount failed. This indicates either that the disk is unformatted or
+ // it contains an unexpected filesystem.
+ existingFormat, err := mounter.getDiskFormat(source)
+ if err != nil {
+ return err
+ }
+ if existingFormat == "" {
+ // Disk is unformatted so format it.
+ args = []string{source}
+ // Use 'ext4' as the default
+ if len(fstype) == 0 {
+ fstype = "ext4"
+ }
+
+ if fstype == "ext4" || fstype == "ext3" {
+ args = []string{"-F", source}
+ }
+ glog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
+ cmd := mounter.Runner.Command("mkfs."+fstype, args...)
+ _, err := cmd.CombinedOutput()
+ if err == nil {
+ // the disk has been formatted successfully try to mount it again.
+ glog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
+ return mounter.Interface.Mount(source, target, fstype, options)
+ }
+ glog.Errorf("format of disk %q failed: type:(%q) target:(%q) options:(%q)error:(%v)", source, fstype, target, options, err)
+ return err
+ } else {
+ // Disk is already formatted and failed to mount
+ if len(fstype) == 0 || fstype == existingFormat {
+ // This is mount error
+ return mountErr
+ } else {
+ // Block device is formatted with unexpected filesystem, let the user know
+ return fmt.Errorf("failed to mount the volume as %q, it already contains %s. Mount error: %v", fstype, existingFormat, mountErr)
+ }
+ }
+ }
+ return mountErr
+}
+
+// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated
+func (mounter *SafeFormatAndMount) getDiskFormat(disk string) (string, error) {
+ args := []string{"-n", "-o", "FSTYPE", disk}
+ cmd := mounter.Runner.Command("lsblk", args...)
+ glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
+ dataOut, err := cmd.CombinedOutput()
+ output := string(dataOut)
+ glog.V(4).Infof("Output: %q", output)
+
+ if err != nil {
+ glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
+ return "", err
+ }
+
+ // Split lsblk output into lines. Unformatted devices should contain only
+ // "\n". Beware of "\n\n", that's a device with one empty partition.
+ output = strings.TrimSuffix(output, "\n") // Avoid last empty line
+ lines := strings.Split(output, "\n")
+ if lines[0] != "" {
+ // The device is formatted
+ return lines[0], nil
+ }
+
+ if len(lines) == 1 {
+ // The device is unformatted and has no dependent devices
+ return "", nil
+ }
+
+ // The device has dependent devices, most probably partitions (LVM, LUKS
+ // and MD RAID are reported as FSTYPE and caught above).
+ return "unknown data, probably partitions", nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
new file mode 100644
index 000000000..632ad0606
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
@@ -0,0 +1,67 @@
+// +build !linux
+
+/*
+Copyright 2014 The Kubernetes 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 mount
+
+type Mounter struct {
+ mounterPath string
+}
+
+func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (mounter *Mounter) Unmount(target string) error {
+ return nil
+}
+
+func (mounter *Mounter) List() ([]MountPoint, error) {
+ return []MountPoint{}, nil
+}
+
+func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, dir)
+}
+
+func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return "", nil
+}
+
+func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
+ return false, nil
+}
+
+func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
+ return true, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
new file mode 100644
index 000000000..4af8ef0d8
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
@@ -0,0 +1,241 @@
+// +build linux
+
+/*
+Copyright 2014 The Kubernetes 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 mount
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/kubernetes/pkg/util/exec"
+)
+
+// NsenterMounter is part of experimental support for running the kubelet
+// in a container. Currently, all docker containers receive their own mount
+// namespaces. NsenterMounter works by executing nsenter to run commands in
+// the host's mount namespace.
+//
+// NsenterMounter requires:
+//
+// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
+// of the bind-mount of the kubelet root directory in the container.
+// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
+// performed in the host's mount namespace do not propagate out to the
+// bind-mount in this docker version.
+// 2. The host's root filesystem must be available at /rootfs
+// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
+// filesystem.
+// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
+// the present, this effectively means that the kubelet is running in a
+// privileged container.
+// 5. The volume path used by the Kubelet must be the same inside and outside
+// the container and be writable by the container (to initialize volume)
+// contents. TODO: remove this requirement.
+// 6. The host image must have mount, findmnt, and umount binaries in /bin,
+// /usr/sbin, or /usr/bin
+//
+// For more information about mount propagation modes, see:
+// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+type NsenterMounter struct {
+ // a map of commands to their paths on the host filesystem
+ paths map[string]string
+}
+
+func NewNsenterMounter() *NsenterMounter {
+ m := &NsenterMounter{
+ paths: map[string]string{
+ "mount": "",
+ "findmnt": "",
+ "umount": "",
+ },
+ }
+ // search for the mount command in other locations besides /usr/bin
+ for binary := range m.paths {
+ // default to root
+ m.paths[binary] = filepath.Join("/", binary)
+ for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
+ binPath := filepath.Join(path, binary)
+ if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
+ continue
+ }
+ m.paths[binary] = binPath
+ break
+ }
+ // TODO: error, so that the kubelet can stop if the mounts don't exist
+ }
+ return m
+}
+
+// NsenterMounter implements mount.Interface
+var _ = Interface(&NsenterMounter{})
+
+const (
+ hostRootFsPath = "/rootfs"
+ hostProcMountsPath = "/rootfs/proc/1/mounts"
+ nsenterPath = "nsenter"
+)
+
+// Mount runs mount(8) in the host's root mount namespace. Aside from this
+// aspect, Mount has the same semantics as the mounter returned by mount.New()
+func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
+ bind, bindRemountOpts := isBind(options)
+
+ if bind {
+ err := n.doNsenterMount(source, target, fstype, []string{"bind"})
+ if err != nil {
+ return err
+ }
+ return n.doNsenterMount(source, target, fstype, bindRemountOpts)
+ }
+
+ return n.doNsenterMount(source, target, fstype, options)
+}
+
+// doNsenterMount nsenters the host's mount namespace and performs the
+// requested mount.
+func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
+ glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options)
+ args := n.makeNsenterArgs(source, target, fstype, options)
+
+ glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
+ exec := exec.New()
+ outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if len(outputBytes) != 0 {
+ glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
+ }
+
+ return err
+}
+
+// makeNsenterArgs makes a list of argument to nsenter in order to do the
+// requested mount.
+func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
+ nsenterArgs := []string{
+ "--mount=/rootfs/proc/1/ns/mnt",
+ "--",
+ n.absHostPath("mount"),
+ }
+
+ args := makeMountArgs(source, target, fstype, options)
+
+ return append(nsenterArgs, args...)
+}
+
+// Unmount runs umount(8) in the host's mount namespace.
+func (n *NsenterMounter) Unmount(target string) error {
+ args := []string{
+ "--mount=/rootfs/proc/1/ns/mnt",
+ "--",
+ n.absHostPath("umount"),
+ target,
+ }
+
+ glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
+ exec := exec.New()
+ outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if len(outputBytes) != 0 {
+ glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
+ }
+
+ return err
+}
+
+// List returns a list of all mounted filesystems in the host's mount namespace.
+func (*NsenterMounter) List() ([]MountPoint, error) {
+ return listProcMounts(hostProcMountsPath)
+}
+
+func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(m, dir)
+}
+
+func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
+ return ((mp.Path == dir) || (mp.Path == deletedDir))
+}
+
+// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
+// in the host's root mount namespace.
+func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ file, err := filepath.Abs(file)
+ if err != nil {
+ return true, err
+ }
+
+ // Check the directory exists
+ if _, err = os.Stat(file); os.IsNotExist(err) {
+ glog.V(5).Infof("findmnt: directory %s does not exist", file)
+ return true, err
+ }
+ // Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
+ // the first of multiple possible mountpoints using --first-only.
+ // Also add fstype output to make sure that the output of target file will give the full path
+ // TODO: Need more refactoring for this function. Track the solution with issue #26996
+ args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
+ glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)
+
+ exec := exec.New()
+ out, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if err != nil {
+ glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
+ // Different operating systems behave differently for paths which are not mount points.
+ // On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
+ // It's safer to assume that it's not a mount point.
+ return true, nil
+ }
+ mountTarget := strings.Split(string(out), " ")[0]
+ mountTarget = strings.TrimSuffix(mountTarget, "\n")
+ glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)
+
+ if mountTarget == file {
+ glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
+ return false, nil
+ }
+ glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
+ return true, nil
+}
+
+// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
+// Returns true if open returns errno EBUSY, and false if errno is nil.
+// Returns an error if errno is any error other than EBUSY.
+// Returns with error if pathname is not a device.
+func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
+ return exclusiveOpenFailsOnDevice(pathname)
+}
+
+// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
+// to a device.
+func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
+ return pathIsDevice(pathname)
+}
+
+//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
+func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(n, mountPath, pluginDir)
+}
+
+func (n *NsenterMounter) absHostPath(command string) string {
+ path, ok := n.paths[command]
+ if !ok {
+ return command
+ }
+ return path
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
new file mode 100644
index 000000000..e955e1b78
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
@@ -0,0 +1,63 @@
+// +build !linux
+
+/*
+Copyright 2014 The Kubernetes 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 mount
+
+type NsenterMounter struct{}
+
+func NewNsenterMounter() *NsenterMounter {
+ return &NsenterMounter{}
+}
+
+var _ = Interface(&NsenterMounter{})
+
+func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (*NsenterMounter) Unmount(target string) error {
+ return nil
+}
+
+func (*NsenterMounter) List() ([]MountPoint, error) {
+ return []MountPoint{}, nil
+}
+
+func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(m, dir)
+}
+
+func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return true, nil
+}
+
+func (*NsenterMounter) DeviceOpened(pathname string) (bool, error) {
+ return false, nil
+}
+
+func (*NsenterMounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return "", nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/net/sets/doc.go b/vendor/k8s.io/kubernetes/pkg/util/net/sets/doc.go
new file mode 100644
index 000000000..8414f74ac
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/net/sets/doc.go
@@ -0,0 +1,28 @@
+/*
+Copyright 2016 The Kubernetes 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.
+*/
+
+// This package contains hand-coded set implementations that should be similar
+// to the autogenerated ones in pkg/util/sets.
+// We can't simply use net.IPNet as a map-key in Go (because it contains a
+// []byte).
+// We could use the same workaround we use here (a string representation as the
+// key) to autogenerate sets. If we do that, or decide on an alternate
+// approach, we should replace the implementations in this package with the
+// autogenerated versions.
+// It is expected that callers will alias this import as "netsets" i.e. import
+// netsets "k8s.io/kubernetes/pkg/util/net/sets"
+
+package sets
diff --git a/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go b/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go
new file mode 100644
index 000000000..5b6fe933f
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2016 The Kubernetes 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 sets
+
+import (
+ "net"
+ "strings"
+)
+
+type IPNet map[string]*net.IPNet
+
+func ParseIPNets(specs ...string) (IPNet, error) {
+ ipnetset := make(IPNet)
+ for _, spec := range specs {
+ spec = strings.TrimSpace(spec)
+ _, ipnet, err := net.ParseCIDR(spec)
+ if err != nil {
+ return nil, err
+ }
+ k := ipnet.String() // In case of normalization
+ ipnetset[k] = ipnet
+ }
+ return ipnetset, nil
+}
+
+// Insert adds items to the set.
+func (s IPNet) Insert(items ...*net.IPNet) {
+ for _, item := range items {
+ s[item.String()] = item
+ }
+}
+
+// Delete removes all items from the set.
+func (s IPNet) Delete(items ...*net.IPNet) {
+ for _, item := range items {
+ delete(s, item.String())
+ }
+}
+
+// Has returns true if and only if item is contained in the set.
+func (s IPNet) Has(item *net.IPNet) bool {
+ _, contained := s[item.String()]
+ return contained
+}
+
+// HasAll returns true if and only if all items are contained in the set.
+func (s IPNet) HasAll(items ...*net.IPNet) bool {
+ for _, item := range items {
+ if !s.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// Difference returns a set of objects that are not in s2
+// For example:
+// s1 = {a1, a2, a3}
+// s2 = {a1, a2, a4, a5}
+// s1.Difference(s2) = {a3}
+// s2.Difference(s1) = {a4, a5}
+func (s IPNet) Difference(s2 IPNet) IPNet {
+ result := make(IPNet)
+ for k, i := range s {
+ _, found := s2[k]
+ if found {
+ continue
+ }
+ result[k] = i
+ }
+ return result
+}
+
+// StringSlice returns a []string with the String representation of each element in the set.
+// Order is undefined.
+func (s IPNet) StringSlice() []string {
+ a := make([]string, 0, len(s))
+ for k := range s {
+ a = append(a, k)
+ }
+ return a
+}
+
+// IsSuperset returns true if and only if s1 is a superset of s2.
+func (s1 IPNet) IsSuperset(s2 IPNet) bool {
+ for k := range s2 {
+ _, found := s1[k]
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if and only if s1 is equal (as a set) to s2.
+// Two sets are equal if their membership is identical.
+// (In practice, this means same elements, order doesn't matter)
+func (s1 IPNet) Equal(s2 IPNet) bool {
+ return len(s1) == len(s2) && s1.IsSuperset(s2)
+}
+
+// Len returns the size of the set.
+func (s IPNet) Len() int {
+ return len(s)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go b/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
new file mode 100644
index 000000000..4e70cc682
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/parsers/parsers.go
@@ -0,0 +1,54 @@
+/*
+Copyright 2015 The Kubernetes 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 parsers
+
+import (
+ "fmt"
+
+ dockerref "github.com/docker/distribution/reference"
+)
+
+const (
+ DefaultImageTag = "latest"
+)
+
+// ParseImageName parses a docker image string into three parts: repo, tag and digest.
+// If both tag and digest are empty, a default image tag will be returned.
+func ParseImageName(image string) (string, string, string, error) {
+ named, err := dockerref.ParseNamed(image)
+ if err != nil {
+ return "", "", "", fmt.Errorf("couldn't parse image name: %v", err)
+ }
+
+ repoToPull := named.Name()
+ var tag, digest string
+
+ tagged, ok := named.(dockerref.Tagged)
+ if ok {
+ tag = tagged.Tag()
+ }
+
+ digested, ok := named.(dockerref.Digested)
+ if ok {
+ digest = digested.Digest().String()
+ }
+ // If no tag was specified, use the default "latest".
+ if len(tag) == 0 && len(digest) == 0 {
+ tag = DefaultImageTag
+ }
+ return repoToPull, tag, digest, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go b/vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go
new file mode 100644
index 000000000..5c01dd88e
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/sysctl/sysctl.go
@@ -0,0 +1,78 @@
+/*
+Copyright 2015 The Kubernetes 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 sysctl
+
+import (
+ "io/ioutil"
+ "path"
+ "strconv"
+ "strings"
+)
+
+const (
+ sysctlBase = "/proc/sys"
+ VmOvercommitMemory = "vm/overcommit_memory"
+ VmPanicOnOOM = "vm/panic_on_oom"
+ KernelPanic = "kernel/panic"
+ KernelPanicOnOops = "kernel/panic_on_oops"
+ RootMaxKeys = "kernel/keys/root_maxkeys"
+ RootMaxBytes = "kernel/keys/root_maxbytes"
+
+ VmOvercommitMemoryAlways = 1 // kernel performs no memory over-commit handling
+ VmPanicOnOOMInvokeOOMKiller = 0 // kernel calls the oom_killer function when OOM occurs
+
+ KernelPanicOnOopsAlways = 1 // kernel panics on kernel oops
+ KernelPanicRebootTimeout = 10 // seconds after a panic for the kernel to reboot
+
+ RootMaxKeysSetting = 1000000 // Needed since docker creates a new key per container
+ RootMaxBytesSetting = RootMaxKeysSetting * 25 // allocate 25 bytes per key * number of MaxKeys
+)
+
+// An injectable interface for running sysctl commands.
+type Interface interface {
+ // GetSysctl returns the value for the specified sysctl setting
+ GetSysctl(sysctl string) (int, error)
+ // SetSysctl modifies the specified sysctl flag to the new value
+ SetSysctl(sysctl string, newVal int) error
+}
+
+// New returns a new Interface for accessing sysctl
+func New() Interface {
+ return &procSysctl{}
+}
+
+// procSysctl implements Interface by reading and writing files under /proc/sys
+type procSysctl struct {
+}
+
+// GetSysctl returns the value for the specified sysctl setting
+func (_ *procSysctl) GetSysctl(sysctl string) (int, error) {
+ data, err := ioutil.ReadFile(path.Join(sysctlBase, sysctl))
+ if err != nil {
+ return -1, err
+ }
+ val, err := strconv.Atoi(strings.Trim(string(data), " \n"))
+ if err != nil {
+ return -1, err
+ }
+ return val, nil
+}
+
+// SetSysctl modifies the specified sysctl flag to the new value
+func (_ *procSysctl) SetSysctl(sysctl string, newVal int) error {
+ return ioutil.WriteFile(path.Join(sysctlBase, sysctl), []byte(strconv.Itoa(newVal)), 0640)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/template.go b/vendor/k8s.io/kubernetes/pkg/util/template.go
new file mode 100644
index 000000000..d09d7dc86
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/template.go
@@ -0,0 +1,48 @@
+/*
+Copyright 2015 The Kubernetes 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 util
+
+import (
+ "bytes"
+ "go/doc"
+ "io"
+ "strings"
+ "text/template"
+)
+
+func wrap(indent string, s string) string {
+ var buf bytes.Buffer
+ doc.ToText(&buf, s, indent, indent+" ", 80-len(indent))
+ return buf.String()
+}
+
+// ExecuteTemplate executes templateText with data and output written to w.
+func ExecuteTemplate(w io.Writer, templateText string, data interface{}) error {
+ t := template.New("top")
+ t.Funcs(template.FuncMap{
+ "trim": strings.TrimSpace,
+ "wrap": wrap,
+ })
+ template.Must(t.Parse(templateText))
+ return t.Execute(w, data)
+}
+
+func ExecuteTemplateToString(templateText string, data interface{}) (string, error) {
+ b := bytes.Buffer{}
+ err := ExecuteTemplate(&b, templateText, data)
+ return b.String(), err
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/resize.go b/vendor/k8s.io/kubernetes/pkg/util/term/resize.go
new file mode 100644
index 000000000..7ca09a858
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/resize.go
@@ -0,0 +1,132 @@
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "fmt"
+
+ "github.com/docker/docker/pkg/term"
+ "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// GetSize returns the current size of the user's terminal. If it isn't a terminal,
+// nil is returned.
+func (t TTY) GetSize() *remotecommand.TerminalSize {
+ outFd, isTerminal := term.GetFdInfo(t.Out)
+ if !isTerminal {
+ return nil
+ }
+ return GetSize(outFd)
+}
+
+// GetSize returns the current size of the terminal associated with fd.
+func GetSize(fd uintptr) *remotecommand.TerminalSize {
+ winsize, err := term.GetWinsize(fd)
+ if err != nil {
+ runtime.HandleError(fmt.Errorf("unable to get terminal size: %v", err))
+ return nil
+ }
+
+ return &remotecommand.TerminalSize{Width: winsize.Width, Height: winsize.Height}
+}
+
+// MonitorSize monitors the terminal's size. It returns a TerminalSizeQueue primed with
+// initialSizes, or nil if there's no TTY present.
+func (t *TTY) MonitorSize(initialSizes ...*remotecommand.TerminalSize) remotecommand.TerminalSizeQueue {
+ outFd, isTerminal := term.GetFdInfo(t.Out)
+ if !isTerminal {
+ return nil
+ }
+
+ t.sizeQueue = &sizeQueue{
+ t: *t,
+ // make it buffered so we can send the initial terminal sizes without blocking, prior to starting
+ // the streaming below
+ resizeChan: make(chan remotecommand.TerminalSize, len(initialSizes)),
+ stopResizing: make(chan struct{}),
+ }
+
+ t.sizeQueue.monitorSize(outFd, initialSizes...)
+
+ return t.sizeQueue
+}
+
+// sizeQueue implements remotecommand.TerminalSizeQueue
+type sizeQueue struct {
+ t TTY
+ // resizeChan receives a Size each time the user's terminal is resized.
+ resizeChan chan remotecommand.TerminalSize
+ stopResizing chan struct{}
+}
+
+// make sure sizeQueue implements the resize.TerminalSizeQueue interface
+var _ remotecommand.TerminalSizeQueue = &sizeQueue{}
+
+// monitorSize primes resizeChan with initialSizes and then monitors for resize events. With each
+// new event, it sends the current terminal size to resizeChan.
+func (s *sizeQueue) monitorSize(outFd uintptr, initialSizes ...*remotecommand.TerminalSize) {
+ // send the initial sizes
+ for i := range initialSizes {
+ if initialSizes[i] != nil {
+ s.resizeChan <- *initialSizes[i]
+ }
+ }
+
+ resizeEvents := make(chan remotecommand.TerminalSize, 1)
+
+ monitorResizeEvents(outFd, resizeEvents, s.stopResizing)
+
+ // listen for resize events in the background
+ go func() {
+ defer runtime.HandleCrash()
+
+ for {
+ select {
+ case size, ok := <-resizeEvents:
+ if !ok {
+ return
+ }
+
+ select {
+ // try to send the size to resizeChan, but don't block
+ case s.resizeChan <- size:
+ // send successful
+ default:
+ // unable to send / no-op
+ }
+ case <-s.stopResizing:
+ return
+ }
+ }
+ }()
+}
+
+// Next returns the new terminal size after the terminal has been resized. It returns nil when
+// monitoring has been stopped.
+func (s *sizeQueue) Next() *remotecommand.TerminalSize {
+ size, ok := <-s.resizeChan
+ if !ok {
+ return nil
+ }
+ return &size
+}
+
+// stop stops the background goroutine that is monitoring for terminal resizes.
+func (s *sizeQueue) stop() {
+ close(s.stopResizing)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents.go b/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents.go
new file mode 100644
index 000000000..75e9690df
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents.go
@@ -0,0 +1,61 @@
+// +build !windows
+
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+
+ "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// monitorResizeEvents spawns a goroutine that waits for SIGWINCH signals (these indicate the
+// terminal has resized). After receiving a SIGWINCH, this gets the terminal size and tries to send
+// it to the resizeEvents channel. The goroutine stops when the stop channel is closed.
+func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
+ go func() {
+ defer runtime.HandleCrash()
+
+ winch := make(chan os.Signal, 1)
+ signal.Notify(winch, syscall.SIGWINCH)
+ defer signal.Stop(winch)
+
+ for {
+ select {
+ case <-winch:
+ size := GetSize(fd)
+ if size == nil {
+ return
+ }
+
+ // try to send size
+ select {
+ case resizeEvents <- *size:
+ // success
+ default:
+ // not sent
+ }
+ case <-stop:
+ return
+ }
+ }
+ }()
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents_windows.go b/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents_windows.go
new file mode 100644
index 000000000..adccf8734
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/resizeevents_windows.go
@@ -0,0 +1,62 @@
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "time"
+
+ "k8s.io/apimachinery/pkg/util/runtime"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// monitorResizeEvents spawns a goroutine that periodically gets the terminal size and tries to send
+// it to the resizeEvents channel if the size has changed. The goroutine stops when the stop channel
+// is closed.
+func monitorResizeEvents(fd uintptr, resizeEvents chan<- remotecommand.TerminalSize, stop chan struct{}) {
+ go func() {
+ defer runtime.HandleCrash()
+
+ size := GetSize(fd)
+ if size == nil {
+ return
+ }
+ lastSize := *size
+
+ for {
+ // see if we need to stop running
+ select {
+ case <-stop:
+ return
+ default:
+ }
+
+ size := GetSize(fd)
+ if size == nil {
+ return
+ }
+
+ if size.Height != lastSize.Height || size.Width != lastSize.Width {
+ lastSize.Height = size.Height
+ lastSize.Width = size.Width
+ resizeEvents <- *size
+ }
+
+ // sleep to avoid hot looping
+ time.Sleep(250 * time.Millisecond)
+ }
+ }()
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/setsize.go b/vendor/k8s.io/kubernetes/pkg/util/term/setsize.go
new file mode 100644
index 000000000..8cccd431a
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/setsize.go
@@ -0,0 +1,29 @@
+// +build !windows
+
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "github.com/docker/docker/pkg/term"
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+// SetSize sets the terminal size associated with fd.
+func SetSize(fd uintptr, size remotecommand.TerminalSize) error {
+ return term.SetWinsize(fd, &term.Winsize{Height: size.Height, Width: size.Width})
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/setsize_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/term/setsize_unsupported.go
new file mode 100644
index 000000000..82220217a
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/setsize_unsupported.go
@@ -0,0 +1,28 @@
+// +build windows
+
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "k8s.io/client-go/tools/remotecommand"
+)
+
+func SetSize(fd uintptr, size remotecommand.TerminalSize) error {
+ // NOP
+ return nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/term.go b/vendor/k8s.io/kubernetes/pkg/util/term/term.go
new file mode 100644
index 000000000..58baee831
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/term.go
@@ -0,0 +1,110 @@
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "io"
+ "os"
+
+ "github.com/docker/docker/pkg/term"
+
+ "k8s.io/kubernetes/pkg/util/interrupt"
+)
+
+// SafeFunc is a function to be invoked by TTY.
+type SafeFunc func() error
+
+// TTY helps invoke a function and preserve the state of the terminal, even if the process is
+// terminated during execution. It also provides support for terminal resizing for remote command
+// execution/attachment.
+type TTY struct {
+ // In is a reader representing stdin. It is a required field.
+ In io.Reader
+ // Out is a writer representing stdout. It must be set to support terminal resizing. It is an
+ // optional field.
+ Out io.Writer
+ // Raw is true if the terminal should be set raw.
+ Raw bool
+ // TryDev indicates the TTY should try to open /dev/tty if the provided input
+ // is not a file descriptor.
+ TryDev bool
+ // Parent is an optional interrupt handler provided to this function - if provided
+ // it will be invoked after the terminal state is restored. If it is not provided,
+ // a signal received during the TTY will result in os.Exit(0) being invoked.
+ Parent *interrupt.Handler
+
+ // sizeQueue is set after a call to MonitorSize() and is used to monitor SIGWINCH signals when the
+ // user's terminal resizes.
+ sizeQueue *sizeQueue
+}
+
+// IsTerminalIn returns true if t.In is a terminal. Does not check /dev/tty
+// even if TryDev is set.
+func (t TTY) IsTerminalIn() bool {
+ return IsTerminal(t.In)
+}
+
+// IsTerminalOut returns true if t.Out is a terminal. Does not check /dev/tty
+// even if TryDev is set.
+func (t TTY) IsTerminalOut() bool {
+ return IsTerminal(t.Out)
+}
+
+// IsTerminal returns whether the passed object is a terminal or not
+func IsTerminal(i interface{}) bool {
+ _, terminal := term.GetFdInfo(i)
+ return terminal
+}
+
+// Safe invokes the provided function and will attempt to ensure that when the
+// function returns (or a termination signal is sent) that the terminal state
+// is reset to the condition it was in prior to the function being invoked. If
+// t.Raw is true the terminal will be put into raw mode prior to calling the function.
+// If the input file descriptor is not a TTY and TryDev is true, the /dev/tty file
+// will be opened (if available).
+func (t TTY) Safe(fn SafeFunc) error {
+ inFd, isTerminal := term.GetFdInfo(t.In)
+
+ if !isTerminal && t.TryDev {
+ if f, err := os.Open("/dev/tty"); err == nil {
+ defer f.Close()
+ inFd = f.Fd()
+ isTerminal = term.IsTerminal(inFd)
+ }
+ }
+ if !isTerminal {
+ return fn()
+ }
+
+ var state *term.State
+ var err error
+ if t.Raw {
+ state, err = term.MakeRaw(inFd)
+ } else {
+ state, err = term.SaveState(inFd)
+ }
+ if err != nil {
+ return err
+ }
+ return interrupt.Chain(t.Parent, func() {
+ if t.sizeQueue != nil {
+ t.sizeQueue.stop()
+ }
+
+ term.RestoreTerminal(inFd, state)
+ }).Run(fn)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/term/term_writer.go b/vendor/k8s.io/kubernetes/pkg/util/term/term_writer.go
new file mode 100644
index 000000000..2d72d1e45
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/term/term_writer.go
@@ -0,0 +1,124 @@
+/*
+Copyright 2016 The Kubernetes 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 term
+
+import (
+ "io"
+ "os"
+
+ "github.com/docker/docker/pkg/term"
+ wordwrap "github.com/mitchellh/go-wordwrap"
+)
+
+type wordWrapWriter struct {
+ limit uint
+ writer io.Writer
+}
+
+// NewResponsiveWriter creates a Writer that detects the column width of the
+// terminal we are in, and adjusts every line width to fit and use recommended
+// terminal sizes for better readability. Does proper word wrapping automatically.
+// if terminal width >= 120 columns use 120 columns
+// if terminal width >= 100 columns use 100 columns
+// if terminal width >= 80 columns use 80 columns
+// In case we're not in a terminal or if it's smaller than 80 columns width,
+// doesn't do any wrapping.
+func NewResponsiveWriter(w io.Writer) io.Writer {
+ file, ok := w.(*os.File)
+ if !ok {
+ return w
+ }
+ fd := file.Fd()
+ if !term.IsTerminal(fd) {
+ return w
+ }
+
+ terminalSize := GetSize(fd)
+ if terminalSize == nil {
+ return w
+ }
+
+ var limit uint
+ switch {
+ case terminalSize.Width >= 120:
+ limit = 120
+ case terminalSize.Width >= 100:
+ limit = 100
+ case terminalSize.Width >= 80:
+ limit = 80
+ }
+
+ return NewWordWrapWriter(w, limit)
+}
+
+// NewWordWrapWriter is a Writer that supports a limit of characters on every line
+// and does auto word wrapping that respects that limit.
+func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
+ return &wordWrapWriter{
+ limit: limit,
+ writer: w,
+ }
+}
+
+func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
+ if w.limit == 0 {
+ return w.writer.Write(p)
+ }
+ original := string(p)
+ wrapped := wordwrap.WrapString(original, w.limit)
+ return w.writer.Write([]byte(wrapped))
+}
+
+// NewPunchCardWriter is a NewWordWrapWriter that limits the line width to 80 columns.
+func NewPunchCardWriter(w io.Writer) io.Writer {
+ return NewWordWrapWriter(w, 80)
+}
+
+type maxWidthWriter struct {
+ maxWidth uint
+ currentWidth uint
+ written uint
+ writer io.Writer
+}
+
+// NewMaxWidthWriter is a Writer that supports a limit of characters on every
+// line, but doesn't do any word wrapping automatically.
+func NewMaxWidthWriter(w io.Writer, maxWidth uint) io.Writer {
+ return &maxWidthWriter{
+ maxWidth: maxWidth,
+ writer: w,
+ }
+}
+
+func (m maxWidthWriter) Write(p []byte) (nn int, err error) {
+ for _, b := range p {
+ if m.currentWidth == m.maxWidth {
+ m.writer.Write([]byte{'\n'})
+ m.currentWidth = 0
+ }
+ if b == '\n' {
+ m.currentWidth = 0
+ }
+ _, err := m.writer.Write([]byte{b})
+ if err != nil {
+ return int(m.written), err
+ }
+ m.written++
+ m.currentWidth++
+ }
+ return len(p), nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/umask.go b/vendor/k8s.io/kubernetes/pkg/util/umask.go
new file mode 100644
index 000000000..35ccce50b
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/umask.go
@@ -0,0 +1,27 @@
+// +build !windows
+
+/*
+Copyright 2014 The Kubernetes 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 util
+
+import (
+ "syscall"
+)
+
+func Umask(mask int) (old int, err error) {
+ return syscall.Umask(mask), nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go b/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go
new file mode 100644
index 000000000..7a1ba1538
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/umask_windows.go
@@ -0,0 +1,27 @@
+// +build windows
+
+/*
+Copyright 2014 The Kubernetes 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 util
+
+import (
+ "errors"
+)
+
+func Umask(mask int) (int, error) {
+ return 0, errors.New("platform and architecture is not supported")
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/util.go b/vendor/k8s.io/kubernetes/pkg/util/util.go
new file mode 100644
index 000000000..389e145e8
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/util.go
@@ -0,0 +1,140 @@
+/*
+Copyright 2014 The Kubernetes 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 util
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+ "regexp"
+)
+
+// Takes a list of strings and compiles them into a list of regular expressions
+func CompileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
+ regexps := []*regexp.Regexp{}
+ for _, regexpStr := range regexpStrings {
+ r, err := regexp.Compile(regexpStr)
+ if err != nil {
+ return []*regexp.Regexp{}, err
+ }
+ regexps = append(regexps, r)
+ }
+ return regexps, nil
+}
+
+// Detects if using systemd as the init system
+// Please note that simply reading /proc/1/cmdline can be misleading because
+// some installation of various init programs can automatically make /sbin/init
+// a symlink or even a renamed version of their main program.
+// TODO(dchen1107): realiably detects the init system using on the system:
+// systemd, upstart, initd, etc.
+func UsingSystemdInitSystem() bool {
+ if _, err := os.Stat("/run/systemd/system"); err == nil {
+ return true
+ }
+
+ return false
+}
+
+// Tests whether all pointer fields in a struct are nil. This is useful when,
+// for example, an API struct is handled by plugins which need to distinguish
+// "no plugin accepted this spec" from "this spec is empty".
+//
+// This function is only valid for structs and pointers to structs. Any other
+// type will cause a panic. Passing a typed nil pointer will return true.
+func AllPtrFieldsNil(obj interface{}) bool {
+ v := reflect.ValueOf(obj)
+ if !v.IsValid() {
+ panic(fmt.Sprintf("reflect.ValueOf() produced a non-valid Value for %#v", obj))
+ }
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return true
+ }
+ v = v.Elem()
+ }
+ for i := 0; i < v.NumField(); i++ {
+ if v.Field(i).Kind() == reflect.Ptr && !v.Field(i).IsNil() {
+ return false
+ }
+ }
+ return true
+}
+
+func FileExists(filename string) (bool, error) {
+ if _, err := os.Stat(filename); os.IsNotExist(err) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+func FileOrSymlinkExists(filename string) (bool, error) {
+ if _, err := os.Lstat(filename); os.IsNotExist(err) {
+ return false, nil
+ } else if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// ReadDirNoStat returns a string of files/directories contained
+// in dirname without calling lstat on them.
+func ReadDirNoStat(dirname string) ([]string, error) {
+ if dirname == "" {
+ dirname = "."
+ }
+
+ f, err := os.Open(dirname)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ return f.Readdirnames(-1)
+}
+
+// IntPtr returns a pointer to an int
+func IntPtr(i int) *int {
+ o := i
+ return &o
+}
+
+// Int32Ptr returns a pointer to an int32
+func Int32Ptr(i int32) *int32 {
+ o := i
+ return &o
+}
+
+// IntPtrDerefOr dereference the int ptr and returns it i not nil,
+// else returns def.
+func IntPtrDerefOr(ptr *int, def int) int {
+ if ptr != nil {
+ return *ptr
+ }
+ return def
+}
+
+// Int32PtrDerefOr dereference the int32 ptr and returns it i not nil,
+// else returns def.
+func Int32PtrDerefOr(ptr *int32, def int32) int32 {
+ if ptr != nil {
+ return *ptr
+ }
+ return def
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/version/doc.go b/vendor/k8s.io/kubernetes/pkg/util/version/doc.go
new file mode 100644
index 000000000..ebe43152e
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/version/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2016 The Kubernetes 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 version provides utilities for version number comparisons
+package version // import "k8s.io/kubernetes/pkg/util/version"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/version/version.go b/vendor/k8s.io/kubernetes/pkg/util/version/version.go
new file mode 100644
index 000000000..327f2e67f
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/version/version.go
@@ -0,0 +1,236 @@
+/*
+Copyright 2016 The Kubernetes 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 version
+
+import (
+ "bytes"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// Version is an opqaue representation of a version number
+type Version struct {
+ components []uint
+ semver bool
+ preRelease string
+ buildMetadata string
+}
+
+var (
+ // versionMatchRE splits a version string into numeric and "extra" parts
+ versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
+ // extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
+ extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
+)
+
+func parse(str string, semver bool) (*Version, error) {
+ parts := versionMatchRE.FindStringSubmatch(str)
+ if parts == nil {
+ return nil, fmt.Errorf("could not parse %q as version", str)
+ }
+ numbers, extra := parts[1], parts[2]
+
+ components := strings.Split(numbers, ".")
+ if (semver && len(components) != 3) || (!semver && len(components) < 2) {
+ return nil, fmt.Errorf("illegal version string %q", str)
+ }
+
+ v := &Version{
+ components: make([]uint, len(components)),
+ semver: semver,
+ }
+ for i, comp := range components {
+ if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
+ return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
+ }
+ num, err := strconv.ParseUint(comp, 10, 0)
+ if err != nil {
+ return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
+ }
+ v.components[i] = uint(num)
+ }
+
+ if semver && extra != "" {
+ extraParts := extraMatchRE.FindStringSubmatch(extra)
+ if extraParts == nil {
+ return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
+ }
+ v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
+
+ for _, comp := range strings.Split(v.preRelease, ".") {
+ if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
+ if strings.HasPrefix(comp, "0") && comp != "0" {
+ return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
+ }
+ }
+ }
+ }
+
+ return v, nil
+}
+
+// ParseGeneric parses a "generic" version string. The version string must consist of two
+// or more dot-separated numeric fields (the first of which can't have leading zeroes),
+// followed by arbitrary uninterpreted data (which need not be separated from the final
+// numeric field by punctuation). For convenience, leading and trailing whitespace is
+// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
+func ParseGeneric(str string) (*Version, error) {
+ return parse(str, false)
+}
+
+// MustParseGeneric is like ParseGeneric except that it panics on error
+func MustParseGeneric(str string) *Version {
+ v, err := ParseGeneric(str)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
+// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
+// leading and trailing whitespace, and allows the version to be preceded by "v"). For
+// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
+// ParseGeneric.
+func ParseSemantic(str string) (*Version, error) {
+ return parse(str, true)
+}
+
+// MustParseSemantic is like ParseSemantic except that it panics on error
+func MustParseSemantic(str string) *Version {
+ v, err := ParseSemantic(str)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
+func (v *Version) BuildMetadata() string {
+ return v.buildMetadata
+}
+
+// String converts a Version back to a string; note that for versions parsed with
+// ParseGeneric, this will not include the trailing uninterpreted portion of the version
+// number.
+func (v *Version) String() string {
+ var buffer bytes.Buffer
+
+ for i, comp := range v.components {
+ if i > 0 {
+ buffer.WriteString(".")
+ }
+ buffer.WriteString(fmt.Sprintf("%d", comp))
+ }
+ if v.preRelease != "" {
+ buffer.WriteString("-")
+ buffer.WriteString(v.preRelease)
+ }
+ if v.buildMetadata != "" {
+ buffer.WriteString("+")
+ buffer.WriteString(v.buildMetadata)
+ }
+
+ return buffer.String()
+}
+
+// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
+// if they are equal
+func (v *Version) compareInternal(other *Version) int {
+ for i := range v.components {
+ switch {
+ case i >= len(other.components):
+ if v.components[i] != 0 {
+ return 1
+ }
+ case other.components[i] < v.components[i]:
+ return 1
+ case other.components[i] > v.components[i]:
+ return -1
+ }
+ }
+
+ if !v.semver || !other.semver {
+ return 0
+ }
+
+ switch {
+ case v.preRelease == "" && other.preRelease != "":
+ return 1
+ case v.preRelease != "" && other.preRelease == "":
+ return -1
+ case v.preRelease == other.preRelease: // includes case where both are ""
+ return 0
+ }
+
+ vPR := strings.Split(v.preRelease, ".")
+ oPR := strings.Split(other.preRelease, ".")
+ for i := range vPR {
+ if i >= len(oPR) {
+ return 1
+ }
+ vNum, err := strconv.ParseUint(vPR[i], 10, 0)
+ if err == nil {
+ oNum, err := strconv.ParseUint(oPR[i], 10, 0)
+ if err == nil {
+ switch {
+ case oNum < vNum:
+ return 1
+ case oNum > vNum:
+ return -1
+ default:
+ continue
+ }
+ }
+ }
+ if oPR[i] < vPR[i] {
+ return 1
+ } else if oPR[i] > vPR[i] {
+ return -1
+ }
+ }
+
+ return 0
+}
+
+// AtLeast tests if a version is at least equal to a given minimum version. If both
+// Versions are Semantic Versions, this will use the Semantic Version comparison
+// algorithm. Otherwise, it will compare only the numeric components, with non-present
+// components being considered "0" (ie, "1.4" is equal to "1.4.0").
+func (v *Version) AtLeast(min *Version) bool {
+ return v.compareInternal(min) != -1
+}
+
+// LessThan tests if a version is less than a given version. (It is exactly the opposite
+// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
+// "is v new enough?".)
+func (v *Version) LessThan(other *Version) bool {
+ return v.compareInternal(other) == -1
+}
+
+// Compare compares v against a version string (which will be parsed as either Semantic
+// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
+// it is greater than other, or 0 if they are equal.
+func (v *Version) Compare(other string) (int, error) {
+ ov, err := parse(other, v.semver)
+ if err != nil {
+ return 0, err
+ }
+ return v.compareInternal(ov), nil
+}