diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 201d9acea2d..8638a9b0573 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -530,9 +530,11 @@ _kubectl_config_view() flags_with_completion=() flags_completion=() + flags+=("--flatten") flags+=("--help") flags+=("-h") flags+=("--merge") + flags+=("--minify") flags+=("--no-headers") flags+=("--output=") two_word_flags+=("-o") diff --git a/docs/kubectl.md b/docs/kubectl.md index 91fbafab545..85f24ac6f07 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -66,4 +66,4 @@ kubectl * [kubectl update](kubectl_update.md) - Update a resource by filename or stdin. * [kubectl version](kubectl_version.md) - Print the client and server version information. -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.865844658 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.453079685 +0000 UTC diff --git a/docs/kubectl_api-versions.md b/docs/kubectl_api-versions.md index 410331ae665..71e2a60f412 100644 --- a/docs/kubectl_api-versions.md +++ b/docs/kubectl_api-versions.md @@ -50,4 +50,4 @@ kubectl api-versions ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.865438603 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452805417 +0000 UTC diff --git a/docs/kubectl_cluster-info.md b/docs/kubectl_cluster-info.md index 7fd935536ce..f4cca1a4788 100644 --- a/docs/kubectl_cluster-info.md +++ b/docs/kubectl_cluster-info.md @@ -50,4 +50,4 @@ kubectl cluster-info ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.865291243 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452687658 +0000 UTC diff --git a/docs/kubectl_config.md b/docs/kubectl_config.md index 7fc7133c13e..bea2102db2b 100644 --- a/docs/kubectl_config.md +++ b/docs/kubectl_config.md @@ -58,6 +58,6 @@ kubectl config SUBCOMMAND * [kubectl config set-credentials](kubectl_config_set-credentials.md) - Sets a user entry in kubeconfig * [kubectl config unset](kubectl_config_unset.md) - Unsets an individual value in a kubeconfig file * [kubectl config use-context](kubectl_config_use-context.md) - Sets the current-context in a kubeconfig file -* [kubectl config view](kubectl_config_view.md) - displays merged kubeconfig settings or a specified kubeconfig file. +* [kubectl config view](kubectl_config_view.md) - displays Merged kubeconfig settings or a specified kubeconfig file. -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.86513156 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452570294 +0000 UTC diff --git a/docs/kubectl_config_set-cluster.md b/docs/kubectl_config_set-cluster.md index 74786a4c3d7..f23b7979ca0 100644 --- a/docs/kubectl_config_set-cluster.md +++ b/docs/kubectl_config_set-cluster.md @@ -68,4 +68,4 @@ $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.864096021 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.451768559 +0000 UTC diff --git a/docs/kubectl_config_set-context.md b/docs/kubectl_config_set-context.md index d9eec2d6c1b..62133348b32 100644 --- a/docs/kubectl_config_set-context.md +++ b/docs/kubectl_config_set-context.md @@ -61,4 +61,4 @@ $ kubectl config set-context gce --user=cluster-admin ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.86442717 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452031181 +0000 UTC diff --git a/docs/kubectl_config_set-credentials.md b/docs/kubectl_config_set-credentials.md index e31879543b6..e85314bbd6d 100644 --- a/docs/kubectl_config_set-credentials.md +++ b/docs/kubectl_config_set-credentials.md @@ -81,4 +81,4 @@ $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt - ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.864263862 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.451906112 +0000 UTC diff --git a/docs/kubectl_config_set.md b/docs/kubectl_config_set.md index 78b1f0e85de..8cfd8c372c5 100644 --- a/docs/kubectl_config_set.md +++ b/docs/kubectl_config_set.md @@ -55,4 +55,4 @@ kubectl config set PROPERTY_NAME PROPERTY_VALUE ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.864594301 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.45217009 +0000 UTC diff --git a/docs/kubectl_config_unset.md b/docs/kubectl_config_unset.md index be412e68019..23f135bc4a6 100644 --- a/docs/kubectl_config_unset.md +++ b/docs/kubectl_config_unset.md @@ -54,4 +54,4 @@ kubectl config unset PROPERTY_NAME ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.864788809 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452318625 +0000 UTC diff --git a/docs/kubectl_config_use-context.md b/docs/kubectl_config_use-context.md index e7e45733545..493a68820a4 100644 --- a/docs/kubectl_config_use-context.md +++ b/docs/kubectl_config_use-context.md @@ -53,4 +53,4 @@ kubectl config use-context CONTEXT_NAME ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.864953658 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452447654 +0000 UTC diff --git a/docs/kubectl_config_view.md b/docs/kubectl_config_view.md index 56378b12575..4b193c4c1f9 100644 --- a/docs/kubectl_config_view.md +++ b/docs/kubectl_config_view.md @@ -1,11 +1,11 @@ ## kubectl config view -displays merged kubeconfig settings or a specified kubeconfig file. +displays Merged kubeconfig settings or a specified kubeconfig file. ### Synopsis -displays merged kubeconfig settings or a specified kubeconfig file. +displays Merged kubeconfig settings or a specified kubeconfig file. You can use --output=template --template=TEMPLATE to extract specific values. @@ -16,7 +16,7 @@ kubectl config view ### Examples ``` -// Show merged kubeconfig settings. +// Show Merged kubeconfig settings. $ kubectl config view // Show only local kubeconfig settings @@ -29,8 +29,10 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 ### Options ``` + --flatten=false: flatten the resulting kubeconfig file into self contained output (useful for creating portable kubeconfig files) -h, --help=false: help for view --merge=true: merge together the full hierarchy of kubeconfig files + --minify=false: remove all information not used by current-context from the output --no-headers=false: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|template|templatefile. --output-version="": Output the formatted object with the given version (default api-version). @@ -73,4 +75,4 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2 ### SEE ALSO * [kubectl config](kubectl_config.md) - config modifies kubeconfig files -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.863759642 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.451646336 +0000 UTC diff --git a/docs/kubectl_create.md b/docs/kubectl_create.md index ee2b5568b57..100406e0e6d 100644 --- a/docs/kubectl_create.md +++ b/docs/kubectl_create.md @@ -63,4 +63,4 @@ $ cat pod.json | kubectl create -f - ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.858089037 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.44924428 +0000 UTC diff --git a/docs/kubectl_delete.md b/docs/kubectl_delete.md index ca044bbf5df..fe5f50c86e3 100644 --- a/docs/kubectl_delete.md +++ b/docs/kubectl_delete.md @@ -81,4 +81,4 @@ $ kubectl delete pods --all ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.858739718 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.449502668 +0000 UTC diff --git a/docs/kubectl_describe.md b/docs/kubectl_describe.md index 8115b45265e..2495238e734 100644 --- a/docs/kubectl_describe.md +++ b/docs/kubectl_describe.md @@ -53,4 +53,4 @@ kubectl describe RESOURCE ID ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.857744518 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.449099339 +0000 UTC diff --git a/docs/kubectl_exec.md b/docs/kubectl_exec.md index 3df8ac7b730..d0c457f2c55 100644 --- a/docs/kubectl_exec.md +++ b/docs/kubectl_exec.md @@ -64,4 +64,4 @@ $ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.860311374 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.450677119 +0000 UTC diff --git a/docs/kubectl_expose.md b/docs/kubectl_expose.md index 689889d2308..314b3018fec 100644 --- a/docs/kubectl_expose.md +++ b/docs/kubectl_expose.md @@ -82,4 +82,4 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.863051668 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.451362859 +0000 UTC diff --git a/docs/kubectl_get.md b/docs/kubectl_get.md index 3bb1f16f101..aab970a4304 100644 --- a/docs/kubectl_get.md +++ b/docs/kubectl_get.md @@ -85,4 +85,4 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.836684094 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.448937009 +0000 UTC diff --git a/docs/kubectl_label.md b/docs/kubectl_label.md index 185689834ea..bcff8fa8762 100644 --- a/docs/kubectl_label.md +++ b/docs/kubectl_label.md @@ -81,4 +81,4 @@ $ kubectl label pods foo bar- ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.863412074 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.45150424 +0000 UTC diff --git a/docs/kubectl_log.md b/docs/kubectl_log.md index c9175c744ec..5d4a098df64 100644 --- a/docs/kubectl_log.md +++ b/docs/kubectl_log.md @@ -62,4 +62,4 @@ $ kubectl log -f 123456-7890 ruby-container ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.859351191 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.450201574 +0000 UTC diff --git a/docs/kubectl_namespace.md b/docs/kubectl_namespace.md index ca006c270fa..46b1dfd2e66 100644 --- a/docs/kubectl_namespace.md +++ b/docs/kubectl_namespace.md @@ -53,4 +53,4 @@ kubectl namespace [namespace] ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.859053402 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.449618977 +0000 UTC diff --git a/docs/kubectl_port-forward.md b/docs/kubectl_port-forward.md index 2a93e7848fa..285aefeb7c2 100644 --- a/docs/kubectl_port-forward.md +++ b/docs/kubectl_port-forward.md @@ -68,4 +68,4 @@ $ kubectl port-forward -p mypod 0:5000 ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.860596821 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.45080362 +0000 UTC diff --git a/docs/kubectl_proxy.md b/docs/kubectl_proxy.md index f11bfaed6a0..afd19673efd 100644 --- a/docs/kubectl_proxy.md +++ b/docs/kubectl_proxy.md @@ -65,4 +65,4 @@ $ kubectl proxy --api-prefix=k8s-api ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.860912037 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.45092701 +0000 UTC diff --git a/docs/kubectl_resize.md b/docs/kubectl_resize.md index 6482643d1b6..d1762db7e4b 100644 --- a/docs/kubectl_resize.md +++ b/docs/kubectl_resize.md @@ -68,4 +68,4 @@ $ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.859972905 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.450528897 +0000 UTC diff --git a/docs/kubectl_rolling-update.md b/docs/kubectl_rolling-update.md index 8501878d3a0..92e7bd4e874 100644 --- a/docs/kubectl_rolling-update.md +++ b/docs/kubectl_rolling-update.md @@ -68,4 +68,4 @@ $ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f - ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.859654934 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.450394023 +0000 UTC diff --git a/docs/kubectl_run-container.md b/docs/kubectl_run-container.md index b9c1c290b51..c795bbc0e54 100644 --- a/docs/kubectl_run-container.md +++ b/docs/kubectl_run-container.md @@ -78,4 +78,4 @@ $ kubectl run-container nginx --image=dockerfile/nginx --overrides='{ "apiVersio ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.861280128 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.4510703 +0000 UTC diff --git a/docs/kubectl_stop.md b/docs/kubectl_stop.md index c1fa7858133..5943de04363 100644 --- a/docs/kubectl_stop.md +++ b/docs/kubectl_stop.md @@ -72,4 +72,4 @@ $ kubectl stop -f path/to/resources ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.862654585 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.451196696 +0000 UTC diff --git a/docs/kubectl_update.md b/docs/kubectl_update.md index 02600a4b74c..fa40443f1d6 100644 --- a/docs/kubectl_update.md +++ b/docs/kubectl_update.md @@ -67,4 +67,4 @@ $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.858390462 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.449377932 +0000 UTC diff --git a/docs/kubectl_version.md b/docs/kubectl_version.md index eaf76fd9a81..f096797c00d 100644 --- a/docs/kubectl_version.md +++ b/docs/kubectl_version.md @@ -51,4 +51,4 @@ kubectl version ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-04-12 19:00:26.865600008 +0000 UTC +###### Auto generated by spf13/cobra at 2015-04-14 14:22:51.452926558 +0000 UTC diff --git a/docs/man/man1/kubectl-config-view.1 b/docs/man/man1/kubectl-config-view.1 index f929f6b40b2..47b4efdd609 100644 --- a/docs/man/man1/kubectl-config-view.1 +++ b/docs/man/man1/kubectl-config-view.1 @@ -3,7 +3,7 @@ .SH NAME .PP -kubectl config view \- displays merged kubeconfig settings or a specified kubeconfig file. +kubectl config view \- displays Merged kubeconfig settings or a specified kubeconfig file. .SH SYNOPSIS @@ -13,13 +13,17 @@ kubectl config view \- displays merged kubeconfig settings or a specified kubeco .SH DESCRIPTION .PP -displays merged kubeconfig settings or a specified kubeconfig file. +displays Merged kubeconfig settings or a specified kubeconfig file. .PP You can use \-\-output=template \-\-template=TEMPLATE to extract specific values. .SH OPTIONS +.PP +\fB\-\-flatten\fP=false + flatten the resulting kubeconfig file into self contained output (useful for creating portable kubeconfig files) + .PP \fB\-h\fP, \fB\-\-help\fP=false help for view @@ -28,6 +32,10 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values \fB\-\-merge\fP=true merge together the full hierarchy of kubeconfig files +.PP +\fB\-\-minify\fP=false + remove all information not used by current\-context from the output + .PP \fB\-\-no\-headers\fP=false When using the default output, don't print headers. @@ -165,7 +173,7 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values .RS .nf -// Show merged kubeconfig settings. +// Show Merged kubeconfig settings. $ kubectl config view // Show only local kubeconfig settings diff --git a/pkg/client/clientcmd/api/helpers.go b/pkg/client/clientcmd/api/helpers.go new file mode 100644 index 00000000000..93681cd83bd --- /dev/null +++ b/pkg/client/clientcmd/api/helpers.go @@ -0,0 +1,149 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 api + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" +) + +// MinifyConfig read the current context and uses that to keep only the relevant pieces of config +// This is useful for making secrets based on kubeconfig files +func MinifyConfig(config *Config) error { + if len(config.CurrentContext) == 0 { + return errors.New("current-context must exist in order to minify") + } + + currContext, exists := config.Contexts[config.CurrentContext] + if !exists { + return fmt.Errorf("cannot locate context %v", config.CurrentContext) + } + + newContexts := map[string]Context{} + newContexts[config.CurrentContext] = currContext + + newClusters := map[string]Cluster{} + if len(currContext.Cluster) > 0 { + if _, exists := config.Clusters[currContext.Cluster]; !exists { + return fmt.Errorf("cannot locate cluster %v", currContext.Cluster) + } + + newClusters[currContext.Cluster] = config.Clusters[currContext.Cluster] + } + + newAuthInfos := map[string]AuthInfo{} + if len(currContext.AuthInfo) > 0 { + if _, exists := config.AuthInfos[currContext.AuthInfo]; !exists { + return fmt.Errorf("cannot locate user %v", currContext.AuthInfo) + } + + newAuthInfos[currContext.AuthInfo] = config.AuthInfos[currContext.AuthInfo] + } + + config.AuthInfos = newAuthInfos + config.Clusters = newClusters + config.Contexts = newContexts + + return nil +} + +// Flatten changes the config object into a self contained config (useful for making secrets) +// AuthPath is not handled. +func FlattenConfig(config *Config) error { + for key, authInfo := range config.AuthInfos { + baseDir, err := MakeAbs(path.Dir(authInfo.LocationOfOrigin), "") + if err != nil { + return err + } + if len(authInfo.AuthPath) != 0 { + return fmt.Errorf("auth path of %v is not empty: %v", key, authInfo.AuthPath) + } + + if err := FlattenContent(&authInfo.ClientCertificate, &authInfo.ClientCertificateData, baseDir); err != nil { + return err + } + if err := FlattenContent(&authInfo.ClientKey, &authInfo.ClientKeyData, baseDir); err != nil { + return err + } + + config.AuthInfos[key] = authInfo + } + for key, cluster := range config.Clusters { + baseDir, err := MakeAbs(path.Dir(cluster.LocationOfOrigin), "") + if err != nil { + return err + } + + if err := FlattenContent(&cluster.CertificateAuthority, &cluster.CertificateAuthorityData, baseDir); err != nil { + return err + } + + config.Clusters[key] = cluster + } + + return nil +} + +func FlattenContent(path *string, contents *[]byte, baseDir string) error { + if len(*path) != 0 { + if len(*contents) > 0 { + return errors.New("cannot have values for both path and contents") + } + + var err error + absPath := ResolvePath(*path, baseDir) + *contents, err = ioutil.ReadFile(absPath) + if err != nil { + return err + } + + *path = "" + } + + return nil +} + +// ResolvePath returns the path as an absolute paths, relative to the given base directory +func ResolvePath(path string, base string) string { + // Don't resolve empty paths + if len(path) > 0 { + // Don't resolve absolute paths + if !filepath.IsAbs(path) { + return filepath.Join(base, path) + } + } + + return path +} + +func MakeAbs(path, base string) (string, error) { + if filepath.IsAbs(path) { + return path, nil + } + if len(base) == 0 { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + base = cwd + } + return filepath.Join(base, path), nil +} diff --git a/pkg/client/clientcmd/api/helpers_test.go b/pkg/client/clientcmd/api/helpers_test.go new file mode 100644 index 00000000000..7bbd5da0ad1 --- /dev/null +++ b/pkg/client/clientcmd/api/helpers_test.go @@ -0,0 +1,204 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 api + +import ( + "io/ioutil" + "os" + "reflect" + "testing" +) + +func newMergedConfig(certFile, certContent, keyFile, keyContent, caFile, caContent string, t *testing.T) Config { + if err := ioutil.WriteFile(certFile, []byte(certContent), 0644); err != nil { + t.Errorf("unexpected error: %v", err) + } + if err := ioutil.WriteFile(keyFile, []byte(keyContent), 0600); err != nil { + t.Errorf("unexpected error: %v", err) + } + if err := ioutil.WriteFile(caFile, []byte(caContent), 0644); err != nil { + t.Errorf("unexpected error: %v", err) + } + + return Config{ + AuthInfos: map[string]AuthInfo{ + "red-user": {Token: "red-token"}, + "blue-user": {Token: "blue-token", ClientCertificate: certFile, ClientKey: keyFile}}, + Clusters: map[string]Cluster{ + "cow-cluster": {Server: "http://cow.org:8080"}, + "chicken-cluster": {Server: "http://chicken.org:8080", CertificateAuthority: caFile}}, + Contexts: map[string]Context{ + "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}, + "shaker-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster"}}, + CurrentContext: "federal-context", + } +} + +func TestMinifySuccess(t *testing.T) { + certFile, _ := ioutil.TempFile("", "") + defer os.Remove(certFile.Name()) + keyFile, _ := ioutil.TempFile("", "") + defer os.Remove(keyFile.Name()) + caFile, _ := ioutil.TempFile("", "") + defer os.Remove(caFile.Name()) + + mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) + + if err := MinifyConfig(&mutatingConfig); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if len(mutatingConfig.Contexts) > 1 { + t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts) + } + if _, exists := mutatingConfig.Contexts["federal-context"]; !exists { + t.Errorf("missing context") + } + + if len(mutatingConfig.Clusters) > 1 { + t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters) + } + if _, exists := mutatingConfig.Clusters["cow-cluster"]; !exists { + t.Errorf("missing cluster") + } + + if len(mutatingConfig.AuthInfos) > 1 { + t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos) + } + if _, exists := mutatingConfig.AuthInfos["red-user"]; !exists { + t.Errorf("missing user") + } +} + +func TestMinifyMissingContext(t *testing.T) { + certFile, _ := ioutil.TempFile("", "") + defer os.Remove(certFile.Name()) + keyFile, _ := ioutil.TempFile("", "") + defer os.Remove(keyFile.Name()) + caFile, _ := ioutil.TempFile("", "") + defer os.Remove(caFile.Name()) + + mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) + mutatingConfig.CurrentContext = "missing" + + errMsg := "cannot locate context missing" + + if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { + t.Errorf("expected %v, got %v", errMsg, err) + } +} + +func TestMinifyMissingCluster(t *testing.T) { + certFile, _ := ioutil.TempFile("", "") + defer os.Remove(certFile.Name()) + keyFile, _ := ioutil.TempFile("", "") + defer os.Remove(keyFile.Name()) + caFile, _ := ioutil.TempFile("", "") + defer os.Remove(caFile.Name()) + + mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) + delete(mutatingConfig.Clusters, mutatingConfig.Contexts[mutatingConfig.CurrentContext].Cluster) + + errMsg := "cannot locate cluster cow-cluster" + + if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { + t.Errorf("expected %v, got %v", errMsg, err) + } +} + +func TestMinifyMissingAuthInfo(t *testing.T) { + certFile, _ := ioutil.TempFile("", "") + defer os.Remove(certFile.Name()) + keyFile, _ := ioutil.TempFile("", "") + defer os.Remove(keyFile.Name()) + caFile, _ := ioutil.TempFile("", "") + defer os.Remove(caFile.Name()) + + mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t) + delete(mutatingConfig.AuthInfos, mutatingConfig.Contexts[mutatingConfig.CurrentContext].AuthInfo) + + errMsg := "cannot locate user red-user" + + if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg { + t.Errorf("expected %v, got %v", errMsg, err) + } +} + +func TestFlattenSuccess(t *testing.T) { + certFile, _ := ioutil.TempFile("", "") + defer os.Remove(certFile.Name()) + keyFile, _ := ioutil.TempFile("", "") + defer os.Remove(keyFile.Name()) + caFile, _ := ioutil.TempFile("", "") + defer os.Remove(caFile.Name()) + + certData := "cert" + keyData := "key" + caData := "ca" + + unchangingCluster := "cow-cluster" + unchangingAuthInfo := "red-user" + changingCluster := "chicken-cluster" + changingAuthInfo := "blue-user" + + startingConfig := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, t) + mutatingConfig := startingConfig + + if err := FlattenConfig(&mutatingConfig); err != nil { + t.Errorf("unexpected error: %v", err) + } + + if len(mutatingConfig.Contexts) != 2 { + t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts) + } + if !reflect.DeepEqual(startingConfig.Contexts, mutatingConfig.Contexts) { + t.Errorf("expected %v, got %v", startingConfig.Contexts, mutatingConfig.Contexts) + } + + if len(mutatingConfig.Clusters) != 2 { + t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters) + } + if !reflect.DeepEqual(startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) { + t.Errorf("expected %v, got %v", startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) + } + if len(mutatingConfig.Clusters[changingCluster].CertificateAuthority) != 0 { + t.Errorf("unexpected caFile") + } + if string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData) != caData { + t.Errorf("expected %v, got %v", caData, string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData)) + } + + if len(mutatingConfig.AuthInfos) != 2 { + t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos) + } + if !reflect.DeepEqual(startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) { + t.Errorf("expected %v, got %v", startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) + } + if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificate) != 0 { + t.Errorf("unexpected caFile") + } + if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData) != certData { + t.Errorf("expected %v, got %v", certData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData)) + } + if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientKey) != 0 { + t.Errorf("unexpected caFile") + } + if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData) != keyData { + t.Errorf("expected %v, got %v", keyData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData)) + } + +} diff --git a/pkg/kubectl/cmd/config/config_test.go b/pkg/kubectl/cmd/config/config_test.go index 7bdf9df7ab9..4360d9d1f26 100644 --- a/pkg/kubectl/cmd/config/config_test.go +++ b/pkg/kubectl/cmd/config/config_test.go @@ -39,6 +39,7 @@ func newRedFederalCowHammerConfig() clientcmdapi.Config { "cow-cluster": {Server: "http://cow.org:8080"}}, Contexts: map[string]clientcmdapi.Context{ "federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"}}, + CurrentContext: "federal-context", } } @@ -70,7 +71,7 @@ func ExampleView() { // cluster: cow-cluster // user: red-user // name: federal-context - // current-context: "" + // current-context: federal-context // kind: Config // preferences: {} // users: diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index 41b69bb9a60..46b81b7b49a 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -31,16 +31,18 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/util" ) -type viewOptions struct { - pathOptions *PathOptions - merge util.BoolFlag +type ViewOptions struct { + PathOptions *PathOptions + Merge util.BoolFlag + Flatten bool + Minify bool } const ( - view_long = `displays merged kubeconfig settings or a specified kubeconfig file. + view_long = `displays Merged kubeconfig settings or a specified kubeconfig file. You can use --output=template --template=TEMPLATE to extract specific values.` - view_example = `// Show merged kubeconfig settings. + view_example = `// Show Merged kubeconfig settings. $ kubectl config view // Show only local kubeconfig settings @@ -50,16 +52,16 @@ $ kubectl config view --local $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2e" }}{{ index .user.password }}{{end}}{{end}}'` ) -func NewCmdConfigView(out io.Writer, pathOptions *PathOptions) *cobra.Command { - options := &viewOptions{pathOptions: pathOptions} +func NewCmdConfigView(out io.Writer, PathOptions *PathOptions) *cobra.Command { + options := &ViewOptions{PathOptions: PathOptions} cmd := &cobra.Command{ Use: "view", - Short: "displays merged kubeconfig settings or a specified kubeconfig file.", + Short: "displays Merged kubeconfig settings or a specified kubeconfig file.", Long: view_long, Example: view_example, Run: func(cmd *cobra.Command, args []string) { - options.complete() + options.Complete() printer, _, err := cmdutil.PrinterForCommand(cmd) if err != nil { @@ -68,15 +70,10 @@ func NewCmdConfigView(out io.Writer, pathOptions *PathOptions) *cobra.Command { version := cmdutil.OutputVersion(cmd, latest.Version) printer = kubectl.NewVersionedPrinter(printer, clientcmdapi.Scheme, version) - config, err := options.loadConfig() - if err != nil { + if err := options.Run(out, printer); err != nil { glog.FatalDepth(1, err) } - err = printer.PrintObj(config, out) - if err != nil { - glog.FatalDepth(1, err) - } }, } @@ -84,24 +81,52 @@ func NewCmdConfigView(out io.Writer, pathOptions *PathOptions) *cobra.Command { // Default to yaml cmd.Flags().Set("output", "yaml") - options.merge.Default(true) - cmd.Flags().Var(&options.merge, "merge", "merge together the full hierarchy of kubeconfig files") + options.Merge.Default(true) + cmd.Flags().Var(&options.Merge, "merge", "merge together the full hierarchy of kubeconfig files") + cmd.Flags().BoolVar(&options.Flatten, "flatten", false, "flatten the resulting kubeconfig file into self contained output (useful for creating portable kubeconfig files)") + cmd.Flags().BoolVar(&options.Minify, "minify", false, "remove all information not used by current-context from the output") return cmd } -func (o *viewOptions) complete() bool { +func (o ViewOptions) Run(out io.Writer, printer kubectl.ResourcePrinter) error { + config, err := o.loadConfig() + if err != nil { + return err + } + + if o.Minify { + if err := clientcmdapi.MinifyConfig(config); err != nil { + return err + } + } + + if o.Flatten { + if err := clientcmdapi.FlattenConfig(config); err != nil { + return err + } + } + + err = printer.PrintObj(config, out) + if err != nil { + return err + } + + return nil +} + +func (o *ViewOptions) Complete() bool { // if --kubeconfig, --global, or --local is specified, then merging doesn't make sense since you're declaring precise intent - if o.pathOptions.Global || o.pathOptions.Local || o.pathOptions.UseEnvVar { - if !o.merge.Provided() { - o.merge.Set("false") + if o.PathOptions.Global || o.PathOptions.Local || o.PathOptions.UseEnvVar { + if !o.Merge.Provided() { + o.Merge.Set("false") } } return true } -func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) { - err := o.validate() +func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) { + err := o.Validate() if err != nil { return nil, err } @@ -110,33 +135,33 @@ func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) { return config, err } -func (o viewOptions) validate() error { - return o.pathOptions.Validate() +func (o ViewOptions) Validate() error { + return o.PathOptions.Validate() } // getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong -func (o *viewOptions) getStartingConfig() (*clientcmdapi.Config, error) { +func (o *ViewOptions) getStartingConfig() (*clientcmdapi.Config, error) { switch { - case !o.merge.Value(): + case !o.Merge.Value(): switch { - case len(o.pathOptions.LoadingRules.ExplicitPath) > 0: - return clientcmd.LoadFromFile(o.pathOptions.LoadingRules.ExplicitPath) + case len(o.PathOptions.LoadingRules.ExplicitPath) > 0: + return clientcmd.LoadFromFile(o.PathOptions.LoadingRules.ExplicitPath) - case o.pathOptions.Global: - return clientcmd.LoadFromFile(o.pathOptions.GlobalFile) + case o.PathOptions.Global: + return clientcmd.LoadFromFile(o.PathOptions.GlobalFile) - case o.pathOptions.UseEnvVar: - return clientcmd.LoadFromFile(o.pathOptions.EnvVarFile) + case o.PathOptions.UseEnvVar: + return clientcmd.LoadFromFile(o.PathOptions.EnvVarFile) - case o.pathOptions.Local: - return clientcmd.LoadFromFile(o.pathOptions.LocalFile) + case o.PathOptions.Local: + return clientcmd.LoadFromFile(o.PathOptions.LocalFile) default: - return nil, errors.New("if merge==false a precise file must to specified") + return nil, errors.New("if Merge==false a precise file must to specified") } default: - return o.pathOptions.getStartingConfig() + return o.PathOptions.getStartingConfig() } }