From bd9ec009fa52c56b701df1a748ee6fa60e2527c1 Mon Sep 17 00:00:00 2001 From: cdjianghan <52602064+cdjianghan@users.noreply.github.com> Date: Tue, 7 Feb 2023 19:13:50 +0800 Subject: [PATCH] add kubernetes version cri version output in sealos verison cmd (#2511) --- cmd/sealos/cmd/version.go | 66 ++++++++++++++++++++++++++++++++++++--- pkg/version/types.go | 54 +++++++++++++++++++++++++++----- pkg/version/version.go | 46 +++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 12 deletions(-) diff --git a/cmd/sealos/cmd/version.go b/cmd/sealos/cmd/version.go index c7fb512d2..ca1e0f901 100644 --- a/cmd/sealos/cmd/version.go +++ b/cmd/sealos/cmd/version.go @@ -17,7 +17,13 @@ package cmd import ( "encoding/json" "fmt" + "strings" + "github.com/pkg/errors" + + "sigs.k8s.io/yaml" + + "github.com/labring/sealos/pkg/clusterfile" "github.com/labring/sealos/pkg/constants" "github.com/labring/sealos/pkg/version" @@ -26,6 +32,7 @@ import ( ) var shortPrint bool +var output string func newVersionCmd() *cobra.Command { var versionCmd = &cobra.Command{ @@ -34,19 +41,23 @@ func newVersionCmd() *cobra.Command { Args: cobra.NoArgs, Example: `sealos version`, RunE: func(cmd *cobra.Command, args []string) error { - marshalled, err := json.Marshal(version.Get()) - if err != nil { - return err + //output default to be yaml + if output != "yaml" && output != "json" { + return errors.New(`--output must be 'yaml' or 'json'`) } + if shortPrint { fmt.Println(version.Get().String()) - } else { - fmt.Println(string(marshalled)) + return nil + } + if err := PrintInfo(); err != nil { + return err } return nil }, } versionCmd.Flags().BoolVar(&shortPrint, "short", false, "if true, print just the version number.") + versionCmd.Flags().StringVarP(&output, "output", "o", "yaml", "One of 'yaml' or 'json'") return versionCmd } @@ -57,3 +68,48 @@ func init() { func getContact() string { return fmt.Sprintf(constants.Contact, version.Get().String()) } + +func PrintInfo() error { + var ( + marshalled []byte + ) + OutputInfo := &version.Output{} + OutputInfo.SealosVersion = version.Get() + cluster, err := clusterfile.GetClusterFromName(clusterName) + if err != nil { + return errors.Wrap(err, "fail to find cluster from name") + } + OutputInfo.KubernetesVersion = version.GetKubernetesVersion(cluster) + OutputInfo.CriRuntimeVersion = version.GetCriRuntimeVersion() + + switch output { + case "yaml": + marshalled, err = yaml.Marshal(&OutputInfo) + if err != nil { + return errors.Wrap(err, "fail to marshal yaml") + } + fmt.Println(string(marshalled)) + case "json": + marshalled, err = json.Marshal(&OutputInfo) + if err != nil { + return errors.Wrap(err, "fail to marshal json") + } + fmt.Println(string(marshalled)) + default: + // There is a bug in the program if we hit this case. + // However, we follow a policy of never panicking. + return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", output) + } + missinfo := []string{} + if OutputInfo.KubernetesVersion == nil { + missinfo = append(missinfo, "kubernetes version") + } + if OutputInfo.CriRuntimeVersion == nil { + missinfo = append(missinfo, "cri runtime version") + } + if OutputInfo.KubernetesVersion == nil || OutputInfo.CriRuntimeVersion == nil { + fmt.Printf("failed to get %s\ncheck kubernetes status or use commend \"sealos run\" to launch kubernetes\n", strings.Join(missinfo, " and ")) + } + + return nil +} diff --git a/pkg/version/types.go b/pkg/version/types.go index e6e9e93a9..9ee80479b 100644 --- a/pkg/version/types.go +++ b/pkg/version/types.go @@ -16,21 +16,61 @@ limitations under the License. package version -import "fmt" +import ( + "fmt" +) // Info contains versioning information. // TODO: Add []string of api versions supported? It's still unclear // how we'll want to distribute that information. type Info struct { - GitVersion string `json:"gitVersion"` - GitCommit string `json:"gitCommit,omitempty"` - BuildDate string `json:"buildDate"` - GoVersion string `json:"goVersion"` - Compiler string `json:"compiler"` - Platform string `json:"platform"` + GitVersion string `json:"gitVersion" yaml:"gitVersion"` + GitCommit string `json:"gitCommit,omitempty" yaml:"gitCommit,omitempty"` + BuildDate string `json:"buildDate" yaml:"buildDate"` + GoVersion string `json:"goVersion" yaml:"goVersion"` + Compiler string `json:"compiler" yaml:"compiler"` + Platform string `json:"platform" yaml:"platform"` } // String returns info as a human-friendly version string. func (info Info) String() string { return fmt.Sprintf("%s-%s", info.GitVersion, info.GitCommit) } + +type Output struct { + SealosVersion Info `json:"SealosVersion,omitempty" yaml:"SealosVersion,omitempty"` + CriRuntimeVersion *CriRuntimeVersion `json:"CriVersionInfo,omitempty" yaml:"CriVersionInfo,omitempty"` + KubernetesVersion *KubernetesVersion `json:"KubernetesVersionInfo,omitempty" yaml:"KubernetesVersionInfo,omitempty"` +} + +type CriRuntimeVersion struct { + // Version of the kubelet runtime API. + Version string `json:"Version,omitempty" yaml:"Version,omitempty"` + // Name of the container runtime. + RuntimeName string `json:"RuntimeName,omitempty" yaml:"RuntimeName,omitempty"` + // Version of the container runtime. The string must be + // semver-compatible. + RuntimeVersion string `json:"RuntimeVersion,omitempty" yaml:"RuntimeVersion,omitempty"` + // API version of the container runtime. The string must be + // semver-compatible. + RuntimeAPIVersion string `json:"RuntimeApiVersion,omitempty" yaml:"RuntimeApiVersion,omitempty"` +} + +// Version is a struct for version information +type KubernetesVersion struct { + ClientVersion *KubectlInfo `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"` + KustomizeVersion string `json:"kustomizeVersion,omitempty" yaml:"kustomizeVersion,omitempty"` + ServerVersion *KubectlInfo `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"` +} + +type KubectlInfo struct { + Major string `json:"major" yaml:"major"` + Minor string `json:"minor" yaml:"minor"` + GitVersion string `json:"gitVersion" yaml:"gitVersion"` + GitCommit string `json:"gitCommit" yaml:"gitCommit"` + GitTreeState string `json:"gitTreeState" yaml:"gitTreeState"` + BuildDate string `json:"buildDate" yaml:"buildDate"` + GoVersion string `json:"goVersion" yaml:"goVersion"` + Compiler string `json:"compiler" yaml:"compiler"` + Platform string `json:"platform" yaml:"platform"` +} diff --git a/pkg/version/version.go b/pkg/version/version.go index a0cb2f60e..c29a44673 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -19,6 +19,16 @@ package version import ( "fmt" "runtime" + + "github.com/labring/sealos/pkg/utils/logger" + + "gopkg.in/yaml.v2" + + "github.com/labring/sealos/pkg/utils/exec" + + "github.com/labring/sealos/pkg/constants" + + v2 "github.com/labring/sealos/pkg/types/v1beta1" ) // Get returns the overall codebase version. It's for detecting @@ -35,3 +45,39 @@ func Get() Info { Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), } } + +func GetKubernetesVersion(cluster *v2.Cluster) *KubernetesVersion { + var cmd string + if cluster == nil { + cmd = "kubectl version -o yaml" + } else { + data := constants.NewData(cluster.Name) + cmd = fmt.Sprintf("kubectl version --kubeconfig %s -o yaml", data.AdminFile()) + } + serverVersion, err := exec.RunBashCmd(cmd) + if err != nil || serverVersion == "" { + logger.Debug(err, "get kubernetes version failed") + return nil + } + var Unmarshaled KubernetesVersion + if err = yaml.Unmarshal([]byte(serverVersion), &Unmarshaled); err != nil { + logger.Debug(err, "unmarshal kubernetes version failed") + return nil + } + return &Unmarshaled +} + +func GetCriRuntimeVersion() *CriRuntimeVersion { + cmd := "crictl version" + criRuntimeVersion, err := exec.RunBashCmd(cmd) + if err != nil || criRuntimeVersion == "" { + logger.Debug(err, "get cri runtime version failed") + return nil + } + var Unmarshaled CriRuntimeVersion + if err = yaml.Unmarshal([]byte(criRuntimeVersion), &Unmarshaled); err != nil { + logger.Debug(err, "unmarshal cri runtime version failed") + return nil + } + return &Unmarshaled +}