...

Source file src/k8s.io/kubectl/pkg/cmd/version/version.go

Documentation: k8s.io/kubectl/pkg/cmd/version

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package version
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"runtime/debug"
    24  
    25  	"github.com/spf13/cobra"
    26  	"sigs.k8s.io/yaml"
    27  
    28  	apimachineryversion "k8s.io/apimachinery/pkg/version"
    29  	"k8s.io/cli-runtime/pkg/genericiooptions"
    30  	"k8s.io/client-go/discovery"
    31  	"k8s.io/client-go/tools/clientcmd"
    32  	"k8s.io/component-base/version"
    33  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    34  	"k8s.io/kubectl/pkg/util/i18n"
    35  	"k8s.io/kubectl/pkg/util/templates"
    36  )
    37  
    38  // TODO(knverey): remove this hardcoding once kubectl being built with module support makes BuildInfo available.
    39  const kustomizeVersion = "v5.0.1"
    40  
    41  // Version is a struct for version information
    42  type Version struct {
    43  	ClientVersion    *apimachineryversion.Info `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"`
    44  	KustomizeVersion string                    `json:"kustomizeVersion,omitempty" yaml:"kustomizeVersion,omitempty"`
    45  	ServerVersion    *apimachineryversion.Info `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
    46  }
    47  
    48  var (
    49  	versionExample = templates.Examples(i18n.T(`
    50  		# Print the client and server versions for the current context
    51  		kubectl version`))
    52  )
    53  
    54  // Options is a struct to support version command
    55  type Options struct {
    56  	ClientOnly bool
    57  	Output     string
    58  
    59  	args []string
    60  
    61  	discoveryClient discovery.CachedDiscoveryInterface
    62  
    63  	genericiooptions.IOStreams
    64  }
    65  
    66  // NewOptions returns initialized Options
    67  func NewOptions(ioStreams genericiooptions.IOStreams) *Options {
    68  	return &Options{
    69  		IOStreams: ioStreams,
    70  	}
    71  
    72  }
    73  
    74  // NewCmdVersion returns a cobra command for fetching versions
    75  func NewCmdVersion(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
    76  	o := NewOptions(ioStreams)
    77  	cmd := &cobra.Command{
    78  		Use:     "version",
    79  		Short:   i18n.T("Print the client and server version information"),
    80  		Long:    i18n.T("Print the client and server version information for the current context."),
    81  		Example: versionExample,
    82  		Run: func(cmd *cobra.Command, args []string) {
    83  			cmdutil.CheckErr(o.Complete(f, cmd, args))
    84  			cmdutil.CheckErr(o.Validate())
    85  			cmdutil.CheckErr(o.Run())
    86  		},
    87  	}
    88  	cmd.Flags().BoolVar(&o.ClientOnly, "client", o.ClientOnly, "If true, shows client version only (no server required).")
    89  	cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.")
    90  	return cmd
    91  }
    92  
    93  // Complete completes all the required options
    94  func (o *Options) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
    95  	var err error
    96  	if o.ClientOnly {
    97  		return nil
    98  	}
    99  	o.discoveryClient, err = f.ToDiscoveryClient()
   100  	// if we had an empty rest.Config, continue and just print out client information.
   101  	// if we had an error other than being unable to build a rest.Config, fail.
   102  	if err != nil && !clientcmd.IsEmptyConfig(err) {
   103  		return err
   104  	}
   105  
   106  	o.args = args
   107  	return nil
   108  }
   109  
   110  // Validate validates the provided options
   111  func (o *Options) Validate() error {
   112  	if len(o.args) != 0 {
   113  		return errors.New(fmt.Sprintf("extra arguments: %v", o.args))
   114  	}
   115  
   116  	if o.Output != "" && o.Output != "yaml" && o.Output != "json" {
   117  		return errors.New(`--output must be 'yaml' or 'json'`)
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  // Run executes version command
   124  func (o *Options) Run() error {
   125  	var (
   126  		serverErr   error
   127  		versionInfo Version
   128  	)
   129  
   130  	versionInfo.ClientVersion = func() *apimachineryversion.Info { v := version.Get(); return &v }()
   131  	versionInfo.KustomizeVersion = getKustomizeVersion()
   132  
   133  	if !o.ClientOnly && o.discoveryClient != nil {
   134  		// Always request fresh data from the server
   135  		o.discoveryClient.Invalidate()
   136  		versionInfo.ServerVersion, serverErr = o.discoveryClient.ServerVersion()
   137  	}
   138  
   139  	switch o.Output {
   140  	case "":
   141  		fmt.Fprintf(o.Out, "Client Version: %s\n", versionInfo.ClientVersion.GitVersion)
   142  		fmt.Fprintf(o.Out, "Kustomize Version: %s\n", versionInfo.KustomizeVersion)
   143  		if versionInfo.ServerVersion != nil {
   144  			fmt.Fprintf(o.Out, "Server Version: %s\n", versionInfo.ServerVersion.GitVersion)
   145  		}
   146  	case "yaml":
   147  		marshalled, err := yaml.Marshal(&versionInfo)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		fmt.Fprintln(o.Out, string(marshalled))
   152  	case "json":
   153  		marshalled, err := json.MarshalIndent(&versionInfo, "", "  ")
   154  		if err != nil {
   155  			return err
   156  		}
   157  		fmt.Fprintln(o.Out, string(marshalled))
   158  	default:
   159  		// There is a bug in the program if we hit this case.
   160  		// However, we follow a policy of never panicking.
   161  		return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", o.Output)
   162  	}
   163  
   164  	if versionInfo.ServerVersion != nil {
   165  		if err := printVersionSkewWarning(o.ErrOut, *versionInfo.ClientVersion, *versionInfo.ServerVersion); err != nil {
   166  			return err
   167  		}
   168  	}
   169  
   170  	return serverErr
   171  }
   172  
   173  func getKustomizeVersion() string {
   174  	if modVersion, ok := GetKustomizeModVersion(); ok {
   175  		return modVersion
   176  	}
   177  	return kustomizeVersion // other clients should provide their own fallback
   178  }
   179  
   180  func GetKustomizeModVersion() (string, bool) {
   181  	info, ok := debug.ReadBuildInfo()
   182  	if !ok {
   183  		return "", false
   184  	}
   185  	for _, dep := range info.Deps {
   186  		if dep.Path == "sigs.k8s.io/kustomize/kustomize/v4" || dep.Path == "sigs.k8s.io/kustomize/kustomize/v5" {
   187  			return dep.Version, true
   188  		}
   189  	}
   190  	return "", false
   191  }
   192  

View as plain text