diff options
29 files changed, 1145 insertions, 166 deletions
diff --git a/edge/pkg/edgehub/task/taskexecutor/node_upgrade.go b/edge/pkg/edgehub/task/taskexecutor/node_upgrade.go index 8546ab96d..dacdb5f72 100644 --- a/edge/pkg/edgehub/task/taskexecutor/node_upgrade.go +++ b/edge/pkg/edgehub/task/taskexecutor/node_upgrade.go @@ -129,7 +129,7 @@ func upgrade(taskReq types.NodeTaskRequest) (event fsm.Event) { func keadmUpgrade(upgradeReq commontypes.NodeUpgradeJobRequest, opts *options.EdgeCoreOptions) error { klog.Infof("Begin to run upgrade command") - upgradeCmd := fmt.Sprintf("keadm upgrade --upgradeID %s --historyID %s --fromVersion %s --toVersion %s --config %s --image %s > /tmp/keadm.log 2>&1", + upgradeCmd := fmt.Sprintf("keadm upgrade edge --upgradeID %s --historyID %s --fromVersion %s --toVersion %s --config %s --image %s > /tmp/keadm.log 2>&1", upgradeReq.UpgradeID, upgradeReq.HistoryID, version.Get(), upgradeReq.Version, opts.ConfigFile, upgradeReq.Image) // run upgrade cmd to upgrade edge node diff --git a/keadm/cmd/keadm/app/cmd/cloud/gettoken.go b/keadm/cmd/keadm/app/cmd/cloud/gettoken.go index 9fb7c0d83..cb21a3aa5 100644 --- a/keadm/cmd/keadm/app/cmd/cloud/gettoken.go +++ b/keadm/cmd/keadm/app/cmd/cloud/gettoken.go @@ -48,7 +48,7 @@ func NewGettoken() *cobra.Command { } func addGettokenFlags(cmd *cobra.Command, gettokenOptions *common.GettokenOptions) { - cmd.Flags().StringVar(&gettokenOptions.Kubeconfig, common.KubeConfig, gettokenOptions.Kubeconfig, + cmd.Flags().StringVar(&gettokenOptions.Kubeconfig, common.FlagNameKubeConfig, gettokenOptions.Kubeconfig, "Use this key to set kube-config path, eg: $HOME/.kube/config") } diff --git a/keadm/cmd/keadm/app/cmd/cloud/init.go b/keadm/cmd/keadm/app/cmd/cloud/init.go index 1ba6dd9e2..aecd89f90 100644 --- a/keadm/cmd/keadm/app/cmd/cloud/init.go +++ b/keadm/cmd/keadm/app/cmd/cloud/init.go @@ -85,35 +85,35 @@ func newInitOptions() *types.InitOptions { } func addInitOtherFlags(cmd *cobra.Command, initOpts *types.InitOptions) { - cmd.Flags().StringVar(&initOpts.KubeEdgeVersion, types.KubeEdgeVersion, initOpts.KubeEdgeVersion, + cmd.Flags().StringVar(&initOpts.KubeEdgeVersion, types.FlagNameKubeEdgeVersion, initOpts.KubeEdgeVersion, "Use this key to set the default image tag") - cmd.Flags().StringVar(&initOpts.AdvertiseAddress, types.AdvertiseAddress, initOpts.AdvertiseAddress, + cmd.Flags().StringVar(&initOpts.AdvertiseAddress, types.FlagNameAdvertiseAddress, initOpts.AdvertiseAddress, "Use this key to set IPs in cloudcore's certificate SubAltNames field. eg: 10.10.102.78,10.10.102.79") - cmd.Flags().StringVar(&initOpts.KubeConfig, types.KubeConfig, initOpts.KubeConfig, + cmd.Flags().StringVar(&initOpts.KubeConfig, types.FlagNameKubeConfig, initOpts.KubeConfig, "Use this key to set kube-config path, eg: $HOME/.kube/config") - cmd.Flags().StringVar(&initOpts.Manifests, types.Manifests, initOpts.Manifests, + cmd.Flags().StringVar(&initOpts.Manifests, types.FlagNameManifests, initOpts.Manifests, "Allow appending file directories of k8s resources to keadm, separated by commas") - cmd.Flags().StringVarP(&initOpts.Manifests, types.Files, "f", initOpts.Manifests, + cmd.Flags().StringVarP(&initOpts.Manifests, types.FlagNameFiles, "f", initOpts.Manifests, "Allow appending file directories of k8s resources to keadm, separated by commas") - cmd.Flags().BoolVarP(&initOpts.DryRun, types.DryRun, "d", initOpts.DryRun, + cmd.Flags().BoolVarP(&initOpts.DryRun, types.FlagNameDryRun, "d", initOpts.DryRun, "Print the generated k8s resources on the stdout, not actual execute. Always use in debug mode") - cmd.Flags().StringVar(&initOpts.ExternalHelmRoot, types.ExternalHelmRoot, initOpts.ExternalHelmRoot, + cmd.Flags().StringVar(&initOpts.ExternalHelmRoot, types.FlagNameExternalHelmRoot, initOpts.ExternalHelmRoot, "Add external helm root path to keadm.") } func addHelmValueOptionsFlags(cmd *cobra.Command, initOpts *types.InitOptions) { - cmd.Flags().StringArrayVar(&initOpts.Sets, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") - cmd.Flags().StringVar(&initOpts.Profile, "profile", initOpts.Profile, fmt.Sprintf("Set profile on the command line (iptablesMgrMode=external or version=v%s)", types.DefaultKubeEdgeVersion)) + cmd.Flags().StringArrayVar(&initOpts.Sets, types.FlagNameSet, []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") + cmd.Flags().StringVar(&initOpts.Profile, types.FlagNameProfile, initOpts.Profile, fmt.Sprintf("Set profile on the command line (iptablesMgrMode=external or version=v%s)", types.DefaultKubeEdgeVersion)) } func addForceOptionsFlags(cmd *cobra.Command, initOpts *types.InitOptions) { - cmd.Flags().BoolVar(&initOpts.Force, types.Force, initOpts.Force, + cmd.Flags().BoolVar(&initOpts.Force, types.FlagNameForce, initOpts.Force, "Forced installing the cloud components without waiting.") } diff --git a/keadm/cmd/keadm/app/cmd/cloud/manifest.go b/keadm/cmd/keadm/app/cmd/cloud/manifest.go index 44a8b81dd..ca7c5ac75 100644 --- a/keadm/cmd/keadm/app/cmd/cloud/manifest.go +++ b/keadm/cmd/keadm/app/cmd/cloud/manifest.go @@ -96,7 +96,7 @@ func addManifestsGenerateJoinOtherFlags(cmd *cobra.Command, initOpts *types.Init addInitOtherFlags(cmd, initOpts) addHelmValueOptionsFlags(cmd, initOpts) - cmd.Flags().BoolVar(&initOpts.SkipCRDs, types.SkipCRDs, initOpts.SkipCRDs, + cmd.Flags().BoolVar(&initOpts.SkipCRDs, types.FlagNameSkipCRDs, initOpts.SkipCRDs, "Skip printing the contents of CRDs to stdout") } diff --git a/keadm/cmd/keadm/app/cmd/cloud/upgrade.go b/keadm/cmd/keadm/app/cmd/cloud/upgrade.go new file mode 100644 index 000000000..21081beb6 --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/cloud/upgrade.go @@ -0,0 +1,85 @@ +/* +Copyright 2024 The KubeEdge 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 cloud + +import ( + "github.com/spf13/cobra" + + types "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common" + "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/helm" +) + +func NewCloudUpgrade() *cobra.Command { + opts := newCloudUpgradeOptions() + + cmd := &cobra.Command{ + Use: "cloud", + Short: "Upgrade the cloud components", + Long: "Upgrade the cloud components to the desired version, " + + "it uses helm to upgrade the installed release of cloudcore chart, which includes all the cloud components", + RunE: func(cmd *cobra.Command, args []string) error { + tool := helm.NewCloudCoreHelmTool(opts.KubeConfig, opts.KubeEdgeVersion) + return tool.Upgrade(opts) + }, + } + + addUpgradeOptionFlags(cmd, opts) + return cmd +} + +func newCloudUpgradeOptions() *types.CloudUpgradeOptions { + opts := &types.CloudUpgradeOptions{} + opts.KubeConfig = types.DefaultKubeConfig + return opts +} + +func addUpgradeOptionFlags(cmd *cobra.Command, opts *types.CloudUpgradeOptions) { + fs := cmd.Flags() + + fs.StringVar(&opts.KubeEdgeVersion, types.FlagNameKubeEdgeVersion, opts.KubeEdgeVersion, + "Use this key to set the upgrade image tag") + + fs.StringVar(&opts.AdvertiseAddress, types.FlagNameAdvertiseAddress, opts.AdvertiseAddress, + "Please set the same value as when you installed it, this value is only used to generate the configuration"+ + " and does not regenerate the certificate. eg: 10.10.102.78,10.10.102.79") + + fs.StringVar(&opts.KubeConfig, types.FlagNameKubeConfig, opts.KubeConfig, + "Use this key to update kube-config path, eg: $HOME/.kube/config") + + fs.BoolVarP(&opts.DryRun, types.FlagNameDryRun, "d", opts.DryRun, + "Print the generated k8s resources on the stdout, not actual execute. Always use in debug mode") + + fs.StringArrayVar(&opts.Sets, types.FlagNameSet, []string{}, + "Sets values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") + + fs.StringArrayVar(&opts.ValueFiles, types.FlagNameValueFiles, []string{}, + "specify values in a YAML file (can specify multiple)") + + fs.BoolVar(&opts.Force, types.FlagNameForce, opts.Force, + "Forced upgrading the cloud components without waiting") + + fs.StringVar(&opts.Profile, types.FlagNameProfile, opts.Profile, + "Sets profile on the command line. If '--values' is specified, this is ignored") + + fs.StringVar(&opts.ExternalHelmRoot, types.FlagNameExternalHelmRoot, opts.ExternalHelmRoot, + "Add external helm root path to keadm") + + fs.BoolVar(&opts.ReuseValues, types.FlagNameReuseValues, false, + "reuse the last release's values and merge in any overrides from the command line via --set and -f.") + + fs.BoolVar(&opts.PrintFinalValues, types.FlagNamePrintFinalValues, false, + "Print the final values configuration for debuging") +} diff --git a/keadm/cmd/keadm/app/cmd/cmd_others.go b/keadm/cmd/keadm/app/cmd/cmd_others.go index 56c394608..30004e279 100644 --- a/keadm/cmd/keadm/app/cmd/cmd_others.go +++ b/keadm/cmd/keadm/app/cmd/cmd_others.go @@ -87,7 +87,7 @@ func NewKubeedgeCommand() *cobra.Command { // beta cmds cmds.AddCommand(beta.NewBeta()) - cmds.AddCommand(edge.NewEdgeUpgrade()) + cmds.AddCommand(NewUpgradeCommand()) cmds.AddCommand(edge.NewEdgeRollback()) diff --git a/keadm/cmd/keadm/app/cmd/common/constant.go b/keadm/cmd/keadm/app/cmd/common/constant.go index 2593ba645..b7fdc7012 100644 --- a/keadm/cmd/keadm/app/cmd/common/constant.go +++ b/keadm/cmd/keadm/app/cmd/common/constant.go @@ -16,87 +16,123 @@ limitations under the License. package common +// Common flag names const ( - // KubeEdgeVersion sets the version of KubeEdge to be used - KubeEdgeVersion = "kubeedge-version" + // FlagNameForce force install + FlagNameForce = "force" - // ImageRepository sets the image repository to pull images - ImageRepository = "image-repository" + // FlagNameKubeEdgeVersion sets the version of KubeEdge to be used + FlagNameKubeEdgeVersion = "kubeedge-version" +) - // KubeConfig sets the path of kubeconfig - KubeConfig = "kube-config" +// Cloud init and upgrade common flag names +const ( + // FlagNameKubeConfig sets the path of kubeconfig + FlagNameKubeConfig = "kube-config" - // Master sets the address of K8s master - Master = "master" + // FlagNameAdvertiseAddress ... + FlagNameAdvertiseAddress = "advertise-address" - // CloudCoreIPPort sets the IP and port of KubeEdge cloud component - CloudCoreIPPort = "cloudcore-ipport" + // FlagNameProfile flag name for install and upgrade in cloud + FlagNameProfile = "profile" - // EdgeNodeName is KubeEdge node unique identification string - EdgeNodeName = "edgenode-name" + // FlagNameExternalHelmRoot external Helm Root + FlagNameExternalHelmRoot = "external-helm-root" - // RemoteRuntimeEndpoint is KubeEdge remote-runtime-endpoint string - RemoteRuntimeEndpoint = "remote-runtime-endpoint" + // FlagNameSet ... + FlagNameSet = "set" - // CertPath sets the path of the certificates generated by the KubeEdge Cloud component - CertPath = "certPath" + // FlagNameValueFiles ... + FlagNameValueFiles = "values" - // DefaultK8SMinimumVersion is the minimum version of K8S - DefaultK8SMinimumVersion = 11 + // FlagNameDryRun Dry-run flag + FlagNameDryRun = "dry-run" - // DefaultKubeEdgeVersion is the default KubeEdge version, it must have no prefix 'v' - DefaultKubeEdgeVersion = "1.15.1" + // FlagNamePrintFinalValues ... + FlagNamePrintFinalValues = "print-final-values" +) - // Token sets the token used when edge applying for the certificate - Token = "token" +// Cloud init flag names +const ( + // FlagNameSkipCRDs skip CRDs + FlagNameSkipCRDs = "skip-crds" - // CertPort is the port where to apply for the edge certificate - CertPort = "certport" + // FlagNameMaster sets the address of K8s master + // Deprecated: only used in deprecated/init.go + FlagNameMaster = "master" - AdvertiseAddress = "advertise-address" + // FlagNameManifests allow appending manifests paths of manifests to keadm, separated by commas + FlagNameManifests = "manifests" - TokenSecretName = "tokensecret" + // FlagNameFiles allow appending manifests paths of manifests to keadm, separated by commas, another supported flag + FlagNameFiles = "files" +) - TokenDataName = "tokendata" +// Cloud upgrade flag names +const ( + // FlagNameReuseValues ... + FlagNameReuseValues = "reuse-values" +) - DomainName = "domainname" +// Edge join flag names +const ( + // FlagNameImageRepository sets the image repository to pull images + FlagNameImageRepository = "image-repository" - Labels = "labels" + // FlagNameCloudCoreIPPort sets the IP and port of KubeEdge cloud component + FlagNameCloudCoreIPPort = "cloudcore-ipport" - // CGroupDriver is type of edgecore Cgroup - CGroupDriver = "cgroupdriver" + // FlagNameEdgeNodeName is KubeEdge node unique identification string + FlagNameEdgeNodeName = "edgenode-name" - // TarballPath sets the temp directory path for KubeEdge tarball, if not exist, download it - // eg. "/tmp/kubeedge" or "/etc/kubeedge" by default - TarballPath = "tarballpath" + // FlagNameRemoteRuntimeEndpoint is KubeEdge remote-runtime-endpoint string + FlagNameRemoteRuntimeEndpoint = "remote-runtime-endpoint" - StrCheck = "check" - StrDiagnose = "diagnose" + // FlagNameCertPath sets the path of the certificates generated by the KubeEdge Cloud component + FlagNameCertPath = "certPath" + + // FlagNameRuntimeType is default runtime type + FlagNameRuntimeType = "runtimetype" - // Init-beta flags combined below: + // FlagNameToken sets the token used when edge applying for the certificate + FlagNameToken = "token" - // Allow appending manifests paths of manifests to keadm, separated by commas - Manifests = "manifests" + // FlagNameCertPort is the port where to apply for the edge certificate + FlagNameCertPort = "certport" - // Allow appending manifests paths of manifests to keadm, separated by commas, another supported flag - Files = "files" + // FlagNameDomainName ... + // Deprecated: only used in deprecated/init.go + FlagNameDomainName = "domainname" - // Dry-run flag - DryRun = "dry-run" + // FlagNameLabels ... + FlagNameLabels = "labels" - // Forced install - Force = "force" + // FlagNameCGroupDriver is type of edgecore Cgroup + FlagNameCGroupDriver = "cgroupdriver" - // Skip CRDs - SkipCRDs = "skip-crds" + // FlagNameTarballPath sets the temp directory path for KubeEdge tarball, if not exist, download it + // eg. "/tmp/kubeedge" or "/etc/kubeedge" by default + FlagNameTarballPath = "tarballpath" +) - // External Helm Root - ExternalHelmRoot = "external-helm-root" +const ( + // DefaultK8SMinimumVersion is the minimum version of K8S + DefaultK8SMinimumVersion = 11 + + // DefaultKubeEdgeVersion is the default KubeEdge version, it must have no prefix 'v' + DefaultKubeEdgeVersion = "1.15.1" // Helm action HelmInstallAction = "install" HelmManifestAction = "manifest" + // Token secret + TokenSecretName = "tokensecret" + TokenDataName = "tokendata" + + StrCheck = "check" + StrDiagnose = "diagnose" + HubProtocol = "hub-protocol" CmdGetDNSIP = "cat /etc/resolv.conf | grep nameserver | grep -v -E ':|#' | awk '{print $2}' | head -n1" diff --git a/keadm/cmd/keadm/app/cmd/common/types.go b/keadm/cmd/keadm/app/cmd/common/types.go index 36a16eb80..5b1a75daa 100644 --- a/keadm/cmd/keadm/app/cmd/common/types.go +++ b/keadm/cmd/keadm/app/cmd/common/types.go @@ -17,37 +17,73 @@ limitations under the License. package common import ( + "fmt" + "strings" + "github.com/blang/semver" ) -// InitBaseOptions has the kubeedge cloud deprecated init base information filled by CLI -type InitBaseOptions struct { - KubeEdgeVersion string - KubeConfig string - Master string - AdvertiseAddress string - DNS string - TarballPath string -} - -// InitOptions has the kubeedge cloud init information filled by CLI -type InitOptions struct { +// CloudInitUpdateBase defines common flags for init and upgrade in the cloud. +type CloudInitUpdateBase struct { KubeConfig string KubeEdgeVersion string AdvertiseAddress string - Manifests string - Namespace string - Sets []string Profile string ExternalHelmRoot string + Sets []string + ValueFiles []string Force bool - SkipCRDs bool DryRun bool + PrintFinalValues bool } -// JoinOptions has the kubeedge cloud init information filled by CLI +const requiredSetSplitLen = 2 + +// GetValidSets returns a valid sets, if the item is an invalid key-value, +// it is removed from the sets and print the error message. +func (b CloudInitUpdateBase) GetValidSets() []string { + if b.Sets == nil { + return nil + } + res := make([]string, 0, len(b.Sets)) + for _, s := range b.Sets { + p := strings.SplitN(s, "=", requiredSetSplitLen) + if len(p) != requiredSetSplitLen { + fmt.Println("Unsupported sets flag: ", s) + continue + } + res = append(res, s) + } + return res +} + +// HasSets returns the key is in the sets +func (b CloudInitUpdateBase) HasSets(key string) bool { + for _, kv := range b.Sets { + p := strings.SplitN(kv, "=", requiredSetSplitLen) + if len(p) == requiredSetSplitLen && p[0] == key { + return true + } + } + return false +} + +// InitOptions defines cloud init flags +type InitOptions struct { + Manifests string + SkipCRDs bool + CloudInitUpdateBase +} + +// CloudUpgradeOptions defines cloud upgrade flags +type CloudUpgradeOptions struct { + ReuseValues bool + CloudInitUpdateBase +} + +// JoinOptions defines edge join flags type JoinOptions struct { - InitBaseOptions + KubeEdgeVersion string CertPath string CloudCoreIPPort string EdgeNodeName string @@ -56,11 +92,14 @@ type JoinOptions struct { CertPort string CGroupDriver string Labels []string + // WithMQTT ... // Deprecated: the mqtt broker is alreay managed by the DaemonSet in the cloud - WithMQTT bool + WithMQTT bool + ImageRepository string HubProtocol string + TarballPath string } type CheckOptions struct { diff --git a/keadm/cmd/keadm/app/cmd/common/types_test.go b/keadm/cmd/keadm/app/cmd/common/types_test.go new file mode 100644 index 000000000..cb49ea5f2 --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/common/types_test.go @@ -0,0 +1,49 @@ +package common + +import ( + "fmt" + "testing" +) + +func TestCloudInitUpdateBaseHasSets(t *testing.T) { + key := "key" + cases := []struct { + sets []string + want bool + }{ + {sets: []string{""}, want: false}, + {sets: []string{"key"}, want: false}, + {sets: []string{"key="}, want: true}, + {sets: []string{"key=value"}, want: true}, + {sets: []string{"key=value=1"}, want: true}, + {sets: []string{"key2=value"}, want: false}, + } + + for _, c := range cases { + obj := CloudInitUpdateBase{Sets: c.sets} + t.Run(fmt.Sprint(c.sets), func(t *testing.T) { + b := obj.HasSets(key) + if b != c.want { + t.Fatalf("failed to test HasSets, expected: %t, actual: %t", c.want, b) + } + }) + } +} + +func TestCloudInitUpdateBaseGetValidSets(t *testing.T) { + wantLen := 4 + sets := []string{ + "", + "key1", + "key2=", + "key3=value", + "key4=value=1", + "key4=value=====", + } + obj := CloudInitUpdateBase{Sets: sets} + res := obj.GetValidSets() + if len(res) != wantLen { + t.Fatalf("failed to test GetValidSets, expected sets len: %d,"+ + " actual sets len: %d", wantLen, len(res)) + } +} diff --git a/keadm/cmd/keadm/app/cmd/config.go b/keadm/cmd/keadm/app/cmd/config.go index b0eae8bc0..6510d9379 100644 --- a/keadm/cmd/keadm/app/cmd/config.go +++ b/keadm/cmd/keadm/app/cmd/config.go @@ -137,16 +137,16 @@ func pullImages(endpoint, cgroupDriver string, images []string) error { // AddImagesCommonConfigFlags adds the flags that configure keadm func AddImagesCommonConfigFlags(cmd *cobra.Command, cfg *Configuration) { - cmd.Flags().StringVar(&cfg.KubeEdgeVersion, cmdcommon.KubeEdgeVersion, cfg.KubeEdgeVersion, + cmd.Flags().StringVar(&cfg.KubeEdgeVersion, cmdcommon.FlagNameKubeEdgeVersion, cfg.KubeEdgeVersion, `Use this key to decide which a specific KubeEdge version to be used.`, ) - cmd.Flags().StringVar(&cfg.ImageRepository, cmdcommon.ImageRepository, cfg.ImageRepository, + cmd.Flags().StringVar(&cfg.ImageRepository, cmdcommon.FlagNameImageRepository, cfg.ImageRepository, `Use this key to decide which image repository to pull images from.`, ) cmd.Flags().StringVar(&cfg.Part, "part", cfg.Part, "Use this key to set which part keadm will install: cloud part or edge part. If not set, keadm will list/pull all images used by both cloud part and edge part.") - cmd.Flags().StringVar(&cfg.RemoteRuntimeEndpoint, cmdcommon.RemoteRuntimeEndpoint, cfg.RemoteRuntimeEndpoint, + cmd.Flags().StringVar(&cfg.RemoteRuntimeEndpoint, cmdcommon.FlagNameRemoteRuntimeEndpoint, cfg.RemoteRuntimeEndpoint, "The endpoint of remote runtime service in edge node") } @@ -158,20 +158,16 @@ func GetKubeEdgeImages(cfg *Configuration) []string { images = image.CloudSet(cfg.ImageRepository, cfg.KubeEdgeVersion).List() case "edge": images = image.EdgeSet(&cmdcommon.JoinOptions{ - WithMQTT: false, - InitBaseOptions: cmdcommon.InitBaseOptions{ - KubeEdgeVersion: cfg.KubeEdgeVersion, - }, + WithMQTT: false, + KubeEdgeVersion: cfg.KubeEdgeVersion, ImageRepository: cfg.ImageRepository, }).List() default: // if not specified, will return all images used by both cloud part and edge part cloudSet := image.CloudSet(cfg.ImageRepository, cfg.KubeEdgeVersion) edgeSet := image.EdgeSet(&cmdcommon.JoinOptions{ - WithMQTT: false, - InitBaseOptions: cmdcommon.InitBaseOptions{ - KubeEdgeVersion: cfg.KubeEdgeVersion, - }, + WithMQTT: false, + KubeEdgeVersion: cfg.KubeEdgeVersion, ImageRepository: cfg.ImageRepository, }) images = cloudSet.Merge(edgeSet).List() diff --git a/keadm/cmd/keadm/app/cmd/deprecated/init.go b/keadm/cmd/keadm/app/cmd/deprecated/init.go index 5c9754785..ac35c6b28 100644 --- a/keadm/cmd/keadm/app/cmd/deprecated/init.go +++ b/keadm/cmd/keadm/app/cmd/deprecated/init.go @@ -76,38 +76,48 @@ func NewDeprecatedCloudInit() *cobra.Command { return cmd } +// InitBaseOptions has the kubeedge cloud deprecated init base information filled by CLI +type InitBaseOptions struct { + KubeEdgeVersion string + KubeConfig string + Master string + AdvertiseAddress string + DNS string + TarballPath string +} + // newInitOptions will initialise new instance of options everytime -func newInitOptions() *types.InitBaseOptions { - opts := &types.InitBaseOptions{} +func newInitOptions() *InitBaseOptions { + opts := &InitBaseOptions{} opts.KubeConfig = types.DefaultKubeConfig return opts } -func addInitFlags(cmd *cobra.Command, initOpts *types.InitBaseOptions) { - cmd.Flags().StringVar(&initOpts.KubeEdgeVersion, types.KubeEdgeVersion, initOpts.KubeEdgeVersion, +func addInitFlags(cmd *cobra.Command, initOpts *InitBaseOptions) { + cmd.Flags().StringVar(&initOpts.KubeEdgeVersion, types.FlagNameKubeEdgeVersion, initOpts.KubeEdgeVersion, "Use this key to download and use the required KubeEdge version") - cmd.Flags().Lookup(types.KubeEdgeVersion).NoOptDefVal = initOpts.KubeEdgeVersion + cmd.Flags().Lookup(types.FlagNameKubeEdgeVersion).NoOptDefVal = initOpts.KubeEdgeVersion - cmd.Flags().StringVar(&initOpts.KubeConfig, types.KubeConfig, initOpts.KubeConfig, + cmd.Flags().StringVar(&initOpts.KubeConfig, types.FlagNameKubeConfig, initOpts.KubeConfig, "Use this key to set kube-config path, eg: $HOME/.kube/config") - cmd.Flags().StringVar(&initOpts.Master, types.Master, initOpts.Master, + cmd.Flags().StringVar(&initOpts.Master, types.FlagNameMaster, initOpts.Master, "Use this key to set K8s master address, eg: http://127.0.0.1:8080") - cmd.Flags().StringVar(&initOpts.AdvertiseAddress, types.AdvertiseAddress, initOpts.AdvertiseAddress, + cmd.Flags().StringVar(&initOpts.AdvertiseAddress, types.FlagNameAdvertiseAddress, initOpts.AdvertiseAddress, "Use this key to set IPs in cloudcore's certificate SubAltNames field. eg: 10.10.102.78,10.10.102.79") - cmd.Flags().StringVar(&initOpts.DNS, types.DomainName, initOpts.DNS, + cmd.Flags().StringVar(&initOpts.DNS, types.FlagNameDomainName, initOpts.DNS, "Use this key to set domain names in cloudcore's certificate SubAltNames field. eg: www.cloudcore.cn,www.kubeedge.cn") - cmd.Flags().StringVar(&initOpts.TarballPath, types.TarballPath, initOpts.TarballPath, + cmd.Flags().StringVar(&initOpts.TarballPath, types.FlagNameTarballPath, initOpts.TarballPath, "Use this key to set the temp directory path for KubeEdge tarball, if not exist, download it") } // Add2CloudToolsList Reads the flagData (containing val and default val) and join options to fill the list of tools. -func Add2CloudToolsList(toolList map[string]types.ToolsInstaller, flagData map[string]types.FlagData, initOptions *types.InitBaseOptions) error { +func Add2CloudToolsList(toolList map[string]types.ToolsInstaller, flagData map[string]types.FlagData, initOptions *InitBaseOptions) error { var kubeVer string - flgData, ok := flagData[types.KubeEdgeVersion] + flgData, ok := flagData[types.FlagNameKubeEdgeVersion] if ok { kubeVer = util.CheckIfAvailable(flgData.Val.(string), flgData.DefVal.(string)) } diff --git a/keadm/cmd/keadm/app/cmd/deprecated/join.go b/keadm/cmd/keadm/app/cmd/deprecated/join.go index cf63f208e..acc10ef41 100644 --- a/keadm/cmd/keadm/app/cmd/deprecated/join.go +++ b/keadm/cmd/keadm/app/cmd/deprecated/join.go @@ -94,7 +94,7 @@ func newJoinOptions() *types.JoinOptions { func Add2EdgeToolsList(toolList map[string]types.ToolsInstaller, flagData map[string]types.FlagData, joinOptions *types.JoinOptions) error { var kubeVer string - flgData, ok := flagData[types.KubeEdgeVersion] + flgData, ok := flagData[types.FlagNameKubeEdgeVersion] if ok { kubeVer = util.CheckIfAvailable(flgData.Val.(string), flgData.DefVal.(string)) } diff --git a/keadm/cmd/keadm/app/cmd/deprecated/reset.go b/keadm/cmd/keadm/app/cmd/deprecated/reset.go index 2805db950..a5766c8fd 100644 --- a/keadm/cmd/keadm/app/cmd/deprecated/reset.go +++ b/keadm/cmd/keadm/app/cmd/deprecated/reset.go @@ -179,7 +179,7 @@ func cleanDirectories(isEdgeNode bool) error { } func addResetFlags(cmd *cobra.Command, resetOpts *common.ResetOptions) { - cmd.Flags().StringVar(&resetOpts.Kubeconfig, common.KubeConfig, resetOpts.Kubeconfig, + cmd.Flags().StringVar(&resetOpts.Kubeconfig, common.FlagNameKubeConfig, resetOpts.Kubeconfig, "Use this key to set kube-config path, eg: $HOME/.kube/config") cmd.Flags().BoolVar(&resetOpts.Force, "force", resetOpts.Force, "Reset the node without prompting for confirmation") diff --git a/keadm/cmd/keadm/app/cmd/edge/join_others.go b/keadm/cmd/keadm/app/cmd/edge/join_others.go index 28dd90a0e..9ad778c71 100644 --- a/keadm/cmd/keadm/app/cmd/edge/join_others.go +++ b/keadm/cmd/keadm/app/cmd/edge/join_others.go @@ -43,39 +43,39 @@ import ( ) func AddJoinOtherFlags(cmd *cobra.Command, joinOptions *common.JoinOptions) { - cmd.Flags().StringVar(&joinOptions.KubeEdgeVersion, common.KubeEdgeVersion, joinOptions.KubeEdgeVersion, + cmd.Flags().StringVar(&joinOptions.KubeEdgeVersion, common.FlagNameKubeEdgeVersion, joinOptions.KubeEdgeVersion, "Use this key to download and use the required KubeEdge version") - cmd.Flags().Lookup(common.KubeEdgeVersion).NoOptDefVal = joinOptions.KubeEdgeVersion + cmd.Flags().Lookup(common.FlagNameKubeEdgeVersion).NoOptDefVal = joinOptions.KubeEdgeVersion - cmd.Flags().StringVar(&joinOptions.CGroupDriver, common.CGroupDriver, joinOptions.CGroupDriver, + cmd.Flags().StringVar(&joinOptions.CGroupDriver, common.FlagNameCGroupDriver, joinOptions.CGroupDriver, "CGroupDriver that uses to manipulate cgroups on the host (cgroupfs or systemd), the default value is cgroupfs") - cmd.Flags().StringVar(&joinOptions.CertPath, common.CertPath, joinOptions.CertPath, + cmd.Flags().StringVar(&joinOptions.CertPath, common.FlagNameCertPath, joinOptions.CertPath, fmt.Sprintf("The certPath used by edgecore, the default value is %s", common.DefaultCertPath)) - cmd.Flags().StringVarP(&joinOptions.CloudCoreIPPort, common.CloudCoreIPPort, "e", joinOptions.CloudCoreIPPort, + cmd.Flags().StringVarP(&joinOptions.CloudCoreIPPort, common.FlagNameCloudCoreIPPort, "e", joinOptions.CloudCoreIPPort, "IP:Port address of KubeEdge CloudCore") - if err := cmd.MarkFlagRequired(common.CloudCoreIPPort); err != nil { + if err := cmd.MarkFlagRequired(common.FlagNameCloudCoreIPPort); err != nil { fmt.Printf("mark flag required failed with error: %v\n", err) } - cmd.Flags().StringVarP(&joinOptions.EdgeNodeName, common.EdgeNodeName, "i", joinOptions.EdgeNodeName, + cmd.Flags().StringVarP(&joinOptions.EdgeNodeName, common.FlagNameEdgeNodeName, "i", joinOptions.EdgeNodeName, "KubeEdge Node unique identification string, if flag not used then the command will generate a unique id on its own") - cmd.Flags().StringVarP(&joinOptions.RemoteRuntimeEndpoint, common.RemoteRuntimeEndpoint, "p", joinOptions.RemoteRuntimeEndpoint, + cmd.Flags().StringVarP(&joinOptions.RemoteRuntimeEndpoint, common.FlagNameRemoteRuntimeEndpoint, "p", joinOptions.RemoteRuntimeEndpoint, "KubeEdge Edge Node RemoteRuntimeEndpoint string.") - cmd.Flags().StringVarP(&joinOptions.Token, common.Token, "t", joinOptions.Token, + cmd.Flags().StringVarP(&joinOptions.Token, common.FlagNameToken, "t", joinOptions.Token, "Used for edge to apply for the certificate") - cmd.Flags().StringVarP(&joinOptions.CertPort, common.CertPort, "s", joinOptions.CertPort, + cmd.Flags().StringVarP(&joinOptions.CertPort, common.FlagNameCertPort, "s", joinOptions.CertPort, "The port where to apply for the edge certificate") - cmd.Flags().StringVar(&joinOptions.TarballPath, common.TarballPath, joinOptions.TarballPath, + cmd.Flags().StringVar(&joinOptions.TarballPath, common.FlagNameTarballPath, joinOptions.TarballPath, "Use this key to set the temp directory path for KubeEdge tarball, if not exist, download it") - cmd.Flags().StringSliceVarP(&joinOptions.Labels, common.Labels, "l", joinOptions.Labels, + cmd.Flags().StringSliceVarP(&joinOptions.Labels, common.FlagNameLabels, "l", joinOptions.Labels, `Use this key to set the customized labels for node, you can input customized labels like key1=value1,key2=value2`) // FIXME: cleanup this code when the static pod mqtt broker no longer needs to be compatible @@ -86,7 +86,7 @@ func AddJoinOtherFlags(cmd *cobra.Command, joinOptions *common.JoinOptions) { klog.Warning("falied to mark the flag with-mqtt to deprecated, err: %v", err) } - cmd.Flags().StringVar(&joinOptions.ImageRepository, common.ImageRepository, joinOptions.ImageRepository, + cmd.Flags().StringVar(&joinOptions.ImageRepository, common.FlagNameImageRepository, joinOptions.ImageRepository, `Use this key to decide which image repository to pull images from`, ) diff --git a/keadm/cmd/keadm/app/cmd/edge/upgrade.go b/keadm/cmd/keadm/app/cmd/edge/upgrade.go index 2d5f934d8..773d3754a 100644 --- a/keadm/cmd/keadm/app/cmd/edge/upgrade.go +++ b/keadm/cmd/keadm/app/cmd/edge/upgrade.go @@ -46,9 +46,9 @@ func NewEdgeUpgrade() *cobra.Command { upgradeOptions := newUpgradeOptions() cmd := &cobra.Command{ - Use: "upgrade", - Short: "Upgrade edge component. Upgrade the edge node to the desired version.", - Long: "Upgrade edge component. Upgrade the edge node to the desired version.", + Use: "edge", + Short: "Upgrade edge components", + Long: "Upgrade edge components. Upgrade the edge node to the desired version.", RunE: func(cmd *cobra.Command, args []string) error { // upgrade edgecore return upgradeOptions.upgrade() diff --git a/keadm/cmd/keadm/app/cmd/helm/cloudcore.go b/keadm/cmd/keadm/app/cmd/helm/cloudcore.go new file mode 100644 index 000000000..93618eccd --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/cloudcore.go @@ -0,0 +1,325 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import ( + "context" + "fmt" + "path" + "strings" + "time" + + "github.com/blang/semver" + "helm.sh/helm/v3/pkg/action" + helmcli "helm.sh/helm/v3/pkg/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" + + "github.com/kubeedge/kubeedge/common/constants" + types "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common" + "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/util" + kecharts "github.com/kubeedge/kubeedge/manifests" +) + +const ( + cloudCoreHelmComponent = "cloudcore" + + dirCharts = "charts" + dirProfiles = "profiles" + + valuesFileName = "values.yaml" + + messageFormatInstallationSuccess = `%s started +=========CHART DETAILS======= +Name: %s +LAST DEPLOYED: %s +NAMESPACE: %s +STATUS: %s +REVISION: %d +` + messageFormatFinalValues = "FINAL VALUES:\n%s" + + messageFormatUpgradationPrintConfig = `This is cloudcore configuration of the previous version. +If you want to revert configuration items, please manually modify the configmap 'cloudcore' +and restart the cloudcore: +%s` +) + +const ( + defaultHelmInstall = true + defaultHelmWait = true + defaultHelmCreateNs = true +) + +var setsKeyImageTags = []string{"cloudCore.image.tag", "iptablesManager.image.tag", "controllerManager.image.tag"} + +var helmSettings = helmcli.New() + +// CloudCoreHelmTool a cloudcore helm chart operation wrapping tool +type CloudCoreHelmTool struct { + util.Common +} + +// NewCloudCoreHelmTool creates a new instance of CloudCoreHelmTool +func NewCloudCoreHelmTool(kubeConfig, kubeedgeVersion string) *CloudCoreHelmTool { + common := util.Common{ + ToolVersion: semver.MustParse(util.GetHelmVersion(kubeedgeVersion, util.RetryTimes)), + KubeConfig: kubeConfig, + OSTypeInstaller: util.GetOSInterface(), + } + common.OSTypeInstaller.SetKubeEdgeVersion(common.ToolVersion) + return &CloudCoreHelmTool{ + Common: common, + } +} + +// Install uses helm client to install cloudcore release +func (c *CloudCoreHelmTool) Install(opts *types.InitOptions) error { + // The flag --force would not care about whether the cloud components exist or not also. + // If gives a external helm root, no need to check and verify, because it is always not a cloudcore. + if !opts.Force && opts.ExternalHelmRoot == "" { + if err := c.verifyCloudCoreProcessRunning(); err != nil { + return err + } + } + if err := c.Common.OSTypeInstaller.IsK8SComponentInstalled(c.Common.KubeConfig, c.Common.Master); err != nil { + return fmt.Errorf("failed to verify k8s component installed, err: %v", err) + } + + fmt.Println("Kubernetes version verification passed, KubeEdge installation will start...") + + appendDefaultSets(opts.KubeEdgeVersion, opts.AdvertiseAddress, &opts.CloudInitUpdateBase) + // Load profile values, and merges the sets flag + vals, err := MergeProfileValues(getValuesFile(opts.Profile), opts.GetValidSets()) + if err != nil { + return err + } + + // TODO: think about how to support addons, and should we support addons? + subDir := path.Join(dirCharts, cloudCoreHelmComponent) + componentName := cloudCoreHelmComponent + + // Build a new renderer instance + renderer := NewGenericRenderer(kecharts.BuiltinOrDir(opts.ExternalHelmRoot), + subDir, componentName, constants.SystemNamespace, vals, opts.SkipCRDs) + // Load the charts to this renderer + if err := renderer.LoadChart(); err != nil { + return fmt.Errorf("cannot load the given charts %s, error: %s", renderer.componentName, err.Error()) + } + + helper, err := NewHelper(opts.KubeConfig, constants.SystemNamespace) + if err != nil { + return err + } + // Determine whether the cloudcore release has been installed + if rel, err := helper.GetRelease(renderer.componentName); err != nil { + return err + } else if rel != nil { + return fmt.Errorf("the cloudcore release already exists, and you can upgrade the cloudcore using the `keadm upgrade`") + } + + // Install the helm release cloudcore + client := action.NewInstall(helper.GetConfig()) + client.DryRun = opts.DryRun + client.CreateNamespace = defaultHelmCreateNs + client.ReleaseName = renderer.componentName + // If the flag force is true, don't wait for the command result of helm install + if !opts.Force { + client.Wait = defaultHelmWait + client.Timeout = DefaultHelmTimeout + } + rel, err := client.Run(renderer.chart, vals) + if err != nil { + return fmt.Errorf("failed to install release %s, err: %v", + renderer.componentName, err) + } + + // Print installation successful message + var lastDeployed string + if !rel.Info.LastDeployed.IsZero() { + lastDeployed = rel.Info.LastDeployed.Format(time.ANSIC) + } + fmt.Printf(messageFormatInstallationSuccess, + strings.ToTitle(renderer.componentName), + rel.Name, + lastDeployed, + rel.Namespace, + rel.Info.Status.String(), + rel.Version) + if opts.PrintFinalValues { + cfgyml, err := yaml.Marshal(rel.Config) + if err != nil { + klog.Warningf("failed to marshal values, err: %v", err) + } + fmt.Printf(messageFormatFinalValues, string(cfgyml)) + } + return nil +} + +// Upgrade uses helm client to upgrade cloudcore release +func (c *CloudCoreHelmTool) Upgrade(opts *types.CloudUpgradeOptions) error { + if err := c.Common.OSTypeInstaller.IsK8SComponentInstalled(c.Common.KubeConfig, c.Common.Master); err != nil { + return fmt.Errorf("failed to verify k8s component installed, err: %v", err) + } + + fmt.Println("Kubernetes version verification passed, KubeEdge upgradation will start...") + + cloudcoreConfig, err := getCloudcoreHistoryConfig(opts.KubeConfig, constants.SystemNamespace) + if err != nil { + return fmt.Errorf("failed to get cloudcore history config, err: %v", err) + } + + componentName := cloudCoreHelmComponent + subDir := path.Join(dirCharts, cloudCoreHelmComponent) + + helper, err := NewHelper(opts.KubeConfig, constants.SystemNamespace) + if err != nil { + return err + } + // Determine whether the cloudcore release has been installed + if rel, err := helper.GetRelease(componentName); err != nil { + return err + } else if rel == nil { + return fmt.Errorf("the cloudcore release not found, and you can init the cloudcore using the `keadm init`") + } + + appendDefaultSets(opts.KubeEdgeVersion, opts.AdvertiseAddress, &opts.CloudInitUpdateBase) + + var vals map[string]interface{} + if len(opts.ValueFiles) == 0 && opts.Profile != "" { + // Load profile values, and merges the sets flag + vals, err = MergeProfileValues(getValuesFile(opts.Profile), opts.GetValidSets()) + if err != nil { + return err + } + } else { + valueOpts := &Options{ + ValueFiles: opts.ValueFiles, + Values: opts.GetValidSets(), + } + vals, err = valueOpts.MergeValues() + if err != nil { + return err + } + } + + // Build a new renderer instance + renderer := NewGenericRenderer(kecharts.BuiltinOrDir(""), + subDir, componentName, constants.SystemNamespace, vals, false) + // Load the charts to this renderer + if err := renderer.LoadChart(); err != nil { + return fmt.Errorf("cannot load the given charts %s, err: %s", renderer.componentName, err.Error()) + } + + // Upgrade the helm release cloudcore + client := action.NewUpgrade(helper.GetConfig()) + client.ReuseValues = opts.ReuseValues + client.DryRun = opts.DryRun + // If the flag force is true, don't wait for the command result of helm upgrade + if !opts.Force { + client.Wait = defaultHelmWait + client.Timeout = DefaultHelmTimeout + } + rel, err := client.Run(renderer.componentName, renderer.chart, vals) + if err != nil { + return fmt.Errorf("failed to upgrade release %s, err: %v", renderer.componentName, err) + } + + // Print upgradation successful message + var lastDeployed string + if !rel.Info.LastDeployed.IsZero() { + lastDeployed = rel.Info.LastDeployed.Format(time.ANSIC) + } + fmt.Printf(messageFormatInstallationSuccess, + strings.ToTitle(renderer.componentName), + rel.Name, + lastDeployed, + rel.Namespace, + rel.Info.Status.String(), + rel.Version) + + if opts.PrintFinalValues { + cfgyml, err := yaml.Marshal(rel.Config) + if err != nil { + klog.Warningf("failed to marshal values, err: %v", err) + } + fmt.Printf(messageFormatFinalValues, string(cfgyml)) + } + + fmt.Printf(messageFormatUpgradationPrintConfig, cloudcoreConfig) + return nil +} + +// Uninstall uses helm client to uninstall cloudcore release +func (c *CloudCoreHelmTool) Uninstall(opts *types.ResetOptions) error { + // clean kubeedge namespace + if err := c.Common.CleanNameSpace(constants.SystemNamespace, opts.Kubeconfig); err != nil { + return fmt.Errorf("failed to clean kubeedge namespace, err: %v", err) + } + return nil +} + +func (c *CloudCoreHelmTool) verifyCloudCoreProcessRunning() error { + cloudcoreRunning, err := c.Common.OSTypeInstaller.IsKubeEdgeProcessRunning(util.KubeCloudBinaryName) + if err != nil { + return fmt.Errorf("failed to verify the cloudcore binnary already running, err: %v", err) + } + if cloudcoreRunning { + return fmt.Errorf("the cloudcore is already running on this node, please run reset to clean up first") + } + return nil +} + +// appendDefaultSets sets some default values configuration to via --sets +func appendDefaultSets(version, advertiseAddress string, opts *types.CloudInitUpdateBase) { + if version != "" { + for _, k := range setsKeyImageTags { + if !opts.HasSets(k) { + opts.Sets = append(opts.Sets, fmt.Sprintf("%s=%s", k, version)) + } + } + } + if advertiseAddress != "" { + for index, addr := range strings.Split(advertiseAddress, ",") { + opts.Sets = append(opts.Sets, fmt.Sprintf("%s[%d]=%s", + "cloudCore.modules.cloudHub.advertiseAddress", index, addr)) + } + } +} + +// getCloudcoreHistoryConfig ... +func getCloudcoreHistoryConfig(kubeconfig, namespace string) (string, error) { + kcli, err := util.KubeClient(kubeconfig) + if err != nil { + return "", err + } + cm, err := kcli.CoreV1().ConfigMaps(namespace). + Get(context.TODO(), "cloudcore", metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("failed to get configmap cloudcore, err: %v", err) + } + return cm.Data["cloudcore.yaml"], nil +} + +// getValuesFile ... +func getValuesFile(profileKey string) string { + pf := profileKey + if !strings.HasSuffix(profileKey, ".yaml") { + pf += ".yaml" + } + return path.Join(dirProfiles, pf) +} diff --git a/keadm/cmd/keadm/app/cmd/helm/cloudcore_test.go b/keadm/cmd/keadm/app/cmd/helm/cloudcore_test.go new file mode 100644 index 000000000..82b82faf7 --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/cloudcore_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import ( + "fmt" + "testing" + + types "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/common" +) + +func TestAppendDefaultSets(t *testing.T) { + version := "v1.16.0" + advertiseAddress := "127.0.0.1" + componentsSize := len(setsKeyImageTags) + opts := types.CloudInitUpdateBase{ + Sets: []string{}, + } + appendDefaultSets(version, advertiseAddress, &opts) + if len(opts.Sets) != componentsSize+1 { + t.Fatalf("sets len want equal %d, but is %d", componentsSize, len(opts.Sets)) + } + var idx int + for idx = range setsKeyImageTags { + want := setsKeyImageTags[idx] + "=" + version + if opts.Sets[idx] != want { + t.Fatalf("sets item[%d] is not %s", idx, want) + } + } + want := "cloudCore.modules.cloudHub.advertiseAddress[0]=" + advertiseAddress + if opts.Sets[idx+1] != want { + t.Fatalf("sets item[%d] is not %s", idx+1, want) + } +} + +func TestGetValuesFile(t *testing.T) { + cases := []struct { + profile string + want string + }{ + {profile: "version", want: "profiles/version.yaml"}, + {profile: "version.yaml", want: "profiles/version.yaml"}, + } + for _, c := range cases { + t.Run(fmt.Sprintf("profile:%s", c.profile), func(t *testing.T) { + res := getValuesFile(c.profile) + if res != c.want { + t.Fatalf("failed to test getValuesFile, expected: %s, actual: %s", + c.want, res) + } + }) + } +} diff --git a/keadm/cmd/keadm/app/cmd/helm/helm_helper.go b/keadm/cmd/keadm/app/cmd/helm/helm_helper.go new file mode 100644 index 000000000..2cf5509fa --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/helm_helper.go @@ -0,0 +1,100 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import ( + "errors" + "fmt" + "io/fs" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/release" + "helm.sh/helm/v3/pkg/storage/driver" + "helm.sh/helm/v3/pkg/strvals" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/klog/v2" + "sigs.k8s.io/yaml" + + kecharts "github.com/kubeedge/kubeedge/manifests" +) + +// MergeProfileValues merges values from specified profile and directly via --set. +func MergeProfileValues(profile string, sets []string, +) (map[string]interface{}, error) { + bff, err := fs.ReadFile(kecharts.FS, profile) + if err != nil { + return nil, fmt.Errorf("failed to read build in profile '%s', err: %v", profile, err) + } + vals := make(map[string]interface{}) + if err := yaml.Unmarshal(bff, &vals); err != nil { + return nil, fmt.Errorf("failed to unmarshal values: %v", err) + } + klog.V(4).Infof("combine values: \n\tvalues:%v\n\tsets:%v", vals, sets) + for _, kv := range sets { + if err := strvals.ParseInto(kv, vals); err != nil { + return nil, fmt.Errorf("failed to parse --set data: %s, err: %v", kv, err) + } + } + return vals, nil +} + +// Helper a helm client wrapping tool. +type Helper struct { + cfg *action.Configuration +} + +// NewHelper creates a new instance of Helper, and initialize the configuration for reuse. +func NewHelper(kubeconfig, namespace string) (*Helper, error) { + cf := genericclioptions.NewConfigFlags(true) + cf.KubeConfig = &kubeconfig + cf.Namespace = &namespace + cfg := &action.Configuration{} + // Make log message print only when you want to debug + logFunc := func(format string, v ...interface{}) { + klog.V(4).Infof(format, v...) + } + if err := cfg.Init(cf, namespace, "", logFunc); err != nil { + return nil, fmt.Errorf("failed to init helm action configuration, err: %v", err) + } + return &Helper{cfg: cfg}, nil +} + +func (h *Helper) GetConfig() *action.Configuration { + return h.cfg +} + +// GetRelease gets a helm release by release name, returns a nil value if not found. +func (h *Helper) GetRelease(releaseName string) (*release.Release, error) { + rel, err := h.cfg.Releases.Last(releaseName) + if err != nil { + if errors.Is(err, driver.ErrReleaseNotFound) { + klog.V(3).Infof("not found release %s", releaseName) + return nil, nil + } + return nil, fmt.Errorf("failed to get release %s, err: %v", releaseName, err) + } + return rel, nil +} + +// GetValues returns a helm release installed values. +func (h *Helper) GetValues(releaseName string) (map[string]interface{}, error) { + gv := action.NewGetValues(h.cfg) + vals, err := gv.Run(releaseName) + if err != nil { + return nil, fmt.Errorf("failed to get release %s values, err: %v", releaseName, err) + } + return vals, nil +} diff --git a/keadm/cmd/keadm/app/cmd/helm/helm_helper_test.go b/keadm/cmd/keadm/app/cmd/helm/helm_helper_test.go new file mode 100644 index 000000000..b2380b0be --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/helm_helper_test.go @@ -0,0 +1,45 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import "testing" + +func TestMergeProfileValues(t *testing.T) { + cases := []struct { + name string + file string + }{ + { + name: "load cloudcore values.yaml", + file: "charts/cloudcore/values.yaml", + }, + { + name: "load profile values", + file: "profiles/version.yaml", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + vals, err := MergeProfileValues(c.file, []string{}) + if err != nil { + t.Fatalf("faield to load helm values, err: %v", err) + } + if len(vals) == 0 { + t.Fatal("the value returned is empty") + } + }) + } +} diff --git a/keadm/cmd/keadm/app/cmd/helm/installer.go b/keadm/cmd/keadm/app/cmd/helm/installer.go index 5ffb7d566..ee29fdaa2 100644 --- a/keadm/cmd/keadm/app/cmd/helm/installer.go +++ b/keadm/cmd/keadm/app/cmd/helm/installer.go @@ -24,22 +24,14 @@ import ( ) const ( - CloudCoreHelmComponent = "cloudcore" - ChartsSubDir = "charts" - DefaultBaseHelmDir = "" DefaultAddonsHelmDir = "addons" - DefaultProfilesDir = "profiles" + // DefaultProfileFilename is the name of the default profile yaml file. DefaultProfileFilename = "version.yaml" - DefaultHelmValuesPath = "values.yaml" DefaultHelmTimeout = time.Duration(60 * time.Second) - DefaultHelmInstall = true - DefaultHelmWait = true - DefaultHelmCreateNs = true - VersionProfileKey = "version" IptablesMgrProfileKey = "iptablesmgr" ControllerManagerProfileKey = "controllermanager" @@ -223,8 +215,8 @@ func (cu *KubeCloudHelmInstTool) buildRenderer(baseHelmRoot string) (*Renderer, if cu.existsProfile && cu.isInnerProfile() { switch cu.ProfileKey { case VersionProfileKey, IptablesMgrProfileKey, ControllerManagerProfileKey: - componentName = CloudCoreHelmComponent - subDir = fmt.Sprintf("%s/%s", ChartsSubDir, CloudCoreHelmComponent) + componentName = cloudCoreHelmComponent + subDir = fmt.Sprintf("%s/%s", dirCharts, cloudCoreHelmComponent) // we can implement edgemesh here later. default: // By default, will search charts in addons dir. @@ -298,10 +290,10 @@ func (cu *KubeCloudHelmInstTool) runHelmInstall(r *Renderer) (*release.Release, helmInstall.Namespace = cu.Namespace // --force would not wait. if !cu.Force { - helmInstall.Wait = DefaultHelmWait + helmInstall.Wait = defaultHelmWait helmInstall.Timeout = DefaultHelmTimeout } - helmInstall.CreateNamespace = DefaultHelmCreateNs + helmInstall.CreateNamespace = defaultHelmCreateNs helmInstall.ReleaseName = r.componentName rel, err := helmInstall.Run(r.chart, r.profileValsMap) @@ -317,7 +309,7 @@ func (cu *KubeCloudHelmInstTool) runHelmInstall(r *Renderer) (*release.Release, helmUpgrade.Namespace = cu.Namespace // --force would not wait. if !cu.Force { - helmUpgrade.Wait = DefaultHelmWait + helmUpgrade.Wait = defaultHelmWait helmUpgrade.Timeout = DefaultHelmTimeout } @@ -340,7 +332,7 @@ func (cu *KubeCloudHelmInstTool) TearDown() error { func (cu *KubeCloudHelmInstTool) checkProfile(baseHelmRoot string) error { // read external profiles - validProfiles, err := cu.readProfiles(baseHelmRoot, DefaultProfilesDir) + validProfiles, err := cu.readProfiles(baseHelmRoot, dirProfiles) if err != nil { return ErrListProfiles } @@ -499,9 +491,9 @@ func builtinProfileToFilename(name string) string { func loadValues(chartsDir string, profileKey string, existsProfile bool) (string, error) { var path string if existsProfile { - path = strings.Join([]string{DefaultProfilesDir, builtinProfileToFilename(profileKey)}, "/") + path = strings.Join([]string{dirProfiles, builtinProfileToFilename(profileKey)}, "/") } else { - path = strings.Join([]string{profileKey, DefaultHelmValuesPath}, "/") + path = strings.Join([]string{profileKey, valuesFileName}, "/") } by, err := fs.ReadFile(kecharts.BuiltinOrDir(chartsDir), path) if err != nil { diff --git a/keadm/cmd/keadm/app/cmd/helm/values.go b/keadm/cmd/keadm/app/cmd/helm/values.go new file mode 100644 index 000000000..3cf803eec --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/values.go @@ -0,0 +1,131 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import ( + "io" + "os" + "strings" + + "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/strvals" + "sigs.k8s.io/yaml" +) + +// Inspired by https://github.com/helm/helm/blob/v3.12.3/pkg/cli/values/options.go + +// Options captures the different ways to specify values +type Options struct { + ValueFiles []string // -f/--values + StringValues []string // --set-string + Values []string // --set + FileValues []string // --set-file + JSONValues []string // --set-json + LiteralValues []string // --set-literal +} + +// MergeValues merges values from files specified via -f/--values and directly +// via --set-json, --set, --set-string, or --set-file, marshaling them to YAML +func (opts *Options) MergeValues() (map[string]interface{}, error) { + base := map[string]interface{}{} + + // User specified a values files via -f/--values + for _, filePath := range opts.ValueFiles { + currentMap := map[string]interface{}{} + + bytes, err := readFile(filePath) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", filePath) + } + // Merge with the previous map + base = mergeMaps(base, currentMap) + } + + // User specified a value via --set-json + for _, value := range opts.JSONValues { + if err := strvals.ParseJSON(value, base); err != nil { + return nil, errors.Errorf("failed parsing --set-json data %s", value) + } + } + + // User specified a value via --set + for _, value := range opts.Values { + if err := strvals.ParseInto(value, base); err != nil { + return nil, errors.Wrap(err, "failed parsing --set data") + } + } + + // User specified a value via --set-string + for _, value := range opts.StringValues { + if err := strvals.ParseIntoString(value, base); err != nil { + return nil, errors.Wrap(err, "failed parsing --set-string data") + } + } + + // User specified a value via --set-file + for _, value := range opts.FileValues { + reader := func(rs []rune) (interface{}, error) { + bytes, err := readFile(string(rs)) + if err != nil { + return nil, err + } + return string(bytes), err + } + if err := strvals.ParseIntoFile(value, base, reader); err != nil { + return nil, errors.Wrap(err, "failed parsing --set-file data") + } + } + + // User specified a value via --set-literal + for _, value := range opts.LiteralValues { + if err := strvals.ParseLiteralInto(value, base); err != nil { + return nil, errors.Wrap(err, "failed parsing --set-literal data") + } + } + + return base, nil +} + +func mergeMaps(a, b map[string]interface{}) map[string]interface{} { + out := make(map[string]interface{}, len(a)) + for k, v := range a { + out[k] = v + } + for k, v := range b { + if v, ok := v.(map[string]interface{}); ok { + if bv, ok := out[k]; ok { + if bv, ok := bv.(map[string]interface{}); ok { + out[k] = mergeMaps(bv, v) + continue + } + } + } + out[k] = v + } + return out +} + +// readFile load a file from stdin, the local directory, or a remote file with a url. +func readFile(filePath string) ([]byte, error) { + if strings.TrimSpace(filePath) == "-" { + return io.ReadAll(os.Stdin) + } + return os.ReadFile(filePath) +} diff --git a/keadm/cmd/keadm/app/cmd/helm/values_test.go b/keadm/cmd/keadm/app/cmd/helm/values_test.go new file mode 100644 index 000000000..26d1e0c0a --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/helm/values_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2024 The KubeEdge 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 helm + +import ( + "reflect" + "testing" +) + +func TestMergeValues(t *testing.T) { + nestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "stuff", + }, + } + anotherNestedMap := map[string]interface{}{ + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + flatMap := map[string]interface{}{ + "foo": "bar", + "baz": "stuff", + } + anotherFlatMap := map[string]interface{}{ + "testing": "fun", + } + + testMap := mergeMaps(flatMap, nestedMap) + equal := reflect.DeepEqual(testMap, nestedMap) + if !equal { + t.Fatalf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) + } + + testMap = mergeMaps(nestedMap, flatMap) + equal = reflect.DeepEqual(testMap, flatMap) + if !equal { + t.Fatalf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) + } + + testMap = mergeMaps(nestedMap, anotherNestedMap) + equal = reflect.DeepEqual(testMap, anotherNestedMap) + if !equal { + t.Fatalf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) + } + + testMap = mergeMaps(anotherFlatMap, anotherNestedMap) + expectedMap := map[string]interface{}{ + "testing": "fun", + "foo": "bar", + "baz": map[string]string{ + "cool": "things", + "awesome": "stuff", + }, + } + equal = reflect.DeepEqual(testMap, expectedMap) + if !equal { + t.Fatalf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) + } +} + +func TestReadFile(t *testing.T) { + filePath := "%a.txt" + _, err := readFile(filePath) + if err == nil { + t.Fatalf("Expected error when has special strings") + } +} diff --git a/keadm/cmd/keadm/app/cmd/reset_others.go b/keadm/cmd/keadm/app/cmd/reset_others.go index 3fd2b96e7..7dc4f5a6f 100644 --- a/keadm/cmd/keadm/app/cmd/reset_others.go +++ b/keadm/cmd/keadm/app/cmd/reset_others.go @@ -214,10 +214,10 @@ func cleanDirectories(isEdgeNode bool) error { } func addResetFlags(cmd *cobra.Command, resetOpts *common.ResetOptions) { - cmd.Flags().StringVar(&resetOpts.Kubeconfig, common.KubeConfig, resetOpts.Kubeconfig, + cmd.Flags().StringVar(&resetOpts.Kubeconfig, common.FlagNameCloudCoreIPPort, resetOpts.Kubeconfig, "Use this key to set kube-config path, eg: $HOME/.kube/config") cmd.Flags().BoolVar(&resetOpts.Force, "force", resetOpts.Force, "Reset the node without prompting for confirmation") - cmd.Flags().StringVar(&resetOpts.Endpoint, common.RemoteRuntimeEndpoint, resetOpts.Endpoint, + cmd.Flags().StringVar(&resetOpts.Endpoint, common.FlagNameRemoteRuntimeEndpoint, resetOpts.Endpoint, "Use this key to set container runtime endpoint") } diff --git a/keadm/cmd/keadm/app/cmd/upgrade.go b/keadm/cmd/keadm/app/cmd/upgrade.go new file mode 100644 index 000000000..84456813d --- /dev/null +++ b/keadm/cmd/keadm/app/cmd/upgrade.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "github.com/spf13/cobra" + + "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/cloud" + "github.com/kubeedge/kubeedge/keadm/cmd/keadm/app/cmd/edge" +) + +const ( + upgradeLongDescription = `Upgrade components of the cloud or the edge. +Specify whether to upgrade the cloud or the edge through three-level commands.` +) + +func NewUpgradeCommand() *cobra.Command { + cmds := &cobra.Command{ + Use: "upgrade", + Short: "Upgrade components of the cloud or the edge", + Long: upgradeLongDescription, + } + + cmds.AddCommand(cloud.NewCloudUpgrade()) + cmds.AddCommand(edge.NewEdgeUpgrade()) + + return cmds +} diff --git a/manifests/charts/cloudcore/Chart.yaml b/manifests/charts/cloudcore/Chart.yaml index a09cb9f7e..17d59ec14 100644 --- a/manifests/charts/cloudcore/Chart.yaml +++ b/manifests/charts/cloudcore/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: cloudcore -version: 0.1.0 -appVersion: 1.12.0 +version: 1.15.1 +appVersion: 1.15.1 description: The KubeEdge cloudcore component. sources: - https://github.com/kubeedge/kubeedge diff --git a/manifests/charts/cloudcore/templates/deployment_cloudcore.yaml b/manifests/charts/cloudcore/templates/deployment_cloudcore.yaml index 09aa2adfc..57a0ff0f4 100644 --- a/manifests/charts/cloudcore/templates/deployment_cloudcore.yaml +++ b/manifests/charts/cloudcore/templates/deployment_cloudcore.yaml @@ -14,6 +14,9 @@ spec: {{- with .Values.cloudCore.labels }} matchLabels: {{- toYaml . | nindent 6 }} {{- end }} + {{- with .Values.cloudCore.strategy }} + strategy: {{ toYaml . | nindent 4 }} + {{- end }} template: metadata: {{- with .Values.cloudCore.labels }} diff --git a/manifests/charts/cloudcore/values.yaml b/manifests/charts/cloudcore/values.yaml index db61c8e14..883898aef 100644 --- a/manifests/charts/cloudcore/values.yaml +++ b/manifests/charts/cloudcore/values.yaml @@ -1,14 +1,14 @@ # Default values for kubeedge. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -appVersion: "1.13.0" +appVersion: "1.15.1" cloudCore: replicaCount: 1 hostNetWork: true image: repository: "kubeedge/cloudcore" - tag: "v1.13.0" + tag: "v1.15.1" pullPolicy: "IfNotPresent" pullSecrets: [] securityContext: @@ -33,6 +33,7 @@ cloudCore: requests: cpu: 100m memory: 512Mi + strategy: {} modules: cloudHub: # Caution!: Leave this entry to empty will cause CloudCore to exit abnormally once KubeEdge is enabled. @@ -108,7 +109,7 @@ controllerManager: enable: false image: repository: "kubeedge/controller-manager" - tag: "v1.12.0" + tag: "v1.15.1" pullPolicy: "IfNotPresent" pullSecrets: [] labels: diff --git a/manifests/profiles/version.yaml b/manifests/profiles/version.yaml index d5a1bf5c2..72ba5466f 100644 --- a/manifests/profiles/version.yaml +++ b/manifests/profiles/version.yaml @@ -1,14 +1,14 @@ # Default values for kubeedge. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -appVersion: "1.13.0" +appVersion: "1.15.1" cloudCore: replicaCount: 1 hostNetWork: true image: repository: "kubeedge/cloudcore" - tag: "v1.13.0" + tag: "v1.15.1" pullPolicy: "IfNotPresent" pullSecrets: [] securityContext: @@ -33,6 +33,7 @@ cloudCore: requests: cpu: 100m memory: 512Mi + strategy: {} modules: cloudHub: advertiseAddress: # Caution!: Leave this entry to empty will cause CloudCore to exit abnormally once KubeEdge is enabled. @@ -68,7 +69,7 @@ iptablesManager: hostNetWork: true image: repository: "kubeedge/iptables-manager" - tag: "v1.12.0" + tag: "v1.15.1" pullPolicy: "IfNotPresent" pullSecrets: [] securityContext: @@ -101,7 +102,7 @@ controllerManager: enable: false image: repository: "kubeedge/controller-manager" - tag: "v1.12.0" + tag: "v1.15.1" pullPolicy: "IfNotPresent" pullSecrets: [] labels: diff --git a/pkg/image/image_test.go b/pkg/image/image_test.go index c495d4b8c..f208dc6ac 100644 --- a/pkg/image/image_test.go +++ b/pkg/image/image_test.go @@ -19,9 +19,7 @@ func TestEdgeSet(t *testing.T) { args: common.JoinOptions{ ImageRepository: "", WithMQTT: true, - InitBaseOptions: common.InitBaseOptions{ - KubeEdgeVersion: "v1.9.1", - }, + KubeEdgeVersion: "v1.9.1", }, want: Set{ EdgeCore: "kubeedge/installation-package:v1.9.1", @@ -33,9 +31,7 @@ func TestEdgeSet(t *testing.T) { args: common.JoinOptions{ ImageRepository: "", WithMQTT: true, - InitBaseOptions: common.InitBaseOptions{ - KubeEdgeVersion: "", - }, + KubeEdgeVersion: "", }, want: Set{ EdgeCore: "kubeedge/installation-package", @@ -47,9 +43,7 @@ func TestEdgeSet(t *testing.T) { args: common.JoinOptions{ ImageRepository: "kubeedge-test", WithMQTT: true, - InitBaseOptions: common.InitBaseOptions{ - KubeEdgeVersion: "v1.9.1", - }, + KubeEdgeVersion: "v1.9.1", }, want: Set{ EdgeCore: "kubeedge-test/installation-package:v1.9.1", @@ -61,9 +55,7 @@ func TestEdgeSet(t *testing.T) { args: common.JoinOptions{ ImageRepository: "kubeedge-test", WithMQTT: false, - InitBaseOptions: common.InitBaseOptions{ - KubeEdgeVersion: "v1.9.1", - }, + KubeEdgeVersion: "v1.9.1", }, want: Set{ EdgeCore: "kubeedge-test/installation-package:v1.9.1", @@ -74,9 +66,7 @@ func TestEdgeSet(t *testing.T) { args: common.JoinOptions{ ImageRepository: "kubeedge-test", WithMQTT: true, - InitBaseOptions: common.InitBaseOptions{ - KubeEdgeVersion: "", - }, + KubeEdgeVersion: "", }, want: Set{ EdgeCore: "kubeedge-test/installation-package", |
