Improvements to afrouterd.

- Added ability to run the afrouterd outside the cluster, using a URL & local kube-config instead of in-cluster config.
- Fixed a bug where the afrouterd would not wait for connectivity to the afrouter before starting its main loop.
  (grpc.Dial is async by default.)

This may or may not resolve VOL-1661.

Change-Id: I58ba2ef52edb7f0eddcf7d7f2735f3b9d460237a
diff --git a/vendor/k8s.io/client-go/tools/auth/OWNERS b/vendor/k8s.io/client-go/tools/auth/OWNERS
new file mode 100644
index 0000000..c607d2a
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/auth/OWNERS
@@ -0,0 +1,7 @@
+approvers:
+- sig-auth-authenticators-approvers
+reviewers:
+- sig-auth-authenticators-reviewers
+labels:
+- sig/auth
+
diff --git a/vendor/k8s.io/client-go/tools/auth/clientauth.go b/vendor/k8s.io/client-go/tools/auth/clientauth.go
new file mode 100644
index 0000000..20339ab
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/auth/clientauth.go
@@ -0,0 +1,125 @@
+/*
+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 auth defines a file format for holding authentication
+information needed by clients of Kubernetes.  Typically,
+a Kubernetes cluster will put auth info for the admin in a known
+location when it is created, and will (soon) put it in a known
+location within a Container's file tree for Containers that
+need access to the Kubernetes API.
+
+Having a defined format allows:
+  - clients to be implemented in multiple languages
+  - applications which link clients to be portable across
+    clusters with different authentication styles (e.g.
+    some may use SSL Client certs, others may not, etc)
+  - when the format changes, applications only
+    need to update this code.
+
+The file format is json, marshalled from a struct authcfg.Info.
+
+Clinet libraries in other languages should use the same format.
+
+It is not intended to store general preferences, such as default
+namespace, output options, etc.  CLIs (such as kubectl) and UIs should
+develop their own format and may wish to inline the authcfg.Info type.
+
+The authcfg.Info is just a file format.  It is distinct from
+client.Config which holds options for creating a client.Client.
+Helper functions are provided in this package to fill in a
+client.Client from an authcfg.Info.
+
+Example:
+
+    import (
+        "pkg/client"
+        "pkg/client/auth"
+    )
+
+    info, err := auth.LoadFromFile(filename)
+    if err != nil {
+      // handle error
+    }
+    clientConfig = client.Config{}
+    clientConfig.Host = "example.com:4901"
+    clientConfig = info.MergeWithConfig()
+    client := client.New(clientConfig)
+    client.Pods(ns).List()
+*/
+package auth
+
+// TODO: need a way to rotate Tokens.  Therefore, need a way for client object to be reset when the authcfg is updated.
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+
+	restclient "k8s.io/client-go/rest"
+)
+
+// Info holds Kubernetes API authorization config.  It is intended
+// to be read/written from a file as a JSON object.
+type Info struct {
+	User        string
+	Password    string
+	CAFile      string
+	CertFile    string
+	KeyFile     string
+	BearerToken string
+	Insecure    *bool
+}
+
+// LoadFromFile parses an Info object from a file path.
+// If the file does not exist, then os.IsNotExist(err) == true
+func LoadFromFile(path string) (*Info, error) {
+	var info Info
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		return nil, err
+	}
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+	err = json.Unmarshal(data, &info)
+	if err != nil {
+		return nil, err
+	}
+	return &info, err
+}
+
+// MergeWithConfig returns a copy of a client.Config with values from the Info.
+// The fields of client.Config with a corresponding field in the Info are set
+// with the value from the Info.
+func (info Info) MergeWithConfig(c restclient.Config) (restclient.Config, error) {
+	var config restclient.Config = c
+	config.Username = info.User
+	config.Password = info.Password
+	config.CAFile = info.CAFile
+	config.CertFile = info.CertFile
+	config.KeyFile = info.KeyFile
+	config.BearerToken = info.BearerToken
+	if info.Insecure != nil {
+		config.Insecure = *info.Insecure
+	}
+	return config, nil
+}
+
+func (info Info) Complete() bool {
+	return len(info.User) > 0 ||
+		len(info.CertFile) > 0 ||
+		len(info.BearerToken) > 0
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/latest/latest.go b/vendor/k8s.io/client-go/tools/clientcmd/api/latest/latest.go
new file mode 100644
index 0000000..35bb5dd
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/latest/latest.go
@@ -0,0 +1,61 @@
+/*
+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 latest
+
+import (
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer/json"
+	"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	"k8s.io/client-go/tools/clientcmd/api"
+	"k8s.io/client-go/tools/clientcmd/api/v1"
+)
+
+// Version is the string that represents the current external default version.
+const Version = "v1"
+
+var ExternalVersion = schema.GroupVersion{Group: "", Version: "v1"}
+
+// OldestVersion is the string that represents the oldest server version supported,
+// for client code that wants to hardcode the lowest common denominator.
+const OldestVersion = "v1"
+
+// Versions is the list of versions that are recognized in code. The order provided
+// may be assumed to be least feature rich to most feature rich, and clients may
+// choose to prefer the latter items in the list over the former items when presented
+// with a set of versions to choose.
+var Versions = []string{"v1"}
+
+var (
+	Codec  runtime.Codec
+	Scheme *runtime.Scheme
+)
+
+func init() {
+	Scheme = runtime.NewScheme()
+	utilruntime.Must(api.AddToScheme(Scheme))
+	utilruntime.Must(v1.AddToScheme(Scheme))
+	yamlSerializer := json.NewYAMLSerializer(json.DefaultMetaFactory, Scheme, Scheme)
+	Codec = versioning.NewDefaultingCodecForScheme(
+		Scheme,
+		yamlSerializer,
+		yamlSerializer,
+		schema.GroupVersion{Version: Version},
+		runtime.InternalGroupVersioner,
+	)
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go
new file mode 100644
index 0000000..2d7142e
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go
@@ -0,0 +1,244 @@
+/*
+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 v1
+
+import (
+	"fmt"
+	"sort"
+
+	"k8s.io/apimachinery/pkg/conversion"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/tools/clientcmd/api"
+)
+
+func addConversionFuncs(scheme *runtime.Scheme) error {
+	return scheme.AddConversionFuncs(
+		func(in *Cluster, out *api.Cluster, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *api.Cluster, out *Cluster, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *Preferences, out *api.Preferences, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *api.Preferences, out *Preferences, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *AuthInfo, out *api.AuthInfo, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *api.AuthInfo, out *AuthInfo, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *Context, out *api.Context, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+		func(in *api.Context, out *Context, s conversion.Scope) error {
+			return s.DefaultConvert(in, out, conversion.IgnoreMissingFields)
+		},
+
+		func(in *Config, out *api.Config, s conversion.Scope) error {
+			out.CurrentContext = in.CurrentContext
+			if err := s.Convert(&in.Preferences, &out.Preferences, 0); err != nil {
+				return err
+			}
+
+			out.Clusters = make(map[string]*api.Cluster)
+			if err := s.Convert(&in.Clusters, &out.Clusters, 0); err != nil {
+				return err
+			}
+			out.AuthInfos = make(map[string]*api.AuthInfo)
+			if err := s.Convert(&in.AuthInfos, &out.AuthInfos, 0); err != nil {
+				return err
+			}
+			out.Contexts = make(map[string]*api.Context)
+			if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil {
+				return err
+			}
+			out.Extensions = make(map[string]runtime.Object)
+			if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil {
+				return err
+			}
+			return nil
+		},
+		func(in *api.Config, out *Config, s conversion.Scope) error {
+			out.CurrentContext = in.CurrentContext
+			if err := s.Convert(&in.Preferences, &out.Preferences, 0); err != nil {
+				return err
+			}
+
+			out.Clusters = make([]NamedCluster, 0, 0)
+			if err := s.Convert(&in.Clusters, &out.Clusters, 0); err != nil {
+				return err
+			}
+			out.AuthInfos = make([]NamedAuthInfo, 0, 0)
+			if err := s.Convert(&in.AuthInfos, &out.AuthInfos, 0); err != nil {
+				return err
+			}
+			out.Contexts = make([]NamedContext, 0, 0)
+			if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil {
+				return err
+			}
+			out.Extensions = make([]NamedExtension, 0, 0)
+			if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil {
+				return err
+			}
+			return nil
+		},
+		func(in *[]NamedCluster, out *map[string]*api.Cluster, s conversion.Scope) error {
+			for _, curr := range *in {
+				newCluster := api.NewCluster()
+				if err := s.Convert(&curr.Cluster, newCluster, 0); err != nil {
+					return err
+				}
+				if (*out)[curr.Name] == nil {
+					(*out)[curr.Name] = newCluster
+				} else {
+					return fmt.Errorf("error converting *[]NamedCluster into *map[string]*api.Cluster: duplicate name \"%v\" in list: %v", curr.Name, *in)
+				}
+			}
+
+			return nil
+		},
+		func(in *map[string]*api.Cluster, out *[]NamedCluster, s conversion.Scope) error {
+			allKeys := make([]string, 0, len(*in))
+			for key := range *in {
+				allKeys = append(allKeys, key)
+			}
+			sort.Strings(allKeys)
+
+			for _, key := range allKeys {
+				newCluster := (*in)[key]
+				oldCluster := &Cluster{}
+				if err := s.Convert(newCluster, oldCluster, 0); err != nil {
+					return err
+				}
+
+				namedCluster := NamedCluster{key, *oldCluster}
+				*out = append(*out, namedCluster)
+			}
+
+			return nil
+		},
+		func(in *[]NamedAuthInfo, out *map[string]*api.AuthInfo, s conversion.Scope) error {
+			for _, curr := range *in {
+				newAuthInfo := api.NewAuthInfo()
+				if err := s.Convert(&curr.AuthInfo, newAuthInfo, 0); err != nil {
+					return err
+				}
+				if (*out)[curr.Name] == nil {
+					(*out)[curr.Name] = newAuthInfo
+				} else {
+					return fmt.Errorf("error converting *[]NamedAuthInfo into *map[string]*api.AuthInfo: duplicate name \"%v\" in list: %v", curr.Name, *in)
+				}
+			}
+
+			return nil
+		},
+		func(in *map[string]*api.AuthInfo, out *[]NamedAuthInfo, s conversion.Scope) error {
+			allKeys := make([]string, 0, len(*in))
+			for key := range *in {
+				allKeys = append(allKeys, key)
+			}
+			sort.Strings(allKeys)
+
+			for _, key := range allKeys {
+				newAuthInfo := (*in)[key]
+				oldAuthInfo := &AuthInfo{}
+				if err := s.Convert(newAuthInfo, oldAuthInfo, 0); err != nil {
+					return err
+				}
+
+				namedAuthInfo := NamedAuthInfo{key, *oldAuthInfo}
+				*out = append(*out, namedAuthInfo)
+			}
+
+			return nil
+		},
+		func(in *[]NamedContext, out *map[string]*api.Context, s conversion.Scope) error {
+			for _, curr := range *in {
+				newContext := api.NewContext()
+				if err := s.Convert(&curr.Context, newContext, 0); err != nil {
+					return err
+				}
+				if (*out)[curr.Name] == nil {
+					(*out)[curr.Name] = newContext
+				} else {
+					return fmt.Errorf("error converting *[]NamedContext into *map[string]*api.Context: duplicate name \"%v\" in list: %v", curr.Name, *in)
+				}
+			}
+
+			return nil
+		},
+		func(in *map[string]*api.Context, out *[]NamedContext, s conversion.Scope) error {
+			allKeys := make([]string, 0, len(*in))
+			for key := range *in {
+				allKeys = append(allKeys, key)
+			}
+			sort.Strings(allKeys)
+
+			for _, key := range allKeys {
+				newContext := (*in)[key]
+				oldContext := &Context{}
+				if err := s.Convert(newContext, oldContext, 0); err != nil {
+					return err
+				}
+
+				namedContext := NamedContext{key, *oldContext}
+				*out = append(*out, namedContext)
+			}
+
+			return nil
+		},
+		func(in *[]NamedExtension, out *map[string]runtime.Object, s conversion.Scope) error {
+			for _, curr := range *in {
+				var newExtension runtime.Object
+				if err := s.Convert(&curr.Extension, &newExtension, 0); err != nil {
+					return err
+				}
+				if (*out)[curr.Name] == nil {
+					(*out)[curr.Name] = newExtension
+				} else {
+					return fmt.Errorf("error converting *[]NamedExtension into *map[string]runtime.Object: duplicate name \"%v\" in list: %v", curr.Name, *in)
+				}
+			}
+
+			return nil
+		},
+		func(in *map[string]runtime.Object, out *[]NamedExtension, s conversion.Scope) error {
+			allKeys := make([]string, 0, len(*in))
+			for key := range *in {
+				allKeys = append(allKeys, key)
+			}
+			sort.Strings(allKeys)
+
+			for _, key := range allKeys {
+				newExtension := (*in)[key]
+				oldExtension := &runtime.RawExtension{}
+				if err := s.Convert(newExtension, oldExtension, 0); err != nil {
+					return err
+				}
+
+				namedExtension := NamedExtension{key, *oldExtension}
+				*out = append(*out, namedExtension)
+			}
+
+			return nil
+		},
+	)
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/v1/doc.go b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/doc.go
new file mode 100644
index 0000000..cbf29cc
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/doc.go
@@ -0,0 +1,19 @@
+/*
+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.
+*/
+
+// +k8s:deepcopy-gen=package
+
+package v1
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/v1/register.go b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/register.go
new file mode 100644
index 0000000..7b91d50
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/register.go
@@ -0,0 +1,56 @@
+/*
+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 v1
+
+import (
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// SchemeGroupVersion is group version used to register these objects
+// TODO this should be in the "kubeconfig" group
+var SchemeGroupVersion = schema.GroupVersion{Group: "", Version: "v1"}
+
+var (
+	// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
+	// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
+	SchemeBuilder      runtime.SchemeBuilder
+	localSchemeBuilder = &SchemeBuilder
+	AddToScheme        = localSchemeBuilder.AddToScheme
+)
+
+func init() {
+	// We only register manually written functions here. The registration of the
+	// generated functions takes place in the generated files. The separation
+	// makes the code compile even when the generated files are missing.
+	localSchemeBuilder.Register(addKnownTypes, addConversionFuncs)
+}
+
+func addKnownTypes(scheme *runtime.Scheme) error {
+	scheme.AddKnownTypes(SchemeGroupVersion,
+		&Config{},
+	)
+	return nil
+}
+
+func (obj *Config) GetObjectKind() schema.ObjectKind { return obj }
+func (obj *Config) SetGroupVersionKind(gvk schema.GroupVersionKind) {
+	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
+}
+func (obj *Config) GroupVersionKind() schema.GroupVersionKind {
+	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/v1/types.go b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/types.go
new file mode 100644
index 0000000..56afb60
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/types.go
@@ -0,0 +1,203 @@
+/*
+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 v1
+
+import (
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// Where possible, json tags match the cli argument names.
+// Top level config objects and all values required for proper functioning are not "omitempty".  Any truly optional piece of config is allowed to be omitted.
+
+// Config holds the information needed to build connect to remote kubernetes clusters as a given user
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type Config struct {
+	// Legacy field from pkg/api/types.go TypeMeta.
+	// TODO(jlowdermilk): remove this after eliminating downstream dependencies.
+	// +optional
+	Kind string `json:"kind,omitempty"`
+	// Legacy field from pkg/api/types.go TypeMeta.
+	// TODO(jlowdermilk): remove this after eliminating downstream dependencies.
+	// +optional
+	APIVersion string `json:"apiVersion,omitempty"`
+	// Preferences holds general information to be use for cli interactions
+	Preferences Preferences `json:"preferences"`
+	// Clusters is a map of referencable names to cluster configs
+	Clusters []NamedCluster `json:"clusters"`
+	// AuthInfos is a map of referencable names to user configs
+	AuthInfos []NamedAuthInfo `json:"users"`
+	// Contexts is a map of referencable names to context configs
+	Contexts []NamedContext `json:"contexts"`
+	// CurrentContext is the name of the context that you would like to use by default
+	CurrentContext string `json:"current-context"`
+	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
+	// +optional
+	Extensions []NamedExtension `json:"extensions,omitempty"`
+}
+
+type Preferences struct {
+	// +optional
+	Colors bool `json:"colors,omitempty"`
+	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
+	// +optional
+	Extensions []NamedExtension `json:"extensions,omitempty"`
+}
+
+// Cluster contains information about how to communicate with a kubernetes cluster
+type Cluster struct {
+	// Server is the address of the kubernetes cluster (https://hostname:port).
+	Server string `json:"server"`
+	// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
+	// +optional
+	InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
+	// CertificateAuthority is the path to a cert file for the certificate authority.
+	// +optional
+	CertificateAuthority string `json:"certificate-authority,omitempty"`
+	// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
+	// +optional
+	CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
+	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
+	// +optional
+	Extensions []NamedExtension `json:"extensions,omitempty"`
+}
+
+// AuthInfo contains information that describes identity information.  This is use to tell the kubernetes cluster who you are.
+type AuthInfo struct {
+	// ClientCertificate is the path to a client cert file for TLS.
+	// +optional
+	ClientCertificate string `json:"client-certificate,omitempty"`
+	// ClientCertificateData contains PEM-encoded data from a client cert file for TLS. Overrides ClientCertificate
+	// +optional
+	ClientCertificateData []byte `json:"client-certificate-data,omitempty"`
+	// ClientKey is the path to a client key file for TLS.
+	// +optional
+	ClientKey string `json:"client-key,omitempty"`
+	// ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey
+	// +optional
+	ClientKeyData []byte `json:"client-key-data,omitempty"`
+	// Token is the bearer token for authentication to the kubernetes cluster.
+	// +optional
+	Token string `json:"token,omitempty"`
+	// TokenFile is a pointer to a file that contains a bearer token (as described above).  If both Token and TokenFile are present, Token takes precedence.
+	// +optional
+	TokenFile string `json:"tokenFile,omitempty"`
+	// Impersonate is the username to imperonate.  The name matches the flag.
+	// +optional
+	Impersonate string `json:"as,omitempty"`
+	// ImpersonateGroups is the groups to imperonate.
+	// +optional
+	ImpersonateGroups []string `json:"as-groups,omitempty"`
+	// ImpersonateUserExtra contains additional information for impersonated user.
+	// +optional
+	ImpersonateUserExtra map[string][]string `json:"as-user-extra,omitempty"`
+	// Username is the username for basic authentication to the kubernetes cluster.
+	// +optional
+	Username string `json:"username,omitempty"`
+	// Password is the password for basic authentication to the kubernetes cluster.
+	// +optional
+	Password string `json:"password,omitempty"`
+	// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
+	// +optional
+	AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
+	// Exec specifies a custom exec-based authentication plugin for the kubernetes cluster.
+	// +optional
+	Exec *ExecConfig `json:"exec,omitempty"`
+	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
+	// +optional
+	Extensions []NamedExtension `json:"extensions,omitempty"`
+}
+
+// Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with)
+type Context struct {
+	// Cluster is the name of the cluster for this context
+	Cluster string `json:"cluster"`
+	// AuthInfo is the name of the authInfo for this context
+	AuthInfo string `json:"user"`
+	// Namespace is the default namespace to use on unspecified requests
+	// +optional
+	Namespace string `json:"namespace,omitempty"`
+	// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
+	// +optional
+	Extensions []NamedExtension `json:"extensions,omitempty"`
+}
+
+// NamedCluster relates nicknames to cluster information
+type NamedCluster struct {
+	// Name is the nickname for this Cluster
+	Name string `json:"name"`
+	// Cluster holds the cluster information
+	Cluster Cluster `json:"cluster"`
+}
+
+// NamedContext relates nicknames to context information
+type NamedContext struct {
+	// Name is the nickname for this Context
+	Name string `json:"name"`
+	// Context holds the context information
+	Context Context `json:"context"`
+}
+
+// NamedAuthInfo relates nicknames to auth information
+type NamedAuthInfo struct {
+	// Name is the nickname for this AuthInfo
+	Name string `json:"name"`
+	// AuthInfo holds the auth information
+	AuthInfo AuthInfo `json:"user"`
+}
+
+// NamedExtension relates nicknames to extension information
+type NamedExtension struct {
+	// Name is the nickname for this Extension
+	Name string `json:"name"`
+	// Extension holds the extension information
+	Extension runtime.RawExtension `json:"extension"`
+}
+
+// AuthProviderConfig holds the configuration for a specified auth provider.
+type AuthProviderConfig struct {
+	Name   string            `json:"name"`
+	Config map[string]string `json:"config"`
+}
+
+// ExecConfig specifies a command to provide client credentials. The command is exec'd
+// and outputs structured stdout holding credentials.
+//
+// See the client.authentiction.k8s.io API group for specifications of the exact input
+// and output format
+type ExecConfig struct {
+	// Command to execute.
+	Command string `json:"command"`
+	// Arguments to pass to the command when executing it.
+	// +optional
+	Args []string `json:"args"`
+	// Env defines additional environment variables to expose to the process. These
+	// are unioned with the host's environment, as well as variables client-go uses
+	// to pass argument to the plugin.
+	// +optional
+	Env []ExecEnvVar `json:"env"`
+
+	// Preferred input version of the ExecInfo. The returned ExecCredentials MUST use
+	// the same encoding version as the input.
+	APIVersion string `json:"apiVersion,omitempty"`
+}
+
+// ExecEnvVar is used for setting environment variables when executing an exec-based
+// credential plugin.
+type ExecEnvVar struct {
+	Name  string `json:"name"`
+	Value string `json:"value"`
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.deepcopy.go b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.deepcopy.go
new file mode 100644
index 0000000..da519df
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.deepcopy.go
@@ -0,0 +1,348 @@
+// +build !ignore_autogenerated
+
+/*
+Copyright 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.
+*/
+
+// Code generated by deepcopy-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AuthInfo) DeepCopyInto(out *AuthInfo) {
+	*out = *in
+	if in.ClientCertificateData != nil {
+		in, out := &in.ClientCertificateData, &out.ClientCertificateData
+		*out = make([]byte, len(*in))
+		copy(*out, *in)
+	}
+	if in.ClientKeyData != nil {
+		in, out := &in.ClientKeyData, &out.ClientKeyData
+		*out = make([]byte, len(*in))
+		copy(*out, *in)
+	}
+	if in.ImpersonateGroups != nil {
+		in, out := &in.ImpersonateGroups, &out.ImpersonateGroups
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.ImpersonateUserExtra != nil {
+		in, out := &in.ImpersonateUserExtra, &out.ImpersonateUserExtra
+		*out = make(map[string][]string, len(*in))
+		for key, val := range *in {
+			var outVal []string
+			if val == nil {
+				(*out)[key] = nil
+			} else {
+				in, out := &val, &outVal
+				*out = make([]string, len(*in))
+				copy(*out, *in)
+			}
+			(*out)[key] = outVal
+		}
+	}
+	if in.AuthProvider != nil {
+		in, out := &in.AuthProvider, &out.AuthProvider
+		*out = new(AuthProviderConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Exec != nil {
+		in, out := &in.Exec, &out.Exec
+		*out = new(ExecConfig)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Extensions != nil {
+		in, out := &in.Extensions, &out.Extensions
+		*out = make([]NamedExtension, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthInfo.
+func (in *AuthInfo) DeepCopy() *AuthInfo {
+	if in == nil {
+		return nil
+	}
+	out := new(AuthInfo)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *AuthProviderConfig) DeepCopyInto(out *AuthProviderConfig) {
+	*out = *in
+	if in.Config != nil {
+		in, out := &in.Config, &out.Config
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthProviderConfig.
+func (in *AuthProviderConfig) DeepCopy() *AuthProviderConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(AuthProviderConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Cluster) DeepCopyInto(out *Cluster) {
+	*out = *in
+	if in.CertificateAuthorityData != nil {
+		in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData
+		*out = make([]byte, len(*in))
+		copy(*out, *in)
+	}
+	if in.Extensions != nil {
+		in, out := &in.Extensions, &out.Extensions
+		*out = make([]NamedExtension, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster.
+func (in *Cluster) DeepCopy() *Cluster {
+	if in == nil {
+		return nil
+	}
+	out := new(Cluster)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Config) DeepCopyInto(out *Config) {
+	*out = *in
+	in.Preferences.DeepCopyInto(&out.Preferences)
+	if in.Clusters != nil {
+		in, out := &in.Clusters, &out.Clusters
+		*out = make([]NamedCluster, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.AuthInfos != nil {
+		in, out := &in.AuthInfos, &out.AuthInfos
+		*out = make([]NamedAuthInfo, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.Contexts != nil {
+		in, out := &in.Contexts, &out.Contexts
+		*out = make([]NamedContext, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	if in.Extensions != nil {
+		in, out := &in.Extensions, &out.Extensions
+		*out = make([]NamedExtension, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config.
+func (in *Config) DeepCopy() *Config {
+	if in == nil {
+		return nil
+	}
+	out := new(Config)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Config) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Context) DeepCopyInto(out *Context) {
+	*out = *in
+	if in.Extensions != nil {
+		in, out := &in.Extensions, &out.Extensions
+		*out = make([]NamedExtension, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Context.
+func (in *Context) DeepCopy() *Context {
+	if in == nil {
+		return nil
+	}
+	out := new(Context)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExecConfig) DeepCopyInto(out *ExecConfig) {
+	*out = *in
+	if in.Args != nil {
+		in, out := &in.Args, &out.Args
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Env != nil {
+		in, out := &in.Env, &out.Env
+		*out = make([]ExecEnvVar, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExecConfig.
+func (in *ExecConfig) DeepCopy() *ExecConfig {
+	if in == nil {
+		return nil
+	}
+	out := new(ExecConfig)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ExecEnvVar) DeepCopyInto(out *ExecEnvVar) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExecEnvVar.
+func (in *ExecEnvVar) DeepCopy() *ExecEnvVar {
+	if in == nil {
+		return nil
+	}
+	out := new(ExecEnvVar)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamedAuthInfo) DeepCopyInto(out *NamedAuthInfo) {
+	*out = *in
+	in.AuthInfo.DeepCopyInto(&out.AuthInfo)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedAuthInfo.
+func (in *NamedAuthInfo) DeepCopy() *NamedAuthInfo {
+	if in == nil {
+		return nil
+	}
+	out := new(NamedAuthInfo)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamedCluster) DeepCopyInto(out *NamedCluster) {
+	*out = *in
+	in.Cluster.DeepCopyInto(&out.Cluster)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedCluster.
+func (in *NamedCluster) DeepCopy() *NamedCluster {
+	if in == nil {
+		return nil
+	}
+	out := new(NamedCluster)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamedContext) DeepCopyInto(out *NamedContext) {
+	*out = *in
+	in.Context.DeepCopyInto(&out.Context)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedContext.
+func (in *NamedContext) DeepCopy() *NamedContext {
+	if in == nil {
+		return nil
+	}
+	out := new(NamedContext)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamedExtension) DeepCopyInto(out *NamedExtension) {
+	*out = *in
+	in.Extension.DeepCopyInto(&out.Extension)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamedExtension.
+func (in *NamedExtension) DeepCopy() *NamedExtension {
+	if in == nil {
+		return nil
+	}
+	out := new(NamedExtension)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Preferences) DeepCopyInto(out *Preferences) {
+	*out = *in
+	if in.Extensions != nil {
+		in, out := &in.Extensions, &out.Extensions
+		*out = make([]NamedExtension, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Preferences.
+func (in *Preferences) DeepCopy() *Preferences {
+	if in == nil {
+		return nil
+	}
+	out := new(Preferences)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/auth_loaders.go b/vendor/k8s.io/client-go/tools/clientcmd/auth_loaders.go
new file mode 100644
index 0000000..1d3c11d
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/auth_loaders.go
@@ -0,0 +1,111 @@
+/*
+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 clientcmd
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+
+	"golang.org/x/crypto/ssh/terminal"
+
+	clientauth "k8s.io/client-go/tools/auth"
+)
+
+// AuthLoaders are used to build clientauth.Info objects.
+type AuthLoader interface {
+	// LoadAuth takes a path to a config file and can then do anything it needs in order to return a valid clientauth.Info
+	LoadAuth(path string) (*clientauth.Info, error)
+}
+
+// default implementation of an AuthLoader
+type defaultAuthLoader struct{}
+
+// LoadAuth for defaultAuthLoader simply delegates to clientauth.LoadFromFile
+func (*defaultAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
+	return clientauth.LoadFromFile(path)
+}
+
+type PromptingAuthLoader struct {
+	reader io.Reader
+}
+
+// LoadAuth parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
+func (a *PromptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
+	// Prompt for user/pass and write a file if none exists.
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		authPtr, err := a.Prompt()
+		auth := *authPtr
+		if err != nil {
+			return nil, err
+		}
+		data, err := json.Marshal(auth)
+		if err != nil {
+			return &auth, err
+		}
+		err = ioutil.WriteFile(path, data, 0600)
+		return &auth, err
+	}
+	authPtr, err := clientauth.LoadFromFile(path)
+	if err != nil {
+		return nil, err
+	}
+	return authPtr, nil
+}
+
+// Prompt pulls the user and password from a reader
+func (a *PromptingAuthLoader) Prompt() (*clientauth.Info, error) {
+	var err error
+	auth := &clientauth.Info{}
+	auth.User, err = promptForString("Username", a.reader, true)
+	if err != nil {
+		return nil, err
+	}
+	auth.Password, err = promptForString("Password", nil, false)
+	if err != nil {
+		return nil, err
+	}
+	return auth, nil
+}
+
+func promptForString(field string, r io.Reader, show bool) (result string, err error) {
+	fmt.Printf("Please enter %s: ", field)
+	if show {
+		_, err = fmt.Fscan(r, &result)
+	} else {
+		var data []byte
+		if terminal.IsTerminal(int(os.Stdin.Fd())) {
+			data, err = terminal.ReadPassword(int(os.Stdin.Fd()))
+			result = string(data)
+		} else {
+			return "", fmt.Errorf("error reading input for %s", field)
+		}
+	}
+	return result, err
+}
+
+// NewPromptingAuthLoader is an AuthLoader that parses an AuthInfo object from a file path. It prompts user and creates file if it doesn't exist.
+func NewPromptingAuthLoader(reader io.Reader) *PromptingAuthLoader {
+	return &PromptingAuthLoader{reader}
+}
+
+// NewDefaultAuthLoader returns a default implementation of an AuthLoader that only reads from a config file
+func NewDefaultAuthLoader() AuthLoader {
+	return &defaultAuthLoader{}
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/client_config.go b/vendor/k8s.io/client-go/tools/clientcmd/client_config.go
new file mode 100644
index 0000000..dea229c
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/client_config.go
@@ -0,0 +1,568 @@
+/*
+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 clientcmd
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"strings"
+
+	"github.com/imdario/mergo"
+	"k8s.io/klog"
+
+	restclient "k8s.io/client-go/rest"
+	clientauth "k8s.io/client-go/tools/auth"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+var (
+	// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
+	// DEPRECATED will be replaced
+	ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
+	// DefaultClientConfig represents the legacy behavior of this package for defaulting
+	// DEPRECATED will be replace
+	DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
+		ClusterDefaults: ClusterDefaults,
+	}, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
+)
+
+// getDefaultServer returns a default setting for DefaultClientConfig
+// DEPRECATED
+func getDefaultServer() string {
+	if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 {
+		return server
+	}
+	return "http://localhost:8080"
+}
+
+// ClientConfig is used to make it easy to get an api server client
+type ClientConfig interface {
+	// RawConfig returns the merged result of all overrides
+	RawConfig() (clientcmdapi.Config, error)
+	// ClientConfig returns a complete client config
+	ClientConfig() (*restclient.Config, error)
+	// Namespace returns the namespace resulting from the merged
+	// result of all overrides and a boolean indicating if it was
+	// overridden
+	Namespace() (string, bool, error)
+	// ConfigAccess returns the rules for loading/persisting the config.
+	ConfigAccess() ConfigAccess
+}
+
+type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderConfigPersister
+
+type promptedCredentials struct {
+	username string
+	password string
+}
+
+// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
+type DirectClientConfig struct {
+	config         clientcmdapi.Config
+	contextName    string
+	overrides      *ConfigOverrides
+	fallbackReader io.Reader
+	configAccess   ConfigAccess
+	// promptedCredentials store the credentials input by the user
+	promptedCredentials promptedCredentials
+}
+
+// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
+func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig {
+	return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
+}
+
+// NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
+func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) ClientConfig {
+	return &DirectClientConfig{config, contextName, overrides, nil, configAccess, promptedCredentials{}}
+}
+
+// NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
+func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) ClientConfig {
+	return &DirectClientConfig{config, contextName, overrides, fallbackReader, configAccess, promptedCredentials{}}
+}
+
+// NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig
+func NewClientConfigFromBytes(configBytes []byte) (ClientConfig, error) {
+	config, err := Load(configBytes)
+	if err != nil {
+		return nil, err
+	}
+
+	return &DirectClientConfig{*config, "", &ConfigOverrides{}, nil, nil, promptedCredentials{}}, nil
+}
+
+// RESTConfigFromKubeConfig is a convenience method to give back a restconfig from your kubeconfig bytes.
+// For programmatic access, this is what you want 80% of the time
+func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
+	clientConfig, err := NewClientConfigFromBytes(configBytes)
+	if err != nil {
+		return nil, err
+	}
+	return clientConfig.ClientConfig()
+}
+
+func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) {
+	return config.config, nil
+}
+
+// ClientConfig implements ClientConfig
+func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
+	// check that getAuthInfo, getContext, and getCluster do not return an error.
+	// Do this before checking if the current config is usable in the event that an
+	// AuthInfo, Context, or Cluster config with user-defined names are not found.
+	// This provides a user with the immediate cause for error if one is found
+	configAuthInfo, err := config.getAuthInfo()
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = config.getContext()
+	if err != nil {
+		return nil, err
+	}
+
+	configClusterInfo, err := config.getCluster()
+	if err != nil {
+		return nil, err
+	}
+
+	if err := config.ConfirmUsable(); err != nil {
+		return nil, err
+	}
+
+	clientConfig := &restclient.Config{}
+	clientConfig.Host = configClusterInfo.Server
+
+	if len(config.overrides.Timeout) > 0 {
+		timeout, err := ParseTimeout(config.overrides.Timeout)
+		if err != nil {
+			return nil, err
+		}
+		clientConfig.Timeout = timeout
+	}
+
+	if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
+		u.RawQuery = ""
+		u.Fragment = ""
+		clientConfig.Host = u.String()
+	}
+	if len(configAuthInfo.Impersonate) > 0 {
+		clientConfig.Impersonate = restclient.ImpersonationConfig{
+			UserName: configAuthInfo.Impersonate,
+			Groups:   configAuthInfo.ImpersonateGroups,
+			Extra:    configAuthInfo.ImpersonateUserExtra,
+		}
+	}
+
+	// only try to read the auth information if we are secure
+	if restclient.IsConfigTransportTLS(*clientConfig) {
+		var err error
+		var persister restclient.AuthProviderConfigPersister
+		if config.configAccess != nil {
+			authInfoName, _ := config.getAuthInfoName()
+			persister = PersisterForUser(config.configAccess, authInfoName)
+		}
+		userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister)
+		if err != nil {
+			return nil, err
+		}
+		mergo.MergeWithOverwrite(clientConfig, userAuthPartialConfig)
+
+		serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo)
+		if err != nil {
+			return nil, err
+		}
+		mergo.MergeWithOverwrite(clientConfig, serverAuthPartialConfig)
+	}
+
+	return clientConfig, nil
+}
+
+// clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
+// both, so we have to split the objects and merge them separately
+// we want this order of precedence for the server identification
+// 1.  configClusterInfo (the final result of command line flags and merged .kubeconfig files)
+// 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
+// 3.  load the ~/.kubernetes_auth file as a default
+func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
+	mergedConfig := &restclient.Config{}
+
+	// configClusterInfo holds the information identify the server provided by .kubeconfig
+	configClientConfig := &restclient.Config{}
+	configClientConfig.CAFile = configClusterInfo.CertificateAuthority
+	configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
+	configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
+	mergo.MergeWithOverwrite(mergedConfig, configClientConfig)
+
+	return mergedConfig, nil
+}
+
+// clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
+// both, so we have to split the objects and merge them separately
+// we want this order of precedence for user identification
+// 1.  configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files)
+// 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
+// 3.  if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
+// 4.  if there is not enough information to identify the user, prompt if possible
+func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister) (*restclient.Config, error) {
+	mergedConfig := &restclient.Config{}
+
+	// blindly overwrite existing values based on precedence
+	if len(configAuthInfo.Token) > 0 {
+		mergedConfig.BearerToken = configAuthInfo.Token
+	} else if len(configAuthInfo.TokenFile) > 0 {
+		ts := restclient.NewCachedFileTokenSource(configAuthInfo.TokenFile)
+		if _, err := ts.Token(); err != nil {
+			return nil, err
+		}
+		mergedConfig.WrapTransport = restclient.TokenSourceWrapTransport(ts)
+	}
+	if len(configAuthInfo.Impersonate) > 0 {
+		mergedConfig.Impersonate = restclient.ImpersonationConfig{
+			UserName: configAuthInfo.Impersonate,
+			Groups:   configAuthInfo.ImpersonateGroups,
+			Extra:    configAuthInfo.ImpersonateUserExtra,
+		}
+	}
+	if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
+		mergedConfig.CertFile = configAuthInfo.ClientCertificate
+		mergedConfig.CertData = configAuthInfo.ClientCertificateData
+		mergedConfig.KeyFile = configAuthInfo.ClientKey
+		mergedConfig.KeyData = configAuthInfo.ClientKeyData
+	}
+	if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
+		mergedConfig.Username = configAuthInfo.Username
+		mergedConfig.Password = configAuthInfo.Password
+	}
+	if configAuthInfo.AuthProvider != nil {
+		mergedConfig.AuthProvider = configAuthInfo.AuthProvider
+		mergedConfig.AuthConfigPersister = persistAuthConfig
+	}
+	if configAuthInfo.Exec != nil {
+		mergedConfig.ExecProvider = configAuthInfo.Exec
+	}
+
+	// if there still isn't enough information to authenticate the user, try prompting
+	if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
+		if len(config.promptedCredentials.username) > 0 && len(config.promptedCredentials.password) > 0 {
+			mergedConfig.Username = config.promptedCredentials.username
+			mergedConfig.Password = config.promptedCredentials.password
+			return mergedConfig, nil
+		}
+		prompter := NewPromptingAuthLoader(fallbackReader)
+		promptedAuthInfo, err := prompter.Prompt()
+		if err != nil {
+			return nil, err
+		}
+		promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo)
+		previouslyMergedConfig := mergedConfig
+		mergedConfig = &restclient.Config{}
+		mergo.MergeWithOverwrite(mergedConfig, promptedConfig)
+		mergo.MergeWithOverwrite(mergedConfig, previouslyMergedConfig)
+		config.promptedCredentials.username = mergedConfig.Username
+		config.promptedCredentials.password = mergedConfig.Password
+	}
+
+	return mergedConfig, nil
+}
+
+// makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
+func makeUserIdentificationConfig(info clientauth.Info) *restclient.Config {
+	config := &restclient.Config{}
+	config.Username = info.User
+	config.Password = info.Password
+	config.CertFile = info.CertFile
+	config.KeyFile = info.KeyFile
+	config.BearerToken = info.BearerToken
+	return config
+}
+
+// makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only server identification information
+func makeServerIdentificationConfig(info clientauth.Info) restclient.Config {
+	config := restclient.Config{}
+	config.CAFile = info.CAFile
+	if info.Insecure != nil {
+		config.Insecure = *info.Insecure
+	}
+	return config
+}
+
+func canIdentifyUser(config restclient.Config) bool {
+	return len(config.Username) > 0 ||
+		(len(config.CertFile) > 0 || len(config.CertData) > 0) ||
+		len(config.BearerToken) > 0 ||
+		config.AuthProvider != nil ||
+		config.ExecProvider != nil
+}
+
+// Namespace implements ClientConfig
+func (config *DirectClientConfig) Namespace() (string, bool, error) {
+	if config.overrides != nil && config.overrides.Context.Namespace != "" {
+		// In the event we have an empty config but we do have a namespace override, we should return
+		// the namespace override instead of having config.ConfirmUsable() return an error. This allows
+		// things like in-cluster clients to execute `kubectl get pods --namespace=foo` and have the
+		// --namespace flag honored instead of being ignored.
+		return config.overrides.Context.Namespace, true, nil
+	}
+
+	if err := config.ConfirmUsable(); err != nil {
+		return "", false, err
+	}
+
+	configContext, err := config.getContext()
+	if err != nil {
+		return "", false, err
+	}
+
+	if len(configContext.Namespace) == 0 {
+		return "default", false, nil
+	}
+
+	return configContext.Namespace, false, nil
+}
+
+// ConfigAccess implements ClientConfig
+func (config *DirectClientConfig) ConfigAccess() ConfigAccess {
+	return config.configAccess
+}
+
+// ConfirmUsable looks a particular context and determines if that particular part of the config is useable.  There might still be errors in the config,
+// but no errors in the sections requested or referenced.  It does not return early so that it can find as many errors as possible.
+func (config *DirectClientConfig) ConfirmUsable() error {
+	validationErrors := make([]error, 0)
+
+	var contextName string
+	if len(config.contextName) != 0 {
+		contextName = config.contextName
+	} else {
+		contextName = config.config.CurrentContext
+	}
+
+	if len(contextName) > 0 {
+		_, exists := config.config.Contexts[contextName]
+		if !exists {
+			validationErrors = append(validationErrors, &errContextNotFound{contextName})
+		}
+	}
+
+	authInfoName, _ := config.getAuthInfoName()
+	authInfo, _ := config.getAuthInfo()
+	validationErrors = append(validationErrors, validateAuthInfo(authInfoName, authInfo)...)
+	clusterName, _ := config.getClusterName()
+	cluster, _ := config.getCluster()
+	validationErrors = append(validationErrors, validateClusterInfo(clusterName, cluster)...)
+	// when direct client config is specified, and our only error is that no server is defined, we should
+	// return a standard "no config" error
+	if len(validationErrors) == 1 && validationErrors[0] == ErrEmptyCluster {
+		return newErrConfigurationInvalid([]error{ErrEmptyConfig})
+	}
+	return newErrConfigurationInvalid(validationErrors)
+}
+
+// getContextName returns the default, or user-set context name, and a boolean that indicates
+// whether the default context name has been overwritten by a user-set flag, or left as its default value
+func (config *DirectClientConfig) getContextName() (string, bool) {
+	if len(config.overrides.CurrentContext) != 0 {
+		return config.overrides.CurrentContext, true
+	}
+	if len(config.contextName) != 0 {
+		return config.contextName, false
+	}
+
+	return config.config.CurrentContext, false
+}
+
+// getAuthInfoName returns a string containing the current authinfo name for the current context,
+// and a boolean indicating  whether the default authInfo name is overwritten by a user-set flag, or
+// left as its default value
+func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
+	if len(config.overrides.Context.AuthInfo) != 0 {
+		return config.overrides.Context.AuthInfo, true
+	}
+	context, _ := config.getContext()
+	return context.AuthInfo, false
+}
+
+// getClusterName returns a string containing the default, or user-set cluster name, and a boolean
+// indicating whether the default clusterName has been overwritten by a user-set flag, or left as
+// its default value
+func (config *DirectClientConfig) getClusterName() (string, bool) {
+	if len(config.overrides.Context.Cluster) != 0 {
+		return config.overrides.Context.Cluster, true
+	}
+	context, _ := config.getContext()
+	return context.Cluster, false
+}
+
+// getContext returns the clientcmdapi.Context, or an error if a required context is not found.
+func (config *DirectClientConfig) getContext() (clientcmdapi.Context, error) {
+	contexts := config.config.Contexts
+	contextName, required := config.getContextName()
+
+	mergedContext := clientcmdapi.NewContext()
+	if configContext, exists := contexts[contextName]; exists {
+		mergo.MergeWithOverwrite(mergedContext, configContext)
+	} else if required {
+		return clientcmdapi.Context{}, fmt.Errorf("context %q does not exist", contextName)
+	}
+	mergo.MergeWithOverwrite(mergedContext, config.overrides.Context)
+
+	return *mergedContext, nil
+}
+
+// getAuthInfo returns the clientcmdapi.AuthInfo, or an error if a required auth info is not found.
+func (config *DirectClientConfig) getAuthInfo() (clientcmdapi.AuthInfo, error) {
+	authInfos := config.config.AuthInfos
+	authInfoName, required := config.getAuthInfoName()
+
+	mergedAuthInfo := clientcmdapi.NewAuthInfo()
+	if configAuthInfo, exists := authInfos[authInfoName]; exists {
+		mergo.MergeWithOverwrite(mergedAuthInfo, configAuthInfo)
+	} else if required {
+		return clientcmdapi.AuthInfo{}, fmt.Errorf("auth info %q does not exist", authInfoName)
+	}
+	mergo.MergeWithOverwrite(mergedAuthInfo, config.overrides.AuthInfo)
+
+	return *mergedAuthInfo, nil
+}
+
+// getCluster returns the clientcmdapi.Cluster, or an error if a required cluster is not found.
+func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
+	clusterInfos := config.config.Clusters
+	clusterInfoName, required := config.getClusterName()
+
+	mergedClusterInfo := clientcmdapi.NewCluster()
+	mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterDefaults)
+	if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
+		mergo.MergeWithOverwrite(mergedClusterInfo, configClusterInfo)
+	} else if required {
+		return clientcmdapi.Cluster{}, fmt.Errorf("cluster %q does not exist", clusterInfoName)
+	}
+	mergo.MergeWithOverwrite(mergedClusterInfo, config.overrides.ClusterInfo)
+	// An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
+	// otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set"
+	caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
+	caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
+	if config.overrides.ClusterInfo.InsecureSkipTLSVerify && caLen == 0 && caDataLen == 0 {
+		mergedClusterInfo.CertificateAuthority = ""
+		mergedClusterInfo.CertificateAuthorityData = nil
+	}
+
+	return *mergedClusterInfo, nil
+}
+
+// inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
+// Can take options overrides for flags explicitly provided to the command inside the cluster container.
+type inClusterClientConfig struct {
+	overrides               *ConfigOverrides
+	inClusterConfigProvider func() (*restclient.Config, error)
+}
+
+var _ ClientConfig = &inClusterClientConfig{}
+
+func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) {
+	return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters")
+}
+
+func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) {
+	if config.inClusterConfigProvider == nil {
+		config.inClusterConfigProvider = restclient.InClusterConfig
+	}
+
+	icc, err := config.inClusterConfigProvider()
+	if err != nil {
+		return nil, err
+	}
+
+	// in-cluster configs only takes a host, token, or CA file
+	// if any of them were individually provided, overwrite anything else
+	if config.overrides != nil {
+		if server := config.overrides.ClusterInfo.Server; len(server) > 0 {
+			icc.Host = server
+		}
+		if token := config.overrides.AuthInfo.Token; len(token) > 0 {
+			icc.BearerToken = token
+		}
+		if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 {
+			icc.TLSClientConfig.CAFile = certificateAuthorityFile
+		}
+	}
+
+	return icc, err
+}
+
+func (config *inClusterClientConfig) Namespace() (string, bool, error) {
+	// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
+	// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
+	if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
+		return ns, false, nil
+	}
+
+	// Fall back to the namespace associated with the service account token, if available
+	if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
+		if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
+			return ns, false, nil
+		}
+	}
+
+	return "default", false, nil
+}
+
+func (config *inClusterClientConfig) ConfigAccess() ConfigAccess {
+	return NewDefaultClientConfigLoadingRules()
+}
+
+// Possible returns true if loading an inside-kubernetes-cluster is possible.
+func (config *inClusterClientConfig) Possible() bool {
+	fi, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token")
+	return os.Getenv("KUBERNETES_SERVICE_HOST") != "" &&
+		os.Getenv("KUBERNETES_SERVICE_PORT") != "" &&
+		err == nil && !fi.IsDir()
+}
+
+// BuildConfigFromFlags is a helper function that builds configs from a master
+// url or a kubeconfig filepath. These are passed in as command line flags for cluster
+// components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath
+// are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback
+// to the default config.
+func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
+	if kubeconfigPath == "" && masterUrl == "" {
+		klog.Warningf("Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.")
+		kubeconfig, err := restclient.InClusterConfig()
+		if err == nil {
+			return kubeconfig, nil
+		}
+		klog.Warning("error creating inClusterConfig, falling back to default config: ", err)
+	}
+	return NewNonInteractiveDeferredLoadingClientConfig(
+		&ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
+		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}}).ClientConfig()
+}
+
+// BuildConfigFromKubeconfigGetter is a helper function that builds configs from a master
+// url and a kubeconfigGetter.
+func BuildConfigFromKubeconfigGetter(masterUrl string, kubeconfigGetter KubeconfigGetter) (*restclient.Config, error) {
+	// TODO: We do not need a DeferredLoader here. Refactor code and see if we can use DirectClientConfig here.
+	cc := NewNonInteractiveDeferredLoadingClientConfig(
+		&ClientConfigGetter{kubeconfigGetter: kubeconfigGetter},
+		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}})
+	return cc.ClientConfig()
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/config.go b/vendor/k8s.io/client-go/tools/clientcmd/config.go
new file mode 100644
index 0000000..b8cc396
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/config.go
@@ -0,0 +1,490 @@
+/*
+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 clientcmd
+
+import (
+	"errors"
+	"os"
+	"path"
+	"path/filepath"
+	"reflect"
+	"sort"
+
+	"k8s.io/klog"
+
+	restclient "k8s.io/client-go/rest"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+// ConfigAccess is used by subcommands and methods in this package to load and modify the appropriate config files
+type ConfigAccess interface {
+	// GetLoadingPrecedence returns the slice of files that should be used for loading and inspecting the config
+	GetLoadingPrecedence() []string
+	// GetStartingConfig returns the config that subcommands should being operating against.  It may or may not be merged depending on loading rules
+	GetStartingConfig() (*clientcmdapi.Config, error)
+	// GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create a new stanza as opposed to updating an existing one.
+	GetDefaultFilename() string
+	// IsExplicitFile indicates whether or not this command is interested in exactly one file.  This implementation only ever does that  via a flag, but implementations that handle local, global, and flags may have more
+	IsExplicitFile() bool
+	// GetExplicitFile returns the particular file this command is operating against.  This implementation only ever has one, but implementations that handle local, global, and flags may have more
+	GetExplicitFile() string
+}
+
+type PathOptions struct {
+	// GlobalFile is the full path to the file to load as the global (final) option
+	GlobalFile string
+	// EnvVar is the env var name that points to the list of kubeconfig files to load
+	EnvVar string
+	// ExplicitFileFlag is the name of the flag to use for prompting for the kubeconfig file
+	ExplicitFileFlag string
+
+	// GlobalFileSubpath is an optional value used for displaying help
+	GlobalFileSubpath string
+
+	LoadingRules *ClientConfigLoadingRules
+}
+
+func (o *PathOptions) GetEnvVarFiles() []string {
+	if len(o.EnvVar) == 0 {
+		return []string{}
+	}
+
+	envVarValue := os.Getenv(o.EnvVar)
+	if len(envVarValue) == 0 {
+		return []string{}
+	}
+
+	fileList := filepath.SplitList(envVarValue)
+	// prevent the same path load multiple times
+	return deduplicate(fileList)
+}
+
+func (o *PathOptions) GetLoadingPrecedence() []string {
+	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
+		return envVarFiles
+	}
+
+	return []string{o.GlobalFile}
+}
+
+func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) {
+	// don't mutate the original
+	loadingRules := *o.LoadingRules
+	loadingRules.Precedence = o.GetLoadingPrecedence()
+
+	clientConfig := NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &ConfigOverrides{})
+	rawConfig, err := clientConfig.RawConfig()
+	if os.IsNotExist(err) {
+		return clientcmdapi.NewConfig(), nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return &rawConfig, nil
+}
+
+func (o *PathOptions) GetDefaultFilename() string {
+	if o.IsExplicitFile() {
+		return o.GetExplicitFile()
+	}
+
+	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
+		if len(envVarFiles) == 1 {
+			return envVarFiles[0]
+		}
+
+		// if any of the envvar files already exists, return it
+		for _, envVarFile := range envVarFiles {
+			if _, err := os.Stat(envVarFile); err == nil {
+				return envVarFile
+			}
+		}
+
+		// otherwise, return the last one in the list
+		return envVarFiles[len(envVarFiles)-1]
+	}
+
+	return o.GlobalFile
+}
+
+func (o *PathOptions) IsExplicitFile() bool {
+	if len(o.LoadingRules.ExplicitPath) > 0 {
+		return true
+	}
+
+	return false
+}
+
+func (o *PathOptions) GetExplicitFile() string {
+	return o.LoadingRules.ExplicitPath
+}
+
+func NewDefaultPathOptions() *PathOptions {
+	ret := &PathOptions{
+		GlobalFile:       RecommendedHomeFile,
+		EnvVar:           RecommendedConfigPathEnvVar,
+		ExplicitFileFlag: RecommendedConfigPathFlag,
+
+		GlobalFileSubpath: path.Join(RecommendedHomeDir, RecommendedFileName),
+
+		LoadingRules: NewDefaultClientConfigLoadingRules(),
+	}
+	ret.LoadingRules.DoNotResolvePaths = true
+
+	return ret
+}
+
+// ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or
+// uses the default destination file to write the results into.  This results in multiple file reads, but it's very easy to follow.
+// Preferences and CurrentContext should always be set in the default destination file.  Since we can't distinguish between empty and missing values
+// (no nil strings), we're forced have separate handling for them.  In the kubeconfig cases, newConfig should have at most one difference,
+// that means that this code will only write into a single file.  If you want to relativizePaths, you must provide a fully qualified path in any
+// modified element.
+func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
+	possibleSources := configAccess.GetLoadingPrecedence()
+	// sort the possible kubeconfig files so we always "lock" in the same order
+	// to avoid deadlock (note: this can fail w/ symlinks, but... come on).
+	sort.Strings(possibleSources)
+	for _, filename := range possibleSources {
+		if err := lockFile(filename); err != nil {
+			return err
+		}
+		defer unlockFile(filename)
+	}
+
+	startingConfig, err := configAccess.GetStartingConfig()
+	if err != nil {
+		return err
+	}
+
+	// We need to find all differences, locate their original files, read a partial config to modify only that stanza and write out the file.
+	// Special case the test for current context and preferences since those always write to the default file.
+	if reflect.DeepEqual(*startingConfig, newConfig) {
+		// nothing to do
+		return nil
+	}
+
+	if startingConfig.CurrentContext != newConfig.CurrentContext {
+		if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil {
+			return err
+		}
+	}
+
+	if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) {
+		if err := writePreferences(configAccess, newConfig.Preferences); err != nil {
+			return err
+		}
+	}
+
+	// Search every cluster, authInfo, and context.  First from new to old for differences, then from old to new for deletions
+	for key, cluster := range newConfig.Clusters {
+		startingCluster, exists := startingConfig.Clusters[key]
+		if !reflect.DeepEqual(cluster, startingCluster) || !exists {
+			destinationFile := cluster.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			configToWrite, err := getConfigFromFile(destinationFile)
+			if err != nil {
+				return err
+			}
+			t := *cluster
+
+			configToWrite.Clusters[key] = &t
+			configToWrite.Clusters[key].LocationOfOrigin = destinationFile
+			if relativizePaths {
+				if err := RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil {
+					return err
+				}
+			}
+
+			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+				return err
+			}
+		}
+	}
+
+	// seenConfigs stores a map of config source filenames to computed config objects
+	seenConfigs := map[string]*clientcmdapi.Config{}
+
+	for key, context := range newConfig.Contexts {
+		startingContext, exists := startingConfig.Contexts[key]
+		if !reflect.DeepEqual(context, startingContext) || !exists {
+			destinationFile := context.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			// we only obtain a fresh config object from its source file
+			// if we have not seen it already - this prevents us from
+			// reading and writing to the same number of files repeatedly
+			// when multiple / all contexts share the same destination file.
+			configToWrite, seen := seenConfigs[destinationFile]
+			if !seen {
+				var err error
+				configToWrite, err = getConfigFromFile(destinationFile)
+				if err != nil {
+					return err
+				}
+				seenConfigs[destinationFile] = configToWrite
+			}
+
+			configToWrite.Contexts[key] = context
+		}
+	}
+
+	// actually persist config object changes
+	for destinationFile, configToWrite := range seenConfigs {
+		if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+			return err
+		}
+	}
+
+	for key, authInfo := range newConfig.AuthInfos {
+		startingAuthInfo, exists := startingConfig.AuthInfos[key]
+		if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists {
+			destinationFile := authInfo.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			configToWrite, err := getConfigFromFile(destinationFile)
+			if err != nil {
+				return err
+			}
+			t := *authInfo
+			configToWrite.AuthInfos[key] = &t
+			configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile
+			if relativizePaths {
+				if err := RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil {
+					return err
+				}
+			}
+
+			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+				return err
+			}
+		}
+	}
+
+	for key, cluster := range startingConfig.Clusters {
+		if _, exists := newConfig.Clusters[key]; !exists {
+			destinationFile := cluster.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			configToWrite, err := getConfigFromFile(destinationFile)
+			if err != nil {
+				return err
+			}
+			delete(configToWrite.Clusters, key)
+
+			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+				return err
+			}
+		}
+	}
+
+	for key, context := range startingConfig.Contexts {
+		if _, exists := newConfig.Contexts[key]; !exists {
+			destinationFile := context.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			configToWrite, err := getConfigFromFile(destinationFile)
+			if err != nil {
+				return err
+			}
+			delete(configToWrite.Contexts, key)
+
+			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+				return err
+			}
+		}
+	}
+
+	for key, authInfo := range startingConfig.AuthInfos {
+		if _, exists := newConfig.AuthInfos[key]; !exists {
+			destinationFile := authInfo.LocationOfOrigin
+			if len(destinationFile) == 0 {
+				destinationFile = configAccess.GetDefaultFilename()
+			}
+
+			configToWrite, err := getConfigFromFile(destinationFile)
+			if err != nil {
+				return err
+			}
+			delete(configToWrite.AuthInfos, key)
+
+			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func PersisterForUser(configAccess ConfigAccess, user string) restclient.AuthProviderConfigPersister {
+	return &persister{configAccess, user}
+}
+
+type persister struct {
+	configAccess ConfigAccess
+	user         string
+}
+
+func (p *persister) Persist(config map[string]string) error {
+	newConfig, err := p.configAccess.GetStartingConfig()
+	if err != nil {
+		return err
+	}
+	authInfo, ok := newConfig.AuthInfos[p.user]
+	if ok && authInfo.AuthProvider != nil {
+		authInfo.AuthProvider.Config = config
+		ModifyConfig(p.configAccess, *newConfig, false)
+	}
+	return nil
+}
+
+// writeCurrentContext takes three possible paths.
+// If newCurrentContext is the same as the startingConfig's current context, then we exit.
+// If newCurrentContext has a value, then that value is written into the default destination file.
+// If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file
+func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error {
+	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
+		return err
+	} else if startingConfig.CurrentContext == newCurrentContext {
+		return nil
+	}
+
+	if configAccess.IsExplicitFile() {
+		file := configAccess.GetExplicitFile()
+		currConfig, err := getConfigFromFile(file)
+		if err != nil {
+			return err
+		}
+		currConfig.CurrentContext = newCurrentContext
+		if err := WriteToFile(*currConfig, file); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	if len(newCurrentContext) > 0 {
+		destinationFile := configAccess.GetDefaultFilename()
+		config, err := getConfigFromFile(destinationFile)
+		if err != nil {
+			return err
+		}
+		config.CurrentContext = newCurrentContext
+
+		if err := WriteToFile(*config, destinationFile); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	// we're supposed to be clearing the current context.  We need to find the first spot in the chain that is setting it and clear it
+	for _, file := range configAccess.GetLoadingPrecedence() {
+		if _, err := os.Stat(file); err == nil {
+			currConfig, err := getConfigFromFile(file)
+			if err != nil {
+				return err
+			}
+
+			if len(currConfig.CurrentContext) > 0 {
+				currConfig.CurrentContext = newCurrentContext
+				if err := WriteToFile(*currConfig, file); err != nil {
+					return err
+				}
+
+				return nil
+			}
+		}
+	}
+
+	return errors.New("no config found to write context")
+}
+
+func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error {
+	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
+		return err
+	} else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) {
+		return nil
+	}
+
+	if configAccess.IsExplicitFile() {
+		file := configAccess.GetExplicitFile()
+		currConfig, err := getConfigFromFile(file)
+		if err != nil {
+			return err
+		}
+		currConfig.Preferences = newPrefs
+		if err := WriteToFile(*currConfig, file); err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	for _, file := range configAccess.GetLoadingPrecedence() {
+		currConfig, err := getConfigFromFile(file)
+		if err != nil {
+			return err
+		}
+
+		if !reflect.DeepEqual(currConfig.Preferences, newPrefs) {
+			currConfig.Preferences = newPrefs
+			if err := WriteToFile(*currConfig, file); err != nil {
+				return err
+			}
+
+			return nil
+		}
+	}
+
+	return errors.New("no config found to write preferences")
+}
+
+// getConfigFromFile tries to read a kubeconfig file and if it can't, returns an error.  One exception, missing files result in empty configs, not an error.
+func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
+	config, err := LoadFromFile(filename)
+	if err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
+	if config == nil {
+		config = clientcmdapi.NewConfig()
+	}
+	return config, nil
+}
+
+// GetConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit.  One exception, missing files result in empty configs, not an exit
+func GetConfigFromFileOrDie(filename string) *clientcmdapi.Config {
+	config, err := getConfigFromFile(filename)
+	if err != nil {
+		klog.FatalDepth(1, err)
+	}
+
+	return config
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/doc.go b/vendor/k8s.io/client-go/tools/clientcmd/doc.go
new file mode 100644
index 0000000..424311e
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/doc.go
@@ -0,0 +1,37 @@
+/*
+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 clientcmd provides one stop shopping for building a working client from a fixed config,
+from a .kubeconfig file, from command line flags, or from any merged combination.
+
+Sample usage from merged .kubeconfig files (local directory, home directory)
+
+	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
+	// if you want to change the loading rules (which files in which order), you can do so here
+
+	configOverrides := &clientcmd.ConfigOverrides{}
+	// if you want to change override values or bind them to flags, there are methods to help you
+
+	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)
+	config, err := kubeConfig.ClientConfig()
+	if err != nil {
+		// Do something
+	}
+	client, err := metav1.New(config)
+	// ...
+*/
+package clientcmd // import "k8s.io/client-go/tools/clientcmd"
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/flag.go b/vendor/k8s.io/client-go/tools/clientcmd/flag.go
new file mode 100644
index 0000000..8d60d20
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/flag.go
@@ -0,0 +1,49 @@
+/*
+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 clientcmd
+
+// transformingStringValue implements pflag.Value to store string values,
+// allowing transforming them while being set
+type transformingStringValue struct {
+	target      *string
+	transformer func(string) (string, error)
+}
+
+func newTransformingStringValue(val string, target *string, transformer func(string) (string, error)) *transformingStringValue {
+	*target = val
+	return &transformingStringValue{
+		target:      target,
+		transformer: transformer,
+	}
+}
+
+func (t *transformingStringValue) Set(val string) error {
+	val, err := t.transformer(val)
+	if err != nil {
+		return err
+	}
+	*t.target = val
+	return nil
+}
+
+func (t *transformingStringValue) Type() string {
+	return "string"
+}
+
+func (t *transformingStringValue) String() string {
+	return string(*t.target)
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/helpers.go b/vendor/k8s.io/client-go/tools/clientcmd/helpers.go
new file mode 100644
index 0000000..b609d1a
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/helpers.go
@@ -0,0 +1,35 @@
+/*
+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 clientcmd
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+)
+
+// ParseTimeout returns a parsed duration from a string
+// A duration string value must be a positive integer, optionally followed by a corresponding time unit (s|m|h).
+func ParseTimeout(duration string) (time.Duration, error) {
+	if i, err := strconv.ParseInt(duration, 10, 64); err == nil && i >= 0 {
+		return (time.Duration(i) * time.Second), nil
+	}
+	if requestTimeout, err := time.ParseDuration(duration); err == nil {
+		return requestTimeout, nil
+	}
+	return 0, fmt.Errorf("Invalid timeout value. Timeout must be a single integer in seconds, or an integer followed by a corresponding time unit (e.g. 1s | 2m | 3h)")
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/loader.go b/vendor/k8s.io/client-go/tools/clientcmd/loader.go
new file mode 100644
index 0000000..7e928a9
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/loader.go
@@ -0,0 +1,633 @@
+/*
+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 clientcmd
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path"
+	"path/filepath"
+	"reflect"
+	goruntime "runtime"
+	"strings"
+
+	"github.com/imdario/mergo"
+	"k8s.io/klog"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilerrors "k8s.io/apimachinery/pkg/util/errors"
+	restclient "k8s.io/client-go/rest"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+	clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"
+	"k8s.io/client-go/util/homedir"
+)
+
+const (
+	RecommendedConfigPathFlag   = "kubeconfig"
+	RecommendedConfigPathEnvVar = "KUBECONFIG"
+	RecommendedHomeDir          = ".kube"
+	RecommendedFileName         = "config"
+	RecommendedSchemaName       = "schema"
+)
+
+var (
+	RecommendedConfigDir  = path.Join(homedir.HomeDir(), RecommendedHomeDir)
+	RecommendedHomeFile   = path.Join(RecommendedConfigDir, RecommendedFileName)
+	RecommendedSchemaFile = path.Join(RecommendedConfigDir, RecommendedSchemaName)
+)
+
+// currentMigrationRules returns a map that holds the history of recommended home directories used in previous versions.
+// Any future changes to RecommendedHomeFile and related are expected to add a migration rule here, in order to make
+// sure existing config files are migrated to their new locations properly.
+func currentMigrationRules() map[string]string {
+	oldRecommendedHomeFile := path.Join(os.Getenv("HOME"), "/.kube/.kubeconfig")
+	oldRecommendedWindowsHomeFile := path.Join(os.Getenv("HOME"), RecommendedHomeDir, RecommendedFileName)
+
+	migrationRules := map[string]string{}
+	migrationRules[RecommendedHomeFile] = oldRecommendedHomeFile
+	if goruntime.GOOS == "windows" {
+		migrationRules[RecommendedHomeFile] = oldRecommendedWindowsHomeFile
+	}
+	return migrationRules
+}
+
+type ClientConfigLoader interface {
+	ConfigAccess
+	// IsDefaultConfig returns true if the returned config matches the defaults.
+	IsDefaultConfig(*restclient.Config) bool
+	// Load returns the latest config
+	Load() (*clientcmdapi.Config, error)
+}
+
+type KubeconfigGetter func() (*clientcmdapi.Config, error)
+
+type ClientConfigGetter struct {
+	kubeconfigGetter KubeconfigGetter
+}
+
+// ClientConfigGetter implements the ClientConfigLoader interface.
+var _ ClientConfigLoader = &ClientConfigGetter{}
+
+func (g *ClientConfigGetter) Load() (*clientcmdapi.Config, error) {
+	return g.kubeconfigGetter()
+}
+
+func (g *ClientConfigGetter) GetLoadingPrecedence() []string {
+	return nil
+}
+func (g *ClientConfigGetter) GetStartingConfig() (*clientcmdapi.Config, error) {
+	return g.kubeconfigGetter()
+}
+func (g *ClientConfigGetter) GetDefaultFilename() string {
+	return ""
+}
+func (g *ClientConfigGetter) IsExplicitFile() bool {
+	return false
+}
+func (g *ClientConfigGetter) GetExplicitFile() string {
+	return ""
+}
+func (g *ClientConfigGetter) IsDefaultConfig(config *restclient.Config) bool {
+	return false
+}
+
+// ClientConfigLoadingRules is an ExplicitPath and string slice of specific locations that are used for merging together a Config
+// Callers can put the chain together however they want, but we'd recommend:
+// EnvVarPathFiles if set (a list of files if set) OR the HomeDirectoryPath
+// ExplicitPath is special, because if a user specifically requests a certain file be used and error is reported if this file is not present
+type ClientConfigLoadingRules struct {
+	ExplicitPath string
+	Precedence   []string
+
+	// MigrationRules is a map of destination files to source files.  If a destination file is not present, then the source file is checked.
+	// If the source file is present, then it is copied to the destination file BEFORE any further loading happens.
+	MigrationRules map[string]string
+
+	// DoNotResolvePaths indicates whether or not to resolve paths with respect to the originating files.  This is phrased as a negative so
+	// that a default object that doesn't set this will usually get the behavior it wants.
+	DoNotResolvePaths bool
+
+	// DefaultClientConfig is an optional field indicating what rules to use to calculate a default configuration.
+	// This should match the overrides passed in to ClientConfig loader.
+	DefaultClientConfig ClientConfig
+}
+
+// ClientConfigLoadingRules implements the ClientConfigLoader interface.
+var _ ClientConfigLoader = &ClientConfigLoadingRules{}
+
+// NewDefaultClientConfigLoadingRules returns a ClientConfigLoadingRules object with default fields filled in.  You are not required to
+// use this constructor
+func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules {
+	chain := []string{}
+
+	envVarFiles := os.Getenv(RecommendedConfigPathEnvVar)
+	if len(envVarFiles) != 0 {
+		fileList := filepath.SplitList(envVarFiles)
+		// prevent the same path load multiple times
+		chain = append(chain, deduplicate(fileList)...)
+
+	} else {
+		chain = append(chain, RecommendedHomeFile)
+	}
+
+	return &ClientConfigLoadingRules{
+		Precedence:     chain,
+		MigrationRules: currentMigrationRules(),
+	}
+}
+
+// Load starts by running the MigrationRules and then
+// takes the loading rules and returns a Config object based on following rules.
+//   if the ExplicitPath, return the unmerged explicit file
+//   Otherwise, return a merged config based on the Precedence slice
+// A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored.
+// Read errors or files with non-deserializable content produce errors.
+// The first file to set a particular map key wins and map key's value is never changed.
+// BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed.
+// This results in some odd looking logic to merge in one direction, merge in the other, and then merge the two.
+// It also means that if two files specify a "red-user", only values from the first file's red-user are used.  Even
+// non-conflicting entries from the second file's "red-user" are discarded.
+// Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder
+// and only absolute file paths are returned.
+func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) {
+	if err := rules.Migrate(); err != nil {
+		return nil, err
+	}
+
+	errlist := []error{}
+
+	kubeConfigFiles := []string{}
+
+	// Make sure a file we were explicitly told to use exists
+	if len(rules.ExplicitPath) > 0 {
+		if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) {
+			return nil, err
+		}
+		kubeConfigFiles = append(kubeConfigFiles, rules.ExplicitPath)
+
+	} else {
+		kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...)
+	}
+
+	kubeconfigs := []*clientcmdapi.Config{}
+	// read and cache the config files so that we only look at them once
+	for _, filename := range kubeConfigFiles {
+		if len(filename) == 0 {
+			// no work to do
+			continue
+		}
+
+		config, err := LoadFromFile(filename)
+		if os.IsNotExist(err) {
+			// skip missing files
+			continue
+		}
+		if err != nil {
+			errlist = append(errlist, fmt.Errorf("Error loading config file \"%s\": %v", filename, err))
+			continue
+		}
+
+		kubeconfigs = append(kubeconfigs, config)
+	}
+
+	// first merge all of our maps
+	mapConfig := clientcmdapi.NewConfig()
+
+	for _, kubeconfig := range kubeconfigs {
+		mergo.MergeWithOverwrite(mapConfig, kubeconfig)
+	}
+
+	// merge all of the struct values in the reverse order so that priority is given correctly
+	// errors are not added to the list the second time
+	nonMapConfig := clientcmdapi.NewConfig()
+	for i := len(kubeconfigs) - 1; i >= 0; i-- {
+		kubeconfig := kubeconfigs[i]
+		mergo.MergeWithOverwrite(nonMapConfig, kubeconfig)
+	}
+
+	// since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and
+	// get the values we expect.
+	config := clientcmdapi.NewConfig()
+	mergo.MergeWithOverwrite(config, mapConfig)
+	mergo.MergeWithOverwrite(config, nonMapConfig)
+
+	if rules.ResolvePaths() {
+		if err := ResolveLocalPaths(config); err != nil {
+			errlist = append(errlist, err)
+		}
+	}
+	return config, utilerrors.NewAggregate(errlist)
+}
+
+// Migrate uses the MigrationRules map.  If a destination file is not present, then the source file is checked.
+// If the source file is present, then it is copied to the destination file BEFORE any further loading happens.
+func (rules *ClientConfigLoadingRules) Migrate() error {
+	if rules.MigrationRules == nil {
+		return nil
+	}
+
+	for destination, source := range rules.MigrationRules {
+		if _, err := os.Stat(destination); err == nil {
+			// if the destination already exists, do nothing
+			continue
+		} else if os.IsPermission(err) {
+			// if we can't access the file, skip it
+			continue
+		} else if !os.IsNotExist(err) {
+			// if we had an error other than non-existence, fail
+			return err
+		}
+
+		if sourceInfo, err := os.Stat(source); err != nil {
+			if os.IsNotExist(err) || os.IsPermission(err) {
+				// if the source file doesn't exist or we can't access it, there's no work to do.
+				continue
+			}
+
+			// if we had an error other than non-existence, fail
+			return err
+		} else if sourceInfo.IsDir() {
+			return fmt.Errorf("cannot migrate %v to %v because it is a directory", source, destination)
+		}
+
+		in, err := os.Open(source)
+		if err != nil {
+			return err
+		}
+		defer in.Close()
+		out, err := os.Create(destination)
+		if err != nil {
+			return err
+		}
+		defer out.Close()
+
+		if _, err = io.Copy(out, in); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// GetLoadingPrecedence implements ConfigAccess
+func (rules *ClientConfigLoadingRules) GetLoadingPrecedence() []string {
+	return rules.Precedence
+}
+
+// GetStartingConfig implements ConfigAccess
+func (rules *ClientConfigLoadingRules) GetStartingConfig() (*clientcmdapi.Config, error) {
+	clientConfig := NewNonInteractiveDeferredLoadingClientConfig(rules, &ConfigOverrides{})
+	rawConfig, err := clientConfig.RawConfig()
+	if os.IsNotExist(err) {
+		return clientcmdapi.NewConfig(), nil
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	return &rawConfig, nil
+}
+
+// GetDefaultFilename implements ConfigAccess
+func (rules *ClientConfigLoadingRules) GetDefaultFilename() string {
+	// Explicit file if we have one.
+	if rules.IsExplicitFile() {
+		return rules.GetExplicitFile()
+	}
+	// Otherwise, first existing file from precedence.
+	for _, filename := range rules.GetLoadingPrecedence() {
+		if _, err := os.Stat(filename); err == nil {
+			return filename
+		}
+	}
+	// If none exists, use the first from precedence.
+	if len(rules.Precedence) > 0 {
+		return rules.Precedence[0]
+	}
+	return ""
+}
+
+// IsExplicitFile implements ConfigAccess
+func (rules *ClientConfigLoadingRules) IsExplicitFile() bool {
+	return len(rules.ExplicitPath) > 0
+}
+
+// GetExplicitFile implements ConfigAccess
+func (rules *ClientConfigLoadingRules) GetExplicitFile() string {
+	return rules.ExplicitPath
+}
+
+// IsDefaultConfig returns true if the provided configuration matches the default
+func (rules *ClientConfigLoadingRules) IsDefaultConfig(config *restclient.Config) bool {
+	if rules.DefaultClientConfig == nil {
+		return false
+	}
+	defaultConfig, err := rules.DefaultClientConfig.ClientConfig()
+	if err != nil {
+		return false
+	}
+	return reflect.DeepEqual(config, defaultConfig)
+}
+
+// LoadFromFile takes a filename and deserializes the contents into Config object
+func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
+	kubeconfigBytes, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+	config, err := Load(kubeconfigBytes)
+	if err != nil {
+		return nil, err
+	}
+	klog.V(6).Infoln("Config loaded from file", filename)
+
+	// set LocationOfOrigin on every Cluster, User, and Context
+	for key, obj := range config.AuthInfos {
+		obj.LocationOfOrigin = filename
+		config.AuthInfos[key] = obj
+	}
+	for key, obj := range config.Clusters {
+		obj.LocationOfOrigin = filename
+		config.Clusters[key] = obj
+	}
+	for key, obj := range config.Contexts {
+		obj.LocationOfOrigin = filename
+		config.Contexts[key] = obj
+	}
+
+	if config.AuthInfos == nil {
+		config.AuthInfos = map[string]*clientcmdapi.AuthInfo{}
+	}
+	if config.Clusters == nil {
+		config.Clusters = map[string]*clientcmdapi.Cluster{}
+	}
+	if config.Contexts == nil {
+		config.Contexts = map[string]*clientcmdapi.Context{}
+	}
+
+	return config, nil
+}
+
+// Load takes a byte slice and deserializes the contents into Config object.
+// Encapsulates deserialization without assuming the source is a file.
+func Load(data []byte) (*clientcmdapi.Config, error) {
+	config := clientcmdapi.NewConfig()
+	// if there's no data in a file, return the default object instead of failing (DecodeInto reject empty input)
+	if len(data) == 0 {
+		return config, nil
+	}
+	decoded, _, err := clientcmdlatest.Codec.Decode(data, &schema.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, config)
+	if err != nil {
+		return nil, err
+	}
+	return decoded.(*clientcmdapi.Config), nil
+}
+
+// WriteToFile serializes the config to yaml and writes it out to a file.  If not present, it creates the file with the mode 0600.  If it is present
+// it stomps the contents
+func WriteToFile(config clientcmdapi.Config, filename string) error {
+	content, err := Write(config)
+	if err != nil {
+		return err
+	}
+	dir := filepath.Dir(filename)
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		if err = os.MkdirAll(dir, 0755); err != nil {
+			return err
+		}
+	}
+
+	if err := ioutil.WriteFile(filename, content, 0600); err != nil {
+		return err
+	}
+	return nil
+}
+
+func lockFile(filename string) error {
+	// TODO: find a way to do this with actual file locks. Will
+	// probably need separate solution for windows and Linux.
+
+	// Make sure the dir exists before we try to create a lock file.
+	dir := filepath.Dir(filename)
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		if err = os.MkdirAll(dir, 0755); err != nil {
+			return err
+		}
+	}
+	f, err := os.OpenFile(lockName(filename), os.O_CREATE|os.O_EXCL, 0)
+	if err != nil {
+		return err
+	}
+	f.Close()
+	return nil
+}
+
+func unlockFile(filename string) error {
+	return os.Remove(lockName(filename))
+}
+
+func lockName(filename string) string {
+	return filename + ".lock"
+}
+
+// Write serializes the config to yaml.
+// Encapsulates serialization without assuming the destination is a file.
+func Write(config clientcmdapi.Config) ([]byte, error) {
+	return runtime.Encode(clientcmdlatest.Codec, &config)
+}
+
+func (rules ClientConfigLoadingRules) ResolvePaths() bool {
+	return !rules.DoNotResolvePaths
+}
+
+// ResolveLocalPaths resolves all relative paths in the config object with respect to the stanza's LocationOfOrigin
+// this cannot be done directly inside of LoadFromFile because doing so there would make it impossible to load a file without
+// modification of its contents.
+func ResolveLocalPaths(config *clientcmdapi.Config) error {
+	for _, cluster := range config.Clusters {
+		if len(cluster.LocationOfOrigin) == 0 {
+			continue
+		}
+		base, err := filepath.Abs(filepath.Dir(cluster.LocationOfOrigin))
+		if err != nil {
+			return fmt.Errorf("Could not determine the absolute path of config file %s: %v", cluster.LocationOfOrigin, err)
+		}
+
+		if err := ResolvePaths(GetClusterFileReferences(cluster), base); err != nil {
+			return err
+		}
+	}
+	for _, authInfo := range config.AuthInfos {
+		if len(authInfo.LocationOfOrigin) == 0 {
+			continue
+		}
+		base, err := filepath.Abs(filepath.Dir(authInfo.LocationOfOrigin))
+		if err != nil {
+			return fmt.Errorf("Could not determine the absolute path of config file %s: %v", authInfo.LocationOfOrigin, err)
+		}
+
+		if err := ResolvePaths(GetAuthInfoFileReferences(authInfo), base); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// RelativizeClusterLocalPaths first absolutizes the paths by calling ResolveLocalPaths.  This assumes that any NEW path is already
+// absolute, but any existing path will be resolved relative to LocationOfOrigin
+func RelativizeClusterLocalPaths(cluster *clientcmdapi.Cluster) error {
+	if len(cluster.LocationOfOrigin) == 0 {
+		return fmt.Errorf("no location of origin for %s", cluster.Server)
+	}
+	base, err := filepath.Abs(filepath.Dir(cluster.LocationOfOrigin))
+	if err != nil {
+		return fmt.Errorf("could not determine the absolute path of config file %s: %v", cluster.LocationOfOrigin, err)
+	}
+
+	if err := ResolvePaths(GetClusterFileReferences(cluster), base); err != nil {
+		return err
+	}
+	if err := RelativizePathWithNoBacksteps(GetClusterFileReferences(cluster), base); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// RelativizeAuthInfoLocalPaths first absolutizes the paths by calling ResolveLocalPaths.  This assumes that any NEW path is already
+// absolute, but any existing path will be resolved relative to LocationOfOrigin
+func RelativizeAuthInfoLocalPaths(authInfo *clientcmdapi.AuthInfo) error {
+	if len(authInfo.LocationOfOrigin) == 0 {
+		return fmt.Errorf("no location of origin for %v", authInfo)
+	}
+	base, err := filepath.Abs(filepath.Dir(authInfo.LocationOfOrigin))
+	if err != nil {
+		return fmt.Errorf("could not determine the absolute path of config file %s: %v", authInfo.LocationOfOrigin, err)
+	}
+
+	if err := ResolvePaths(GetAuthInfoFileReferences(authInfo), base); err != nil {
+		return err
+	}
+	if err := RelativizePathWithNoBacksteps(GetAuthInfoFileReferences(authInfo), base); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func RelativizeConfigPaths(config *clientcmdapi.Config, base string) error {
+	return RelativizePathWithNoBacksteps(GetConfigFileReferences(config), base)
+}
+
+func ResolveConfigPaths(config *clientcmdapi.Config, base string) error {
+	return ResolvePaths(GetConfigFileReferences(config), base)
+}
+
+func GetConfigFileReferences(config *clientcmdapi.Config) []*string {
+	refs := []*string{}
+
+	for _, cluster := range config.Clusters {
+		refs = append(refs, GetClusterFileReferences(cluster)...)
+	}
+	for _, authInfo := range config.AuthInfos {
+		refs = append(refs, GetAuthInfoFileReferences(authInfo)...)
+	}
+
+	return refs
+}
+
+func GetClusterFileReferences(cluster *clientcmdapi.Cluster) []*string {
+	return []*string{&cluster.CertificateAuthority}
+}
+
+func GetAuthInfoFileReferences(authInfo *clientcmdapi.AuthInfo) []*string {
+	s := []*string{&authInfo.ClientCertificate, &authInfo.ClientKey, &authInfo.TokenFile}
+	// Only resolve exec command if it isn't PATH based.
+	if authInfo.Exec != nil && strings.ContainsRune(authInfo.Exec.Command, filepath.Separator) {
+		s = append(s, &authInfo.Exec.Command)
+	}
+	return s
+}
+
+// ResolvePaths updates the given refs to be absolute paths, relative to the given base directory
+func ResolvePaths(refs []*string, base string) error {
+	for _, ref := range refs {
+		// Don't resolve empty paths
+		if len(*ref) > 0 {
+			// Don't resolve absolute paths
+			if !filepath.IsAbs(*ref) {
+				*ref = filepath.Join(base, *ref)
+			}
+		}
+	}
+	return nil
+}
+
+// RelativizePathWithNoBacksteps updates the given refs to be relative paths, relative to the given base directory as long as they do not require backsteps.
+// Any path requiring a backstep is left as-is as long it is absolute.  Any non-absolute path that can't be relativized produces an error
+func RelativizePathWithNoBacksteps(refs []*string, base string) error {
+	for _, ref := range refs {
+		// Don't relativize empty paths
+		if len(*ref) > 0 {
+			rel, err := MakeRelative(*ref, base)
+			if err != nil {
+				return err
+			}
+
+			// if we have a backstep, don't mess with the path
+			if strings.HasPrefix(rel, "../") {
+				if filepath.IsAbs(*ref) {
+					continue
+				}
+
+				return fmt.Errorf("%v requires backsteps and is not absolute", *ref)
+			}
+
+			*ref = rel
+		}
+	}
+	return nil
+}
+
+func MakeRelative(path, base string) (string, error) {
+	if len(path) > 0 {
+		rel, err := filepath.Rel(base, path)
+		if err != nil {
+			return path, err
+		}
+		return rel, nil
+	}
+	return path, nil
+}
+
+// deduplicate removes any duplicated values and returns a new slice, keeping the order unchanged
+func deduplicate(s []string) []string {
+	encountered := map[string]bool{}
+	ret := make([]string, 0)
+	for i := range s {
+		if encountered[s[i]] {
+			continue
+		}
+		encountered[s[i]] = true
+		ret = append(ret, s[i])
+	}
+	return ret
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder.go b/vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder.go
new file mode 100644
index 0000000..76380db
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder.go
@@ -0,0 +1,168 @@
+/*
+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 clientcmd
+
+import (
+	"io"
+	"sync"
+
+	"k8s.io/klog"
+
+	restclient "k8s.io/client-go/rest"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+// DeferredLoadingClientConfig is a ClientConfig interface that is backed by a client config loader.
+// It is used in cases where the loading rules may change after you've instantiated them and you want to be sure that
+// the most recent rules are used.  This is useful in cases where you bind flags to loading rule parameters before
+// the parse happens and you want your calling code to be ignorant of how the values are being mutated to avoid
+// passing extraneous information down a call stack
+type DeferredLoadingClientConfig struct {
+	loader         ClientConfigLoader
+	overrides      *ConfigOverrides
+	fallbackReader io.Reader
+
+	clientConfig ClientConfig
+	loadingLock  sync.Mutex
+
+	// provided for testing
+	icc InClusterConfig
+}
+
+// InClusterConfig abstracts details of whether the client is running in a cluster for testing.
+type InClusterConfig interface {
+	ClientConfig
+	Possible() bool
+}
+
+// NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name
+func NewNonInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides) ClientConfig {
+	return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}}
+}
+
+// NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader
+func NewInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig {
+	return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}, fallbackReader: fallbackReader}
+}
+
+func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) {
+	if config.clientConfig == nil {
+		config.loadingLock.Lock()
+		defer config.loadingLock.Unlock()
+
+		if config.clientConfig == nil {
+			mergedConfig, err := config.loader.Load()
+			if err != nil {
+				return nil, err
+			}
+
+			var mergedClientConfig ClientConfig
+			if config.fallbackReader != nil {
+				mergedClientConfig = NewInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.fallbackReader, config.loader)
+			} else {
+				mergedClientConfig = NewNonInteractiveClientConfig(*mergedConfig, config.overrides.CurrentContext, config.overrides, config.loader)
+			}
+
+			config.clientConfig = mergedClientConfig
+		}
+	}
+
+	return config.clientConfig, nil
+}
+
+func (config *DeferredLoadingClientConfig) RawConfig() (clientcmdapi.Config, error) {
+	mergedConfig, err := config.createClientConfig()
+	if err != nil {
+		return clientcmdapi.Config{}, err
+	}
+
+	return mergedConfig.RawConfig()
+}
+
+// ClientConfig implements ClientConfig
+func (config *DeferredLoadingClientConfig) ClientConfig() (*restclient.Config, error) {
+	mergedClientConfig, err := config.createClientConfig()
+	if err != nil {
+		return nil, err
+	}
+
+	// load the configuration and return on non-empty errors and if the
+	// content differs from the default config
+	mergedConfig, err := mergedClientConfig.ClientConfig()
+	switch {
+	case err != nil:
+		if !IsEmptyConfig(err) {
+			// return on any error except empty config
+			return nil, err
+		}
+	case mergedConfig != nil:
+		// the configuration is valid, but if this is equal to the defaults we should try
+		// in-cluster configuration
+		if !config.loader.IsDefaultConfig(mergedConfig) {
+			return mergedConfig, nil
+		}
+	}
+
+	// check for in-cluster configuration and use it
+	if config.icc.Possible() {
+		klog.V(4).Infof("Using in-cluster configuration")
+		return config.icc.ClientConfig()
+	}
+
+	// return the result of the merged client config
+	return mergedConfig, err
+}
+
+// Namespace implements KubeConfig
+func (config *DeferredLoadingClientConfig) Namespace() (string, bool, error) {
+	mergedKubeConfig, err := config.createClientConfig()
+	if err != nil {
+		return "", false, err
+	}
+
+	ns, overridden, err := mergedKubeConfig.Namespace()
+	// if we get an error and it is not empty config, or if the merged config defined an explicit namespace, or
+	// if in-cluster config is not possible, return immediately
+	if (err != nil && !IsEmptyConfig(err)) || overridden || !config.icc.Possible() {
+		// return on any error except empty config
+		return ns, overridden, err
+	}
+
+	if len(ns) > 0 {
+		// if we got a non-default namespace from the kubeconfig, use it
+		if ns != "default" {
+			return ns, false, nil
+		}
+
+		// if we got a default namespace, determine whether it was explicit or implicit
+		if raw, err := mergedKubeConfig.RawConfig(); err == nil {
+			if context := raw.Contexts[raw.CurrentContext]; context != nil && len(context.Namespace) > 0 {
+				return ns, false, nil
+			}
+		}
+	}
+
+	klog.V(4).Infof("Using in-cluster namespace")
+
+	// allow the namespace from the service account token directory to be used.
+	return config.icc.Namespace()
+}
+
+// ConfigAccess implements ClientConfig
+func (config *DeferredLoadingClientConfig) ConfigAccess() ConfigAccess {
+	return config.loader
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/overrides.go b/vendor/k8s.io/client-go/tools/clientcmd/overrides.go
new file mode 100644
index 0000000..bfca032
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/overrides.go
@@ -0,0 +1,247 @@
+/*
+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 clientcmd
+
+import (
+	"strconv"
+	"strings"
+
+	"github.com/spf13/pflag"
+
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+// ConfigOverrides holds values that should override whatever information is pulled from the actual Config object.  You can't
+// simply use an actual Config object, because Configs hold maps, but overrides are restricted to "at most one"
+type ConfigOverrides struct {
+	AuthInfo clientcmdapi.AuthInfo
+	// ClusterDefaults are applied before the configured cluster info is loaded.
+	ClusterDefaults clientcmdapi.Cluster
+	ClusterInfo     clientcmdapi.Cluster
+	Context         clientcmdapi.Context
+	CurrentContext  string
+	Timeout         string
+}
+
+// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly
+// corresponds to ConfigOverrides
+type ConfigOverrideFlags struct {
+	AuthOverrideFlags    AuthOverrideFlags
+	ClusterOverrideFlags ClusterOverrideFlags
+	ContextOverrideFlags ContextOverrideFlags
+	CurrentContext       FlagInfo
+	Timeout              FlagInfo
+}
+
+// AuthOverrideFlags holds the flag names to be used for binding command line flags for AuthInfo objects
+type AuthOverrideFlags struct {
+	ClientCertificate FlagInfo
+	ClientKey         FlagInfo
+	Token             FlagInfo
+	Impersonate       FlagInfo
+	ImpersonateGroups FlagInfo
+	Username          FlagInfo
+	Password          FlagInfo
+}
+
+// ContextOverrideFlags holds the flag names to be used for binding command line flags for Cluster objects
+type ContextOverrideFlags struct {
+	ClusterName  FlagInfo
+	AuthInfoName FlagInfo
+	Namespace    FlagInfo
+}
+
+// ClusterOverride holds the flag names to be used for binding command line flags for Cluster objects
+type ClusterOverrideFlags struct {
+	APIServer             FlagInfo
+	APIVersion            FlagInfo
+	CertificateAuthority  FlagInfo
+	InsecureSkipTLSVerify FlagInfo
+}
+
+// FlagInfo contains information about how to register a flag.  This struct is useful if you want to provide a way for an extender to
+// get back a set of recommended flag names, descriptions, and defaults, but allow for customization by an extender.  This makes for
+// coherent extension, without full prescription
+type FlagInfo struct {
+	// LongName is the long string for a flag.  If this is empty, then the flag will not be bound
+	LongName string
+	// ShortName is the single character for a flag.  If this is empty, then there will be no short flag
+	ShortName string
+	// Default is the default value for the flag
+	Default string
+	// Description is the description for the flag
+	Description string
+}
+
+// AddSecretAnnotation add secret flag to Annotation.
+func (f FlagInfo) AddSecretAnnotation(flags *pflag.FlagSet) FlagInfo {
+	flags.SetAnnotation(f.LongName, "classified", []string{"true"})
+	return f
+}
+
+// BindStringFlag binds the flag based on the provided info.  If LongName == "", nothing is registered
+func (f FlagInfo) BindStringFlag(flags *pflag.FlagSet, target *string) FlagInfo {
+	// you can't register a flag without a long name
+	if len(f.LongName) > 0 {
+		flags.StringVarP(target, f.LongName, f.ShortName, f.Default, f.Description)
+	}
+	return f
+}
+
+// BindTransformingStringFlag binds the flag based on the provided info.  If LongName == "", nothing is registered
+func (f FlagInfo) BindTransformingStringFlag(flags *pflag.FlagSet, target *string, transformer func(string) (string, error)) FlagInfo {
+	// you can't register a flag without a long name
+	if len(f.LongName) > 0 {
+		flags.VarP(newTransformingStringValue(f.Default, target, transformer), f.LongName, f.ShortName, f.Description)
+	}
+	return f
+}
+
+// BindStringSliceFlag binds the flag based on the provided info.  If LongName == "", nothing is registered
+func (f FlagInfo) BindStringArrayFlag(flags *pflag.FlagSet, target *[]string) FlagInfo {
+	// you can't register a flag without a long name
+	if len(f.LongName) > 0 {
+		sliceVal := []string{}
+		if len(f.Default) > 0 {
+			sliceVal = []string{f.Default}
+		}
+		flags.StringArrayVarP(target, f.LongName, f.ShortName, sliceVal, f.Description)
+	}
+	return f
+}
+
+// BindBoolFlag binds the flag based on the provided info.  If LongName == "", nothing is registered
+func (f FlagInfo) BindBoolFlag(flags *pflag.FlagSet, target *bool) FlagInfo {
+	// you can't register a flag without a long name
+	if len(f.LongName) > 0 {
+		// try to parse Default as a bool.  If it fails, assume false
+		boolVal, err := strconv.ParseBool(f.Default)
+		if err != nil {
+			boolVal = false
+		}
+
+		flags.BoolVarP(target, f.LongName, f.ShortName, boolVal, f.Description)
+	}
+	return f
+}
+
+const (
+	FlagClusterName      = "cluster"
+	FlagAuthInfoName     = "user"
+	FlagContext          = "context"
+	FlagNamespace        = "namespace"
+	FlagAPIServer        = "server"
+	FlagInsecure         = "insecure-skip-tls-verify"
+	FlagCertFile         = "client-certificate"
+	FlagKeyFile          = "client-key"
+	FlagCAFile           = "certificate-authority"
+	FlagEmbedCerts       = "embed-certs"
+	FlagBearerToken      = "token"
+	FlagImpersonate      = "as"
+	FlagImpersonateGroup = "as-group"
+	FlagUsername         = "username"
+	FlagPassword         = "password"
+	FlagTimeout          = "request-timeout"
+)
+
+// RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
+func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags {
+	return ConfigOverrideFlags{
+		AuthOverrideFlags:    RecommendedAuthOverrideFlags(prefix),
+		ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix),
+		ContextOverrideFlags: RecommendedContextOverrideFlags(prefix),
+
+		CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"},
+		Timeout:        FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."},
+	}
+}
+
+// RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
+func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags {
+	return AuthOverrideFlags{
+		ClientCertificate: FlagInfo{prefix + FlagCertFile, "", "", "Path to a client certificate file for TLS"},
+		ClientKey:         FlagInfo{prefix + FlagKeyFile, "", "", "Path to a client key file for TLS"},
+		Token:             FlagInfo{prefix + FlagBearerToken, "", "", "Bearer token for authentication to the API server"},
+		Impersonate:       FlagInfo{prefix + FlagImpersonate, "", "", "Username to impersonate for the operation"},
+		ImpersonateGroups: FlagInfo{prefix + FlagImpersonateGroup, "", "", "Group to impersonate for the operation, this flag can be repeated to specify multiple groups."},
+		Username:          FlagInfo{prefix + FlagUsername, "", "", "Username for basic authentication to the API server"},
+		Password:          FlagInfo{prefix + FlagPassword, "", "", "Password for basic authentication to the API server"},
+	}
+}
+
+// RecommendedClusterOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
+func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags {
+	return ClusterOverrideFlags{
+		APIServer:             FlagInfo{prefix + FlagAPIServer, "", "", "The address and port of the Kubernetes API server"},
+		CertificateAuthority:  FlagInfo{prefix + FlagCAFile, "", "", "Path to a cert file for the certificate authority"},
+		InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"},
+	}
+}
+
+// RecommendedContextOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
+func RecommendedContextOverrideFlags(prefix string) ContextOverrideFlags {
+	return ContextOverrideFlags{
+		ClusterName:  FlagInfo{prefix + FlagClusterName, "", "", "The name of the kubeconfig cluster to use"},
+		AuthInfoName: FlagInfo{prefix + FlagAuthInfoName, "", "", "The name of the kubeconfig user to use"},
+		Namespace:    FlagInfo{prefix + FlagNamespace, "n", "", "If present, the namespace scope for this CLI request"},
+	}
+}
+
+// BindOverrideFlags is a convenience method to bind the specified flags to their associated variables
+func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) {
+	BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags)
+	BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags)
+	BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags)
+	flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext)
+	flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout)
+}
+
+// BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables
+func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, flags *pflag.FlagSet, flagNames AuthOverrideFlags) {
+	flagNames.ClientCertificate.BindStringFlag(flags, &authInfo.ClientCertificate).AddSecretAnnotation(flags)
+	flagNames.ClientKey.BindStringFlag(flags, &authInfo.ClientKey).AddSecretAnnotation(flags)
+	flagNames.Token.BindStringFlag(flags, &authInfo.Token).AddSecretAnnotation(flags)
+	flagNames.Impersonate.BindStringFlag(flags, &authInfo.Impersonate).AddSecretAnnotation(flags)
+	flagNames.ImpersonateGroups.BindStringArrayFlag(flags, &authInfo.ImpersonateGroups).AddSecretAnnotation(flags)
+	flagNames.Username.BindStringFlag(flags, &authInfo.Username).AddSecretAnnotation(flags)
+	flagNames.Password.BindStringFlag(flags, &authInfo.Password).AddSecretAnnotation(flags)
+}
+
+// BindClusterFlags is a convenience method to bind the specified flags to their associated variables
+func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, flagNames ClusterOverrideFlags) {
+	flagNames.APIServer.BindStringFlag(flags, &clusterInfo.Server)
+	flagNames.CertificateAuthority.BindStringFlag(flags, &clusterInfo.CertificateAuthority)
+	flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify)
+}
+
+// BindFlags is a convenience method to bind the specified flags to their associated variables
+func BindContextFlags(contextInfo *clientcmdapi.Context, flags *pflag.FlagSet, flagNames ContextOverrideFlags) {
+	flagNames.ClusterName.BindStringFlag(flags, &contextInfo.Cluster)
+	flagNames.AuthInfoName.BindStringFlag(flags, &contextInfo.AuthInfo)
+	flagNames.Namespace.BindTransformingStringFlag(flags, &contextInfo.Namespace, RemoveNamespacesPrefix)
+}
+
+// RemoveNamespacesPrefix is a transformer that strips "ns/", "namespace/" and "namespaces/" prefixes case-insensitively
+func RemoveNamespacesPrefix(value string) (string, error) {
+	for _, prefix := range []string{"namespaces/", "namespace/", "ns/"} {
+		if len(value) > len(prefix) && strings.EqualFold(value[0:len(prefix)], prefix) {
+			value = value[len(prefix):]
+			break
+		}
+	}
+	return value, nil
+}
diff --git a/vendor/k8s.io/client-go/tools/clientcmd/validation.go b/vendor/k8s.io/client-go/tools/clientcmd/validation.go
new file mode 100644
index 0000000..629c0b3
--- /dev/null
+++ b/vendor/k8s.io/client-go/tools/clientcmd/validation.go
@@ -0,0 +1,298 @@
+/*
+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 clientcmd
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"reflect"
+	"strings"
+
+	utilerrors "k8s.io/apimachinery/pkg/util/errors"
+	"k8s.io/apimachinery/pkg/util/validation"
+	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+)
+
+var (
+	ErrNoContext   = errors.New("no context chosen")
+	ErrEmptyConfig = errors.New("no configuration has been provided")
+	// message is for consistency with old behavior
+	ErrEmptyCluster = errors.New("cluster has no server defined")
+)
+
+type errContextNotFound struct {
+	ContextName string
+}
+
+func (e *errContextNotFound) Error() string {
+	return fmt.Sprintf("context was not found for specified context: %v", e.ContextName)
+}
+
+// IsContextNotFound returns a boolean indicating whether the error is known to
+// report that a context was not found
+func IsContextNotFound(err error) bool {
+	if err == nil {
+		return false
+	}
+	if _, ok := err.(*errContextNotFound); ok || err == ErrNoContext {
+		return true
+	}
+	return strings.Contains(err.Error(), "context was not found for specified context")
+}
+
+// IsEmptyConfig returns true if the provided error indicates the provided configuration
+// is empty.
+func IsEmptyConfig(err error) bool {
+	switch t := err.(type) {
+	case errConfigurationInvalid:
+		return len(t) == 1 && t[0] == ErrEmptyConfig
+	}
+	return err == ErrEmptyConfig
+}
+
+// errConfigurationInvalid is a set of errors indicating the configuration is invalid.
+type errConfigurationInvalid []error
+
+// errConfigurationInvalid implements error and Aggregate
+var _ error = errConfigurationInvalid{}
+var _ utilerrors.Aggregate = errConfigurationInvalid{}
+
+func newErrConfigurationInvalid(errs []error) error {
+	switch len(errs) {
+	case 0:
+		return nil
+	default:
+		return errConfigurationInvalid(errs)
+	}
+}
+
+// Error implements the error interface
+func (e errConfigurationInvalid) Error() string {
+	return fmt.Sprintf("invalid configuration: %v", utilerrors.NewAggregate(e).Error())
+}
+
+// Errors implements the AggregateError interface
+func (e errConfigurationInvalid) Errors() []error {
+	return e
+}
+
+// IsConfigurationInvalid returns true if the provided error indicates the configuration is invalid.
+func IsConfigurationInvalid(err error) bool {
+	switch err.(type) {
+	case *errContextNotFound, errConfigurationInvalid:
+		return true
+	}
+	return IsContextNotFound(err)
+}
+
+// Validate checks for errors in the Config.  It does not return early so that it can find as many errors as possible.
+func Validate(config clientcmdapi.Config) error {
+	validationErrors := make([]error, 0)
+
+	if clientcmdapi.IsConfigEmpty(&config) {
+		return newErrConfigurationInvalid([]error{ErrEmptyConfig})
+	}
+
+	if len(config.CurrentContext) != 0 {
+		if _, exists := config.Contexts[config.CurrentContext]; !exists {
+			validationErrors = append(validationErrors, &errContextNotFound{config.CurrentContext})
+		}
+	}
+
+	for contextName, context := range config.Contexts {
+		validationErrors = append(validationErrors, validateContext(contextName, *context, config)...)
+	}
+
+	for authInfoName, authInfo := range config.AuthInfos {
+		validationErrors = append(validationErrors, validateAuthInfo(authInfoName, *authInfo)...)
+	}
+
+	for clusterName, clusterInfo := range config.Clusters {
+		validationErrors = append(validationErrors, validateClusterInfo(clusterName, *clusterInfo)...)
+	}
+
+	return newErrConfigurationInvalid(validationErrors)
+}
+
+// ConfirmUsable looks a particular context and determines if that particular part of the config is useable.  There might still be errors in the config,
+// but no errors in the sections requested or referenced.  It does not return early so that it can find as many errors as possible.
+func ConfirmUsable(config clientcmdapi.Config, passedContextName string) error {
+	validationErrors := make([]error, 0)
+
+	if clientcmdapi.IsConfigEmpty(&config) {
+		return newErrConfigurationInvalid([]error{ErrEmptyConfig})
+	}
+
+	var contextName string
+	if len(passedContextName) != 0 {
+		contextName = passedContextName
+	} else {
+		contextName = config.CurrentContext
+	}
+
+	if len(contextName) == 0 {
+		return ErrNoContext
+	}
+
+	context, exists := config.Contexts[contextName]
+	if !exists {
+		validationErrors = append(validationErrors, &errContextNotFound{contextName})
+	}
+
+	if exists {
+		validationErrors = append(validationErrors, validateContext(contextName, *context, config)...)
+		validationErrors = append(validationErrors, validateAuthInfo(context.AuthInfo, *config.AuthInfos[context.AuthInfo])...)
+		validationErrors = append(validationErrors, validateClusterInfo(context.Cluster, *config.Clusters[context.Cluster])...)
+	}
+
+	return newErrConfigurationInvalid(validationErrors)
+}
+
+// validateClusterInfo looks for conflicts and errors in the cluster info
+func validateClusterInfo(clusterName string, clusterInfo clientcmdapi.Cluster) []error {
+	validationErrors := make([]error, 0)
+
+	emptyCluster := clientcmdapi.NewCluster()
+	if reflect.DeepEqual(*emptyCluster, clusterInfo) {
+		return []error{ErrEmptyCluster}
+	}
+
+	if len(clusterInfo.Server) == 0 {
+		if len(clusterName) == 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("default cluster has no server defined"))
+		} else {
+			validationErrors = append(validationErrors, fmt.Errorf("no server found for cluster %q", clusterName))
+		}
+	}
+	// Make sure CA data and CA file aren't both specified
+	if len(clusterInfo.CertificateAuthority) != 0 && len(clusterInfo.CertificateAuthorityData) != 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override.", clusterName))
+	}
+	if len(clusterInfo.CertificateAuthority) != 0 {
+		clientCertCA, err := os.Open(clusterInfo.CertificateAuthority)
+		defer clientCertCA.Close()
+		if err != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("unable to read certificate-authority %v for %v due to %v", clusterInfo.CertificateAuthority, clusterName, err))
+		}
+	}
+
+	return validationErrors
+}
+
+// validateAuthInfo looks for conflicts and errors in the auth info
+func validateAuthInfo(authInfoName string, authInfo clientcmdapi.AuthInfo) []error {
+	validationErrors := make([]error, 0)
+
+	usingAuthPath := false
+	methods := make([]string, 0, 3)
+	if len(authInfo.Token) != 0 {
+		methods = append(methods, "token")
+	}
+	if len(authInfo.Username) != 0 || len(authInfo.Password) != 0 {
+		methods = append(methods, "basicAuth")
+	}
+
+	if len(authInfo.ClientCertificate) != 0 || len(authInfo.ClientCertificateData) != 0 {
+		// Make sure cert data and file aren't both specified
+		if len(authInfo.ClientCertificate) != 0 && len(authInfo.ClientCertificateData) != 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("client-cert-data and client-cert are both specified for %v. client-cert-data will override.", authInfoName))
+		}
+		// Make sure key data and file aren't both specified
+		if len(authInfo.ClientKey) != 0 && len(authInfo.ClientKeyData) != 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("client-key-data and client-key are both specified for %v; client-key-data will override", authInfoName))
+		}
+		// Make sure a key is specified
+		if len(authInfo.ClientKey) == 0 && len(authInfo.ClientKeyData) == 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("client-key-data or client-key must be specified for %v to use the clientCert authentication method.", authInfoName))
+		}
+
+		if len(authInfo.ClientCertificate) != 0 {
+			clientCertFile, err := os.Open(authInfo.ClientCertificate)
+			defer clientCertFile.Close()
+			if err != nil {
+				validationErrors = append(validationErrors, fmt.Errorf("unable to read client-cert %v for %v due to %v", authInfo.ClientCertificate, authInfoName, err))
+			}
+		}
+		if len(authInfo.ClientKey) != 0 {
+			clientKeyFile, err := os.Open(authInfo.ClientKey)
+			defer clientKeyFile.Close()
+			if err != nil {
+				validationErrors = append(validationErrors, fmt.Errorf("unable to read client-key %v for %v due to %v", authInfo.ClientKey, authInfoName, err))
+			}
+		}
+	}
+
+	if authInfo.Exec != nil {
+		if authInfo.AuthProvider != nil {
+			validationErrors = append(validationErrors, fmt.Errorf("authProvider cannot be provided in combination with an exec plugin for %s", authInfoName))
+		}
+		if len(authInfo.Exec.Command) == 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("command must be specified for %v to use exec authentication plugin", authInfoName))
+		}
+		if len(authInfo.Exec.APIVersion) == 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("apiVersion must be specified for %v to use exec authentication plugin", authInfoName))
+		}
+		for _, v := range authInfo.Exec.Env {
+			if len(v.Name) == 0 {
+				validationErrors = append(validationErrors, fmt.Errorf("env variable name must be specified for %v to use exec authentication plugin", authInfoName))
+			} else if len(v.Value) == 0 {
+				validationErrors = append(validationErrors, fmt.Errorf("env variable %s value must be specified for %v to use exec authentication plugin", v.Name, authInfoName))
+			}
+		}
+	}
+
+	// authPath also provides information for the client to identify the server, so allow multiple auth methods in that case
+	if (len(methods) > 1) && (!usingAuthPath) {
+		validationErrors = append(validationErrors, fmt.Errorf("more than one authentication method found for %v; found %v, only one is allowed", authInfoName, methods))
+	}
+
+	// ImpersonateGroups or ImpersonateUserExtra should be requested with a user
+	if (len(authInfo.ImpersonateGroups) > 0 || len(authInfo.ImpersonateUserExtra) > 0) && (len(authInfo.Impersonate) == 0) {
+		validationErrors = append(validationErrors, fmt.Errorf("requesting groups or user-extra for %v without impersonating a user", authInfoName))
+	}
+	return validationErrors
+}
+
+// validateContext looks for errors in the context.  It is not transitive, so errors in the reference authInfo or cluster configs are not included in this return
+func validateContext(contextName string, context clientcmdapi.Context, config clientcmdapi.Config) []error {
+	validationErrors := make([]error, 0)
+
+	if len(contextName) == 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("empty context name for %#v is not allowed", context))
+	}
+
+	if len(context.AuthInfo) == 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("user was not specified for context %q", contextName))
+	} else if _, exists := config.AuthInfos[context.AuthInfo]; !exists {
+		validationErrors = append(validationErrors, fmt.Errorf("user %q was not found for context %q", context.AuthInfo, contextName))
+	}
+
+	if len(context.Cluster) == 0 {
+		validationErrors = append(validationErrors, fmt.Errorf("cluster was not specified for context %q", contextName))
+	} else if _, exists := config.Clusters[context.Cluster]; !exists {
+		validationErrors = append(validationErrors, fmt.Errorf("cluster %q was not found for context %q", context.Cluster, contextName))
+	}
+
+	if len(context.Namespace) != 0 {
+		if len(validation.IsDNS1123Label(context.Namespace)) != 0 {
+			validationErrors = append(validationErrors, fmt.Errorf("namespace %q for context %q does not conform to the kubernetes DNS_LABEL rules", context.Namespace, contextName))
+		}
+	}
+
+	return validationErrors
+}
diff --git a/vendor/k8s.io/client-go/util/homedir/homedir.go b/vendor/k8s.io/client-go/util/homedir/homedir.go
new file mode 100644
index 0000000..816db57
--- /dev/null
+++ b/vendor/k8s.io/client-go/util/homedir/homedir.go
@@ -0,0 +1,47 @@
+/*
+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 homedir
+
+import (
+	"os"
+	"runtime"
+)
+
+// HomeDir returns the home directory for the current user
+func HomeDir() string {
+	if runtime.GOOS == "windows" {
+
+		// First prefer the HOME environmental variable
+		if home := os.Getenv("HOME"); len(home) > 0 {
+			if _, err := os.Stat(home); err == nil {
+				return home
+			}
+		}
+		if homeDrive, homePath := os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"); len(homeDrive) > 0 && len(homePath) > 0 {
+			homeDir := homeDrive + homePath
+			if _, err := os.Stat(homeDir); err == nil {
+				return homeDir
+			}
+		}
+		if userProfile := os.Getenv("USERPROFILE"); len(userProfile) > 0 {
+			if _, err := os.Stat(userProfile); err == nil {
+				return userProfile
+			}
+		}
+	}
+	return os.Getenv("HOME")
+}