Merge pull request #25463 from asalkeld/master
Automatic merge from submit-queue Add command "kubectl config get-contexts" ```release-note * A new command "kubectl config get-contexts" has been added. ``` fixes #25383
This commit is contained in:
		| @@ -15,6 +15,7 @@ docs/man/man1/kubectl-cluster-info-dump.1 | ||||
| docs/man/man1/kubectl-cluster-info.1 | ||||
| docs/man/man1/kubectl-completion.1 | ||||
| docs/man/man1/kubectl-config-current-context.1 | ||||
| docs/man/man1/kubectl-config-get-contexts.1 | ||||
| docs/man/man1/kubectl-config-set-cluster.1 | ||||
| docs/man/man1/kubectl-config-set-context.1 | ||||
| docs/man/man1/kubectl-config-set-credentials.1 | ||||
| @@ -75,6 +76,7 @@ docs/user-guide/kubectl/kubectl_cluster-info_dump.md | ||||
| docs/user-guide/kubectl/kubectl_completion.md | ||||
| docs/user-guide/kubectl/kubectl_config.md | ||||
| docs/user-guide/kubectl/kubectl_config_current-context.md | ||||
| docs/user-guide/kubectl/kubectl_config_get-contexts.md | ||||
| docs/user-guide/kubectl/kubectl_config_set-cluster.md | ||||
| docs/user-guide/kubectl/kubectl_config_set-context.md | ||||
| docs/user-guide/kubectl/kubectl_config_set-credentials.md | ||||
|   | ||||
| @@ -2,11 +2,9 @@ | ||||
|  | ||||
| - [v1.3.0](#v130) | ||||
|   - [Downloads](#downloads) | ||||
|   - [Major Themes](#major-themes) | ||||
|   - [Other notable improvements](#other-notable-improvements) | ||||
|   - [Known Issues](#known-issues) | ||||
|   - [Highlights](#highlights) | ||||
|   - [Known Issues and Important Steps before Upgrading](#known-issues-and-important-steps-before-upgrading) | ||||
|   - [Provider-specific Notes](#provider-specific-notes) | ||||
|   - [Changelog since v1.3.0-beta.3](#changelog-since-v130-beta3) | ||||
|     - [Previous Releases Included in v1.3.0](#previous-releases-included-in-v130) | ||||
| - [v1.3.0-beta.3](#v130-beta3) | ||||
|   - [Downloads](#downloads) | ||||
|   | ||||
							
								
								
									
										3
									
								
								docs/man/man1/kubectl-config-get-contexts.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/man/man1/kubectl-config-get-contexts.1
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| This file is autogenerated, but we've stopped checking such files into the | ||||
| repository to reduce the need for rebases. Please run hack/generate-docs.sh to | ||||
| populate this file. | ||||
							
								
								
									
										36
									
								
								docs/user-guide/kubectl/kubectl_config_get-contexts.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								docs/user-guide/kubectl/kubectl_config_get-contexts.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <!-- BEGIN MUNGE: UNVERSIONED_WARNING --> | ||||
|  | ||||
| <!-- BEGIN STRIP_FOR_RELEASE --> | ||||
|  | ||||
| <img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||||
|      width="25" height="25"> | ||||
| <img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||||
|      width="25" height="25"> | ||||
| <img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||||
|      width="25" height="25"> | ||||
| <img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||||
|      width="25" height="25"> | ||||
| <img src="http://kubernetes.io/img/warning.png" alt="WARNING" | ||||
|      width="25" height="25"> | ||||
|  | ||||
| <h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2> | ||||
|  | ||||
| If you are using a released version of Kubernetes, you should | ||||
| refer to the docs that go with that version. | ||||
|  | ||||
| Documentation for other releases can be found at | ||||
| [releases.k8s.io](http://releases.k8s.io). | ||||
| </strong> | ||||
| -- | ||||
|  | ||||
| <!-- END STRIP_FOR_RELEASE --> | ||||
|  | ||||
| <!-- END MUNGE: UNVERSIONED_WARNING --> | ||||
|  | ||||
| This file is autogenerated, but we've stopped checking such files into the | ||||
| repository to reduce the need for rebases. Please run hack/generate-docs.sh to | ||||
| populate this file. | ||||
|  | ||||
| <!-- BEGIN MUNGE: GENERATED_ANALYTICS --> | ||||
| []() | ||||
| <!-- END MUNGE: GENERATED_ANALYTICS --> | ||||
| @@ -57,6 +57,7 @@ The loading order follows these rules: | ||||
| 	cmd.AddCommand(NewCmdConfigUnset(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigCurrentContext(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions)) | ||||
| 	cmd.AddCommand(NewCmdConfigGetContexts(out, pathOptions)) | ||||
|  | ||||
| 	return cmd | ||||
| } | ||||
|   | ||||
							
								
								
									
										168
									
								
								pkg/kubectl/cmd/config/get_contexts.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								pkg/kubectl/cmd/config/get_contexts.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"text/tabwriter" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd" | ||||
| 	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" | ||||
| 	"k8s.io/kubernetes/pkg/kubectl" | ||||
|  | ||||
| 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" | ||||
| 	utilerrors "k8s.io/kubernetes/pkg/util/errors" | ||||
| 	"k8s.io/kubernetes/pkg/util/sets" | ||||
| ) | ||||
|  | ||||
| // GetContextsOptions contains the assignable options from the args. | ||||
| type GetContextsOptions struct { | ||||
| 	configAccess clientcmd.ConfigAccess | ||||
| 	nameOnly     bool | ||||
| 	showHeaders  bool | ||||
| 	contextNames []string | ||||
| 	out          io.Writer | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	getContextsLong = `Displays one or many contexts from the kubeconfig file.` | ||||
|  | ||||
| 	getContextsExample = `# List all the contexts in your kubeconfig file | ||||
| kubectl config get-contexts | ||||
|  | ||||
| # Describe one context in your kubeconfig file. | ||||
| kubectl config get-contexts my-context` | ||||
| ) | ||||
|  | ||||
| // NewCmdConfigGetContexts creates a command object for the "get-contexts" action, which | ||||
| // retrieves one or more contexts from a kubeconfig. | ||||
| func NewCmdConfigGetContexts(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command { | ||||
| 	options := &GetContextsOptions{configAccess: configAccess} | ||||
|  | ||||
| 	cmd := &cobra.Command{ | ||||
| 		Use:     "get-contexts [(-o|--output=)name)]", | ||||
| 		Short:   "Describe one or many contexts", | ||||
| 		Long:    getContextsLong, | ||||
| 		Example: getContextsExample, | ||||
| 		Run: func(cmd *cobra.Command, args []string) { | ||||
| 			validOutputTypes := sets.NewString("", "json", "yaml", "wide", "name", "go-template", "go-template-file", "jsonpath", "jsonpath-file") | ||||
| 			supportedOutputTypes := sets.NewString("", "name") | ||||
| 			outputFormat := cmdutil.GetFlagString(cmd, "output") | ||||
| 			if !validOutputTypes.Has(outputFormat) { | ||||
| 				cmdutil.CheckErr(fmt.Errorf("output must be one of '' or 'name': %v", outputFormat)) | ||||
| 			} | ||||
| 			if !supportedOutputTypes.Has(outputFormat) { | ||||
| 				fmt.Fprintf(out, "--output %v is not available in kubectl config get-contexts; reseting to default output format", outputFormat) | ||||
| 				cmd.Flags().Set("output", "") | ||||
| 			} | ||||
| 			cmdutil.CheckErr(options.Complete(cmd, args, out)) | ||||
| 			cmdutil.CheckErr(options.RunGetContexts()) | ||||
| 		}, | ||||
| 	} | ||||
| 	cmdutil.AddOutputFlags(cmd) | ||||
| 	cmdutil.AddNoHeadersFlags(cmd) | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| // Complete assigns GetContextsOptions from the args. | ||||
| func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string, out io.Writer) error { | ||||
| 	o.contextNames = args | ||||
| 	o.out = out | ||||
| 	o.nameOnly = false | ||||
| 	if cmdutil.GetFlagString(cmd, "output") == "name" { | ||||
| 		o.nameOnly = true | ||||
| 	} | ||||
| 	o.showHeaders = true | ||||
| 	if cmdutil.GetFlagBool(cmd, "no-headers") || o.nameOnly { | ||||
| 		o.showHeaders = false | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RunGetContexts implements all the necessary functionality for context retrieval. | ||||
| func (o GetContextsOptions) RunGetContexts() error { | ||||
| 	config, err := o.configAccess.GetStartingConfig() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	out, found := o.out.(*tabwriter.Writer) | ||||
| 	if !found { | ||||
| 		out = kubectl.GetNewTabWriter(o.out) | ||||
| 		defer out.Flush() | ||||
| 	} | ||||
|  | ||||
| 	// Build a list of context names to print, and warn if any requested contexts are not found. | ||||
| 	// Do this before printing the headers so it doesn't look ugly. | ||||
| 	allErrs := []error{} | ||||
| 	toPrint := []string{} | ||||
| 	if len(o.contextNames) == 0 { | ||||
| 		for name := range config.Contexts { | ||||
| 			toPrint = append(toPrint, name) | ||||
| 		} | ||||
| 	} else { | ||||
| 		for _, name := range o.contextNames { | ||||
| 			_, ok := config.Contexts[name] | ||||
| 			if ok { | ||||
| 				toPrint = append(toPrint, name) | ||||
| 			} else { | ||||
| 				allErrs = append(allErrs, fmt.Errorf("context %v not found", name)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if o.showHeaders { | ||||
| 		err = printContextHeaders(out, o.nameOnly) | ||||
| 		if err != nil { | ||||
| 			allErrs = append(allErrs, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, name := range toPrint { | ||||
| 		err = printContext(name, config.Contexts[name], out, o.nameOnly, config.CurrentContext == name) | ||||
| 		if err != nil { | ||||
| 			allErrs = append(allErrs, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return utilerrors.NewAggregate(allErrs) | ||||
| } | ||||
|  | ||||
| func printContextHeaders(out io.Writer, nameOnly bool) error { | ||||
| 	columnNames := []string{"CURRENT", "NAME", "CLUSTER", "AUTHINFO", "NAMESPACE"} | ||||
| 	if nameOnly { | ||||
| 		columnNames = columnNames[:1] | ||||
| 	} | ||||
| 	_, err := fmt.Fprintf(out, "%s\n", strings.Join(columnNames, "\t")) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func printContext(name string, context *clientcmdapi.Context, w io.Writer, nameOnly, current bool) error { | ||||
| 	if nameOnly { | ||||
| 		_, err := fmt.Fprintf(w, "%s\n", name) | ||||
| 		return err | ||||
| 	} | ||||
| 	prefix := " " | ||||
| 	if current { | ||||
| 		prefix = "*" | ||||
| 	} | ||||
| 	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", prefix, name, context.Cluster, context.AuthInfo, context.Namespace) | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										158
									
								
								pkg/kubectl/cmd/config/get_contexts_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								pkg/kubectl/cmd/config/get_contexts_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| /* | ||||
| 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 config | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd" | ||||
| 	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" | ||||
| ) | ||||
|  | ||||
| type getContextsTest struct { | ||||
| 	startingConfig clientcmdapi.Config | ||||
| 	names          []string | ||||
| 	noHeader       bool | ||||
| 	nameOnly       bool | ||||
| 	expectedOut    string | ||||
| } | ||||
|  | ||||
| func TestGetContextsAll(t *testing.T) { | ||||
| 	tconf := clientcmdapi.Config{ | ||||
| 		CurrentContext: "shaker-context", | ||||
| 		Contexts: map[string]*clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: tconf, | ||||
| 		names:          []string{}, | ||||
| 		noHeader:       false, | ||||
| 		nameOnly:       false, | ||||
| 		expectedOut: `CURRENT   NAME             CLUSTER       AUTHINFO    NAMESPACE | ||||
| *         shaker-context   big-cluster   blue-user   saw-ns | ||||
| `, | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGetContextsAllNoHeader(t *testing.T) { | ||||
| 	tconf := clientcmdapi.Config{ | ||||
| 		CurrentContext: "shaker-context", | ||||
| 		Contexts: map[string]*clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: tconf, | ||||
| 		names:          []string{}, | ||||
| 		noHeader:       true, | ||||
| 		nameOnly:       false, | ||||
| 		expectedOut:    "*         shaker-context   big-cluster   blue-user   saw-ns\n", | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGetContextsAllName(t *testing.T) { | ||||
| 	tconf := clientcmdapi.Config{ | ||||
| 		Contexts: map[string]*clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: tconf, | ||||
| 		names:          []string{}, | ||||
| 		noHeader:       false, | ||||
| 		nameOnly:       true, | ||||
| 		expectedOut:    "shaker-context\n", | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGetContextsAllNameNoHeader(t *testing.T) { | ||||
| 	tconf := clientcmdapi.Config{ | ||||
| 		CurrentContext: "shaker-context", | ||||
| 		Contexts: map[string]*clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: tconf, | ||||
| 		names:          []string{}, | ||||
| 		noHeader:       true, | ||||
| 		nameOnly:       true, | ||||
| 		expectedOut:    "shaker-context\n", | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGetContextsAllNone(t *testing.T) { | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: *clientcmdapi.NewConfig(), | ||||
| 		names:          []string{}, | ||||
| 		noHeader:       true, | ||||
| 		nameOnly:       false, | ||||
| 		expectedOut:    "", | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGetContextsSelectOneOfTwo(t *testing.T) { | ||||
| 	tconf := clientcmdapi.Config{ | ||||
| 		CurrentContext: "shaker-context", | ||||
| 		Contexts: map[string]*clientcmdapi.Context{ | ||||
| 			"shaker-context": {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}, | ||||
| 			"not-this":       {AuthInfo: "blue-user", Cluster: "big-cluster", Namespace: "saw-ns"}}} | ||||
| 	test := getContextsTest{ | ||||
| 		startingConfig: tconf, | ||||
| 		names:          []string{"shaker-context"}, | ||||
| 		noHeader:       true, | ||||
| 		nameOnly:       true, | ||||
| 		expectedOut:    "shaker-context\n", | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func (test getContextsTest) run(t *testing.T) { | ||||
| 	fakeKubeFile, _ := ioutil.TempFile("", "") | ||||
| 	defer os.Remove(fakeKubeFile.Name()) | ||||
| 	err := clientcmd.WriteToFile(test.startingConfig, fakeKubeFile.Name()) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	pathOptions := clientcmd.NewDefaultPathOptions() | ||||
| 	pathOptions.GlobalFile = fakeKubeFile.Name() | ||||
| 	pathOptions.EnvVar = "" | ||||
| 	buf := bytes.NewBuffer([]byte{}) | ||||
| 	options := GetContextsOptions{ | ||||
| 		configAccess: pathOptions, | ||||
| 	} | ||||
| 	cmd := NewCmdConfigGetContexts(buf, options.configAccess) | ||||
| 	if test.nameOnly { | ||||
| 		cmd.Flags().Set("output", "name") | ||||
| 	} | ||||
| 	if test.noHeader { | ||||
| 		cmd.Flags().Set("no-headers", "true") | ||||
| 	} | ||||
| 	cmd.Run(cmd, test.names) | ||||
| 	if len(test.expectedOut) != 0 { | ||||
| 		if buf.String() != test.expectedOut { | ||||
| 			t.Errorf("Expected %v, but got %v", test.expectedOut, buf.String()) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		t.Errorf("Unexpected error: %v", err) | ||||
| 	} | ||||
| } | ||||
| @@ -30,9 +30,9 @@ import ( | ||||
|  | ||||
| // AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path) | ||||
| func AddPrinterFlags(cmd *cobra.Command) { | ||||
| 	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].") | ||||
| 	AddOutputFlags(cmd) | ||||
| 	cmd.Flags().String("output-version", "", "Output the formatted object with the given group version (for ex: 'extensions/v1beta1').") | ||||
| 	cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers.") | ||||
| 	AddNoHeadersFlags(cmd) | ||||
| 	cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)") | ||||
| 	cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].") | ||||
| 	cmd.MarkFlagFilename("template") | ||||
| @@ -45,6 +45,16 @@ func AddOutputFlagsForMutation(cmd *cobra.Command) { | ||||
| 	cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).") | ||||
| } | ||||
|  | ||||
| // AddOutputFlags adds output related flags to a command. | ||||
| func AddOutputFlags(cmd *cobra.Command) { | ||||
| 	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].") | ||||
| } | ||||
|  | ||||
| // AddNoHeadersFlags adds no-headers flags to a command. | ||||
| func AddNoHeadersFlags(cmd *cobra.Command) { | ||||
| 	cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers.") | ||||
| } | ||||
|  | ||||
| // PrintSuccess prints message after finishing mutating operations | ||||
| func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, operation string) { | ||||
| 	resource, _ = mapper.ResourceSingularizer(resource) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 k8s-merge-robot
					k8s-merge-robot