From ca5e892c34d5099e281ce71c4ba4617da28c2a62 Mon Sep 17 00:00:00 2001 From: mengyipeng <413183498@qq.com> Date: Fri, 18 Oct 2024 19:12:23 +0800 Subject: [PATCH] adapt playground init --- pkg/cmd/cluster/cluster_test.go | 146 ++++++++++++++++-- pkg/cmd/cluster/create_subcmds.go | 20 ++- pkg/cmd/cluster/create_subcmds_test.go | 8 +- pkg/cmd/playground/destroy.go | 76 +--------- pkg/cmd/playground/init.go | 201 ++++--------------------- pkg/cmd/playground/init_test.go | 23 +-- pkg/cmd/playground/types.go | 2 +- pkg/testing/fake.go | 1 + 8 files changed, 186 insertions(+), 291 deletions(-) diff --git a/pkg/cmd/cluster/cluster_test.go b/pkg/cmd/cluster/cluster_test.go index 507493839..97107c6d1 100644 --- a/pkg/cmd/cluster/cluster_test.go +++ b/pkg/cmd/cluster/cluster_test.go @@ -20,9 +20,15 @@ along with this program. If not, see . package cluster import ( + "github.com/apecloud/kbcli/pkg/cluster" + "github.com/apecloud/kbcli/pkg/printer" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/version" + fakediscovery "k8s.io/client-go/discovery/fake" "k8s.io/client-go/kubernetes/scheme" "fmt" @@ -40,33 +46,143 @@ import ( ) var _ = Describe("Cluster", func() { - const ( + clusterType = "apecloud-mysql" clusterName = "test" namespace = "default" ) - var streams genericiooptions.IOStreams - var tf *cmdtesting.TestFactory - fakeConfigData := map[string]string{ - "config.yaml": `# the default storage class name. - DEFAULT_STORAGE_CLASS: ""`, - } + var ( + tf *cmdtesting.TestFactory + streams genericiooptions.IOStreams + createOptions *action.CreateOptions + mockClient = func(data runtime.Object) *cmdtesting.TestFactory { + tf = testing.NewTestFactory(testing.Namespace) + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &clientfake.RESTClient{ + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + GroupVersion: schema.GroupVersion{Group: types.AppsAPIGroup, Version: types.AppsAPIVersion}, + Resp: &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, data)}, + } + tf.Client = tf.UnstructuredClient + tf.FakeDynamicClient = testing.FakeDynamicClient(data) + tf.WithDiscoveryClient(cmdtesting.NewFakeCachedDiscoveryClient()) + return tf + } + ) + BeforeEach(func() { + _ = kbappsv1.AddToScheme(scheme.Scheme) + _ = metav1.AddMetaToScheme(scheme.Scheme) streams, _, _, _ = genericiooptions.NewTestIOStreams() - tf = cmdtesting.NewTestFactory().WithNamespace(namespace) - cd := testing.FakeClusterDef() - fakeDefaultStorageClass := testing.FakeStorageClass(testing.StorageClassName, testing.IsDefault) - // TODO: remove unused codes? - tf.FakeDynamicClient = testing.FakeDynamicClient(cd, fakeDefaultStorageClass, testing.FakeConfigMap("kubeblocks-manager-config", types.DefaultNamespace, fakeConfigData), testing.FakeSecret(types.DefaultNamespace, clusterName)) - tf.Client = &clientfake.RESTClient{} + tf = mockClient(testing.FakeCompDef()) + createOptions = &action.CreateOptions{ + IOStreams: streams, + Factory: tf, + } }) AfterEach(func() { tf.Cleanup() }) - // TODO: add create cluster case - Context("delete cluster", func() { + Context("create", func() { + It("without name", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + Expect(o).ShouldNot(BeNil()) + Expect(o.ChartInfo).ShouldNot(BeNil()) + o.Format = printer.YAML + + Expect(o.CreateOptions.Complete()).To(Succeed()) + o.Client = testing.FakeClientSet() + fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) + fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} + Expect(o.Complete(nil)).To(Succeed()) + Expect(o.Validate()).To(Succeed()) + Expect(o.Name).ShouldNot(BeEmpty()) + Expect(o.Run()).Should(Succeed()) + }) + + It("without name", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + Expect(o).ShouldNot(BeNil()) + Expect(o.ChartInfo).ShouldNot(BeNil()) + o.Format = printer.YAML + + Expect(o.CreateOptions.Complete()).To(Succeed()) + o.Client = testing.FakeClientSet() + fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) + fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} + Expect(o.Complete(nil)).To(Succeed()) + Expect(o.Validate()).To(Succeed()) + Expect(o.Name).ShouldNot(BeEmpty()) + Expect(o.Run()).Should(Succeed()) + }) + }) + + Context("create validate", func() { + var o *CreateSubCmdsOptions + BeforeEach(func() { + o = &CreateSubCmdsOptions{ + CreateOptions: &action.CreateOptions{ + Factory: tf, + Namespace: namespace, + Dynamic: tf.FakeDynamicClient, + IOStreams: streams, + }, + } + o.Name = "mycluster" + o.ChartInfo, _ = cluster.BuildChartInfo(clusterType) + }) + + It("can validate the cluster name must begin with a letter and can only contain lowercase letters, numbers, and '-'.", func() { + type fn func() + var succeed = func(name string) fn { + return func() { + o.Name = name + Expect(o.Validate()).Should(Succeed()) + } + } + var failed = func(name string) fn { + return func() { + o.Name = name + Expect(o.Validate()).Should(HaveOccurred()) + } + } + // more case to add + invalidCase := []string{ + "1abcd", "abcd-", "-abcd", "abc#d", "ABCD", "*&(&%", + } + + validCase := []string{ + "abcd", "abcd1", "a1-2b-3d", + } + + for i := range invalidCase { + failed(invalidCase[i]) + } + + for i := range validCase { + succeed(validCase[i]) + } + + }) + + It("can validate whether the name is not longer than 16 characters when create a new cluster", func() { + Expect(len(o.Name)).Should(BeNumerically("<=", 16)) + Expect(o.Validate()).Should(Succeed()) + moreThan16 := 17 + bytes := make([]byte, 0) + var clusterNameMoreThan16 string + for i := 0; i < moreThan16; i++ { + bytes = append(bytes, byte(i%26+'a')) + } + clusterNameMoreThan16 = string(bytes) + Expect(len(clusterNameMoreThan16)).Should(BeNumerically(">", 16)) + o.Name = clusterNameMoreThan16 + Expect(o.Validate()).Should(HaveOccurred()) + }) }) diff --git a/pkg/cmd/cluster/create_subcmds.go b/pkg/cmd/cluster/create_subcmds.go index 1fb5c86d4..fa3e57701 100644 --- a/pkg/cmd/cluster/create_subcmds.go +++ b/pkg/cmd/cluster/create_subcmds.go @@ -23,6 +23,7 @@ import ( "context" "fmt" "os" + "regexp" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/errors" @@ -97,8 +98,8 @@ func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { Run: func(cmd *cobra.Command, args []string) { o.Args = args cmdutil.CheckErr(o.CreateOptions.Complete()) - cmdutil.CheckErr(o.complete(cmd)) - cmdutil.CheckErr(o.validate()) + cmdutil.CheckErr(o.Complete(cmd)) + cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Run()) }, } @@ -150,7 +151,7 @@ func generateClusterName(dynamic dynamic.Interface, namespace string) (string, e return "", fmt.Errorf("failed to generate cluster name") } -func (o *CreateSubCmdsOptions) complete(cmd *cobra.Command) error { +func (o *CreateSubCmdsOptions) Complete(cmd *cobra.Command) error { var err error // if name is not specified, generate a random cluster name @@ -162,7 +163,9 @@ func (o *CreateSubCmdsOptions) complete(cmd *cobra.Command) error { } // get values from flags - o.Values = getValuesFromFlags(cmd.LocalNonPersistentFlags()) + if cmd != nil { + o.Values = getValuesFromFlags(cmd.LocalNonPersistentFlags()) + } // get all the rendered objects objs, err := o.getObjectsInfo() @@ -211,7 +214,14 @@ func (o *CreateSubCmdsOptions) complete(cmd *cobra.Command) error { return nil } -func (o *CreateSubCmdsOptions) validate() error { +func (o *CreateSubCmdsOptions) Validate() error { + matched, _ := regexp.MatchString(`^[a-z]([-a-z0-9]*[a-z0-9])?$`, o.Name) + if !matched { + return fmt.Errorf("cluster name must begin with a letter and can only contain lowercase letters, numbers, and '-'") + } + if len(o.Name) > 16 { + return fmt.Errorf("cluster name should be less than 16 characters") + } return cluster.ValidateValues(o.ChartInfo, o.Values) } diff --git a/pkg/cmd/cluster/create_subcmds_test.go b/pkg/cmd/cluster/create_subcmds_test.go index 63e429cbc..5eef97a87 100644 --- a/pkg/cmd/cluster/create_subcmds_test.go +++ b/pkg/cmd/cluster/create_subcmds_test.go @@ -111,14 +111,14 @@ var _ = Describe("create cluster by cluster type", func() { o.Client = testing.FakeClientSet() fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} - Expect(o.complete(mysqlCmd)).Should(Succeed()) + Expect(o.Complete(mysqlCmd)).Should(Succeed()) Expect(o.Name).ShouldNot(BeEmpty()) Expect(o.Values).ShouldNot(BeNil()) Expect(o.ChartInfo.ClusterDef).Should(Equal(apeCloudMysql)) By("validate") o.Dynamic = testing.FakeDynamicClient() - Expect(o.validate()).Should(Succeed()) + Expect(o.Validate()).Should(Succeed()) By("run") o.DryRun = "client" @@ -157,14 +157,14 @@ var _ = Describe("create cluster by cluster type", func() { fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} Expect(shardCmd.Flags().Set("mode", "cluster")).Should(Succeed()) - Expect(o.complete(shardCmd)).Should(Succeed()) + Expect(o.Complete(shardCmd)).Should(Succeed()) Expect(o.Name).ShouldNot(BeEmpty()) Expect(o.Values).ShouldNot(BeNil()) Expect(o.ChartInfo.ComponentDef[0]).Should(Equal(redisComponent)) By("validate") o.Dynamic = testing.FakeDynamicClient() - Expect(o.validate()).Should(Succeed()) + Expect(o.Validate()).Should(Succeed()) By("run") o.DryRun = "client" diff --git a/pkg/cmd/playground/destroy.go b/pkg/cmd/playground/destroy.go index de5dda092..b10631ed5 100644 --- a/pkg/cmd/playground/destroy.go +++ b/pkg/cmd/playground/destroy.go @@ -36,7 +36,6 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" - cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" @@ -48,7 +47,6 @@ import ( "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" "github.com/apecloud/kbcli/pkg/util/helm" - "github.com/apecloud/kbcli/pkg/util/prompt" ) var ( @@ -95,10 +93,7 @@ func (o *destroyOptions) destroy() error { return fmt.Errorf("no playground cluster found") } - if o.prevCluster.CloudProvider == cp.Local { - return o.destroyLocal() - } - return o.destroyCloud() + return o.destroyLocal() } // destroyLocal destroy local k3d cluster that will destroy all resources @@ -120,75 +115,6 @@ func (o *destroyOptions) destroyLocal() error { return o.removeStateFile() } -// destroyCloud destroys cloud kubernetes cluster, before destroying, we should delete -// all clusters created by KubeBlocks, uninstall KubeBlocks and remove the KubeBlocks -// namespace that will destroy all resources created by KubeBlocks, avoid to leave resources behind -func (o *destroyOptions) destroyCloud() error { - var err error - - printer.Warning(o.Out, `This action will destroy the kubernetes cluster, there may be residual resources, - please confirm and manually clean up related resources after this action. - -`) - - fmt.Fprintf(o.Out, "Do you really want to destroy the kubernetes cluster %s?\n%s\n\n The operation cannot be rollbacked. Only 'yes' will be accepted to confirm.\n\n", - o.prevCluster.ClusterName, o.prevCluster.String()) - - // confirm to destroy - if !o.autoApprove { - entered, _ := prompt.NewPrompt("Enter a value:", nil, o.In).Run() - if entered != yesStr { - fmt.Fprintf(o.Out, "\nPlayground destroy cancelled.\n") - return cmdutil.ErrExit - } - } - - o.startTime = time.Now() - - // for cloud provider, we should delete all clusters created by KubeBlocks first, - // uninstall KubeBlocks and remove the KubeBlocks namespace, then destroy the - // playground cluster, avoid to leave resources behind. - // delete all clusters created by KubeBlocks, MUST BE VERY CAUTIOUS, use the right - // kubeconfig and context, otherwise, it will delete the wrong cluster. - if err = o.deleteClustersAndUninstallKB(); err != nil { - if strings.Contains(err.Error(), kubeClusterUnreachableErr.Error()) { - printer.Warning(o.Out, err.Error()) - } else { - return err - } - } - - // destroy playground kubernetes cluster - cpPath, err := cloudProviderRepoDir(o.prevCluster.KbcliVersion) - if err != nil { - return err - } - - provider, err := cp.New(o.prevCluster.CloudProvider, cpPath, o.Out, o.ErrOut) - if err != nil { - return err - } - - fmt.Fprintf(o.Out, "Destroy %s %s cluster %s...\n", - o.prevCluster.CloudProvider, cp.K8sService(o.prevCluster.CloudProvider), o.prevCluster.ClusterName) - if err = provider.DeleteK8sCluster(o.prevCluster); err != nil { - return err - } - - // remove the cluster kubeconfig from the use default kubeconfig - if err = o.removeKubeConfig(); err != nil { - return err - } - - // at last, remove the state file - if err = o.removeStateFile(); err != nil { - return err - } - - fmt.Fprintf(o.Out, "Playground destroy completed in %s.\n", time.Since(o.startTime).Truncate(time.Second)) - return nil -} - func (o *destroyOptions) deleteClustersAndUninstallKB() error { var err error diff --git a/pkg/cmd/playground/init.go b/pkg/cmd/playground/init.go index 1d103eeb7..e53f4d7af 100644 --- a/pkg/cmd/playground/init.go +++ b/pkg/cmd/playground/init.go @@ -29,15 +29,15 @@ import ( gv "github.com/hashicorp/go-version" "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/exp/slices" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/rand" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/klog/v2" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" cp "github.com/apecloud/kbcli/pkg/cloudprovider" + "github.com/apecloud/kbcli/pkg/cluster" + cmdcluster "github.com/apecloud/kbcli/pkg/cmd/cluster" "github.com/apecloud/kbcli/pkg/cmd/kubeblocks" "github.com/apecloud/kbcli/pkg/printer" "github.com/apecloud/kbcli/pkg/spinner" @@ -59,18 +59,6 @@ on the created kubernetes cluster, and an apecloud-mysql cluster named mycluster # create a k3d cluster on local host and install KubeBlocks kbcli playground init - # create an AWS EKS cluster and install KubeBlocks, the region is required - kbcli playground init --cloud-provider aws --region us-west-1 - - # create an Alibaba cloud ACK cluster and install KubeBlocks, the region is required - kbcli playground init --cloud-provider alicloud --region cn-hangzhou - - # create a Tencent cloud TKE cluster and install KubeBlocks, the region is required - kbcli playground init --cloud-provider tencentcloud --region ap-chengdu - - # create a Google cloud GKE cluster and install KubeBlocks, the region is required - kbcli playground init --cloud-provider gcp --region us-east1 - # after init, run the following commands to experience KubeBlocks quickly # list database cluster and check its status kbcli cluster list @@ -87,8 +75,6 @@ on the created kubernetes cluster, and an apecloud-mysql cluster named mycluster # destroy playground kbcli playground destroy`) - supportedCloudProviders = []string{cp.Local, cp.AWS, cp.GCP, cp.AliCloud, cp.TencentCloud} - spinnerMsg = func(format string, a ...any) spinner.Option { return spinner.WithMessage(fmt.Sprintf("%-50s", fmt.Sprintf(format, a...))) } @@ -97,9 +83,8 @@ on the created kubernetes cluster, and an apecloud-mysql cluster named mycluster type initOptions struct { genericiooptions.IOStreams helmCfg *helm.Config - clusterDef string + clusterType string kbVersion string - cloudProvider string region string autoApprove bool dockerVersion *gv.Version @@ -124,28 +109,18 @@ func newInitCmd(streams genericiooptions.IOStreams) *cobra.Command { }, } - cmd.Flags().StringVar(&o.clusterDef, "cluster-definition", defaultClusterDef, "Specify the cluster definition, run \"kbcli cd list\" to get the available cluster definitions") + cmd.Flags().StringVar(&o.clusterType, "cluster-type", defaultClusterType, "Specify the cluster type to create, use 'kbcli cluster create --help' to get the available cluster type.") cmd.Flags().StringVar(&o.kbVersion, "version", version.DefaultKubeBlocksVersion, "KubeBlocks version") - cmd.Flags().StringVar(&o.cloudProvider, "cloud-provider", defaultCloudProvider, fmt.Sprintf("Cloud provider type, one of %v", supportedCloudProviders)) cmd.Flags().StringVar(&o.region, "region", "", "The region to create kubernetes cluster") cmd.Flags().DurationVar(&o.Timeout, "timeout", 600*time.Second, "Time to wait for init playground, such as --timeout=10m") cmd.Flags().BoolVar(&o.autoApprove, "auto-approve", false, "Skip interactive approval during the initialization of playground") - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "cloud-provider", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return cp.CloudProviders(), cobra.ShellCompDirectiveNoFileComp - })) return cmd } func (o *initOptions) complete(cmd *cobra.Command) error { var err error - if o.cloudProvider != cp.Local { - return nil - } - if o.dockerVersion, err = util.GetDockerVersion(); err != nil { return err } @@ -158,19 +133,11 @@ func (o *initOptions) complete(cmd *cobra.Command) error { } func (o *initOptions) validate() error { - if !slices.Contains(supportedCloudProviders, o.cloudProvider) { - return fmt.Errorf("cloud provider %s is not supported, only support %v", o.cloudProvider, supportedCloudProviders) - } - - if o.cloudProvider != cp.Local && o.region == "" { - return fmt.Errorf("region should be specified when cloud provider %s is specified", o.cloudProvider) + if o.clusterType == "" { + return fmt.Errorf("a valid cluster type is needed, use --cluster-type to specify one") } - if o.clusterDef == "" { - return fmt.Errorf("a valid cluster definition is needed, use --cluster-definition to specify one") - } - - if o.cloudProvider == cp.Local && o.dockerVersion.LessThan(version.MinimumDockerVersion) { + if o.dockerVersion.LessThan(version.MinimumDockerVersion) { return fmt.Errorf("your docker version %s is lower than the minimum version %s, please upgrade your docker", o.dockerVersion, version.MinimumDockerVersion) } @@ -181,15 +148,12 @@ func (o *initOptions) validate() error { } func (o *initOptions) run() error { - if o.cloudProvider == cp.Local { - return o.local() - } - return o.cloud() + return o.local() } // local bootstraps a playground in the local host func (o *initOptions) local() error { - provider, err := cp.New(o.cloudProvider, "", o.Out, o.ErrOut) + provider, err := cp.New(cp.Local, "", o.Out, o.ErrOut) if err != nil { return err } @@ -231,81 +195,6 @@ func (o *initOptions) local() error { return o.installKBAndCluster(clusterInfo) } -// bootstraps a playground in the remote cloud -func (o *initOptions) cloud() error { - cpPath, err := cloudProviderRepoDir("") - if err != nil { - return err - } - - var clusterInfo *cp.K8sClusterInfo - - // if kubernetes cluster exists, confirm to continue or not, if not, user should - // destroy the old cluster first - if o.prevCluster != nil { - clusterInfo = o.prevCluster - if err = o.confirmToContinue(); err != nil { - return err - } - } else { - clusterName := fmt.Sprintf("%s-%s", cloudClusterNamePrefix, rand.String(5)) - clusterInfo = &cp.K8sClusterInfo{ - ClusterName: clusterName, - CloudProvider: o.cloudProvider, - Region: o.region, - } - if err = o.confirmInitNewKubeCluster(); err != nil { - return err - } - - fmt.Fprintf(o.Out, "\nWrite cluster info to state file %s\n", o.stateFilePath) - if err := writeClusterInfo(o.stateFilePath, clusterInfo); err != nil { - return errors.Wrapf(err, "failed to write kubernetes cluster info to state file %s:\n %v", o.stateFilePath, clusterInfo) - } - - fmt.Fprintf(o.Out, "Creating %s %s cluster %s ... \n", o.cloudProvider, cp.K8sService(o.cloudProvider), clusterName) - } - - o.startTime = time.Now() - printer.PrintBlankLine(o.Out) - - // clone apecloud/cloud-provider repo to local path - fmt.Fprintf(o.Out, "Clone ApeCloud cloud-provider repo to %s...\n", cpPath) - branchName := "kb-playground" - if err = util.CloneGitRepo(cp.GitRepoURL, branchName, cpPath); err != nil { - return err - } - - provider, err := cp.New(o.cloudProvider, cpPath, o.Out, o.ErrOut) - if err != nil { - return err - } - - // create a kubernetes cluster in the cloud - if err = provider.CreateK8sCluster(clusterInfo); err != nil { - klog.V(1).Infof("create K8S cluster failed: %s", err.Error()) - return err - } - klog.V(1).Info("create K8S cluster success") - - printer.PrintBlankLine(o.Out) - - // write cluster info to state file and get new cluster info with kubeconfig - clusterInfo, err = o.writeStateFile(provider) - if err != nil { - return err - } - - // write cluster kubeconfig to default kubeconfig file and switch current context to it - if err = o.setKubeConfig(clusterInfo); err != nil { - return err - } - - // install KubeBlocks and create a database cluster - klog.V(1).Info("start to install KubeBlocks in K8S cluster... ") - return o.installKBAndCluster(clusterInfo) -} - // confirmToContinue confirms to continue init process if there is an existed kubernetes cluster func (o *initOptions) confirmToContinue() error { clusterName := o.prevCluster.ClusterName @@ -318,7 +207,7 @@ func (o *initOptions) confirmToContinue() error { } } fmt.Fprintf(o.Out, "Continue to initialize %s %s cluster %s... \n", - o.cloudProvider, cp.K8sService(o.cloudProvider), clusterName) + cp.Local, cp.K8sService(cp.Local), clusterName) return nil } @@ -410,7 +299,7 @@ func (o *initOptions) installKBAndCluster(info *cp.K8sClusterInfo) error { } klog.V(1).Info("KubeBlocks installed successfully") // install database cluster - clusterInfo := "ClusterDefinition: " + o.clusterDef + clusterInfo := "ClusterType: " + o.clusterType s := spinner.New(o.Out, spinnerMsg("Create cluster %s (%s)", kbClusterName, clusterInfo)) defer s.Fail() if err = o.createCluster(); err != nil && !apierrors.IsAlreadyExists(err) { @@ -463,19 +352,11 @@ func (o *initOptions) installKubeBlocks(k8sClusterName string) error { "agamotto.enabled=true", ) - if o.cloudProvider == cp.Local { - insOpts.ValueOpts.Values = append(insOpts.ValueOpts.Values, - // use hostpath csi driver to support snapshot - "snapshot-controller.enabled=true", - "csi-hostpath-driver.enabled=true", - ) - } else if o.cloudProvider == cp.AWS { - insOpts.ValueOpts.Values = append(insOpts.ValueOpts.Values, - // enable aws-load-balancer-controller addon automatically on playground - "aws-load-balancer-controller.enabled=true", - fmt.Sprintf("aws-load-balancer-controller.clusterName=%s", k8sClusterName), - ) - } + insOpts.ValueOpts.Values = append(insOpts.ValueOpts.Values, + // use hostpath csi driver to support snapshot + "snapshot-controller.enabled=true", + "csi-hostpath-driver.enabled=true", + ) if err = insOpts.PreCheck(); err != nil { // if the KubeBlocks has been installed, we ignore the error @@ -494,38 +375,24 @@ func (o *initOptions) installKubeBlocks(k8sClusterName string) error { // createCluster constructs a cluster create options and run func (o *initOptions) createCluster() error { - // TODO: Update with new creation cmd - /*c := cmdcluster.NewCreateOptions(util.NewFactory(), genericiooptions.NewTestIOStreamsDiscard()) - c.ClusterDefRef = o.clusterDef - // c.ClusterVersionRef = o.clusterVersion - c.Namespace = defaultNamespace - c.Name = kbClusterName - c.UpdatableFlags = cmdcluster.UpdatableFlags{ - TerminationPolicy: "WipeOut", - DisableExporter: false, - PodAntiAffinity: "Preferred", - Tenancy: "SharedNode", - } - - // if we are running on local, create cluster with one replica - if o.cloudProvider == cp.Local { - c.Values = append(c.Values, "replicas=1") - } else { - // if we are running on cloud, create cluster with three replicas - c.Values = append(c.Values, "replicas=3") + c, err := cmdcluster.NewSubCmdsOptions(&cmdcluster.NewCreateOptions(util.NewFactory(), genericiooptions.NewTestIOStreamsDiscard()).CreateOptions, cluster.ClusterType(o.clusterType)) + if err != nil { + return err } - - if err := c.CreateOptions.Complete(); err != nil { + c.Args = []string{kbClusterName} + err = c.CreateOptions.Complete() + if err != nil { return err } - if err := c.Validate(); err != nil { + err = c.Complete(nil) + if err != nil { return err } - if err := c.Complete(); err != nil { + err = c.Validate() + if err != nil { return err } - return c.Run()*/ - return nil + return c.Run() } // checkExistedCluster checks playground kubernetes cluster exists or not, a kbcli client only @@ -540,19 +407,7 @@ func (o *initOptions) checkExistedCluster() error { warningMsg := fmt.Sprintf("playground only supports one kubernetes cluster,\n if a cluster is already existed, please destroy it first.\n%s\n", o.prevCluster.String()) // if cloud provider is not same with the existed cluster cloud provider, suggest // user to destroy the previous cluster first - if o.prevCluster.CloudProvider != o.cloudProvider { - printer.Warning(o.Out, warningMsg) - return cmdutil.ErrExit - } - - if o.prevCluster.CloudProvider == cp.Local { - return nil - } - - // previous kubernetes cluster is a cloud provider cluster, check if the region - // is same with the new cluster region, if not, suggest user to destroy the previous - // cluster first - if o.prevCluster.Region != o.region { + if o.prevCluster.CloudProvider != cp.Local { printer.Warning(o.Out, warningMsg) return cmdutil.ErrExit } diff --git a/pkg/cmd/playground/init_test.go b/pkg/cmd/playground/init_test.go index a273c6d58..3f43a7997 100644 --- a/pkg/cmd/playground/init_test.go +++ b/pkg/cmd/playground/init_test.go @@ -28,7 +28,6 @@ import ( gv "github.com/hashicorp/go-version" "k8s.io/cli-runtime/pkg/genericiooptions" - cp "github.com/apecloud/kbcli/pkg/cloudprovider" clitesting "github.com/apecloud/kbcli/pkg/testing" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util/helm" @@ -52,38 +51,26 @@ var _ = Describe("playground", func() { Expect(cmd != nil).Should(BeTrue()) o := &initOptions{ - clusterDef: clitesting.ClusterDefName, + clusterType: clitesting.ClusterType, IOStreams: streams, - cloudProvider: defaultCloudProvider, helmCfg: helm.NewConfig("", testKubeConfigPath, "", false), dockerVersion: version.MinimumDockerVersion, } Expect(o.validate()).Should(Succeed()) Expect(o.run()).Should(HaveOccurred()) Expect(o.installKubeBlocks("test")).Should(HaveOccurred()) - // TODO: re-add it when updating cluster creation function - // Expect(o.createCluster()).Should(HaveOccurred()) + Expect(o.createCluster()).Should(HaveOccurred()) }) It("init at local host without outdate docker", func() { var err error o := &initOptions{ - clusterDef: clitesting.ClusterDefName, - IOStreams: streams, - cloudProvider: defaultCloudProvider, - helmCfg: helm.NewConfig("", testKubeConfigPath, "", false), + clusterType: clitesting.ClusterType, + IOStreams: streams, + helmCfg: helm.NewConfig("", testKubeConfigPath, "", false), } o.dockerVersion, err = gv.NewVersion("20.10.0") Expect(err).Should(BeNil()) Expect(o.validate()).Should(HaveOccurred()) }) - - It("init at remote cloud", func() { - o := &initOptions{ - IOStreams: streams, - clusterDef: clitesting.ClusterDefName, - cloudProvider: cp.AWS, - } - Expect(o.validate()).Should(HaveOccurred()) - }) }) diff --git a/pkg/cmd/playground/types.go b/pkg/cmd/playground/types.go index 969edb055..13ede14ff 100644 --- a/pkg/cmd/playground/types.go +++ b/pkg/cmd/playground/types.go @@ -32,7 +32,7 @@ const ( const ( defaultCloudProvider = cloudprovider.Local - defaultClusterDef = "apecloud-mysql" + defaultClusterType = "apecloud-mysql" // defaultNamespace is the namespace of playground cluster defaultNamespace = "default" diff --git a/pkg/testing/fake.go b/pkg/testing/fake.go index 42d45be5c..804c0e677 100644 --- a/pkg/testing/fake.go +++ b/pkg/testing/fake.go @@ -54,6 +54,7 @@ const ( ClusterName = "fake-cluster-name" Namespace = "fake-namespace" ClusterDefName = "fake-cluster-definition" + ClusterType = "fake-cluster-Type" CompDefName = "fake-component-definition" ComponentName = "fake-component-name" NodeName = "fake-node-name"