From 73c5d1d98e2ffda3b92966bb44694fb38f5bd3b9 Mon Sep 17 00:00:00 2001 From: gnolong <2391353625@qq.com> Date: Mon, 12 Aug 2024 21:12:24 +0800 Subject: [PATCH] fix: playground waits cd and cv --- go.mod | 2 +- go.sum | 7 +- pkg/cmd/cluster/create.go | 2 +- pkg/cmd/cluster/create_subcmds.go | 10 +-- pkg/cmd/cluster/create_subcmds_test.go | 6 +- pkg/cmd/playground/init.go | 111 ++++++++++++++++++++++++- 6 files changed, 122 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index ed318f846..d7eebede0 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/go-git/go-git/v5 v5.6.1 github.com/go-logr/logr v1.4.1 github.com/google/uuid v1.6.0 - github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hc-install v0.5.2 github.com/hashicorp/terraform-exec v0.18.0 github.com/jedib0t/go-pretty/v6 v6.4.6 diff --git a/go.sum b/go.sum index 6c41da52f..f3693a29b 100644 --- a/go.sum +++ b/go.sum @@ -248,8 +248,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= -github.com/apecloud/kubeblocks v0.9.0-beta.32 h1:DUztOHGW17ceN0etYMdYIj8dUK1MPCXX+gkd2CUf6XM= -github.com/apecloud/kubeblocks v0.9.0-beta.32/go.mod h1:kp9nenBgXsO03SbxN7a5S2HdNTsIQlpTcSgY8Mf2KS0= github.com/apecloud/kubeblocks v0.9.1-beta.6 h1:/7k80XnLzJxhW+CaUgiIThm6JlCto+giNqscl6fKU6s= github.com/apecloud/kubeblocks v0.9.1-beta.6/go.mod h1:kp9nenBgXsO03SbxN7a5S2HdNTsIQlpTcSgY8Mf2KS0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= @@ -753,8 +751,9 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -2101,8 +2100,6 @@ k8s.io/cli-runtime v0.28.3/go.mod h1:jeX37ZPjIcENVuXDDTskG3+FnVuZms5D9omDXS/2Jjc k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= k8s.io/code-generator v0.28.3/go.mod h1:A2EAHTRYvCvBrb/MM2zZBNipeCk3f8NtpdNIKawC43M= -k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= -k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s= k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= k8s.io/component-helpers v0.28.3 h1:te9ieTGzcztVktUs92X53P6BamAoP73MK0qQP0WmDqc= diff --git a/pkg/cmd/cluster/create.go b/pkg/cmd/cluster/create.go index 203932ca1..c3b0c4692 100755 --- a/pkg/cmd/cluster/create.go +++ b/pkg/cmd/cluster/create.go @@ -318,7 +318,7 @@ func NewCreateCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. registerFlagCompletionFunc(cmd, f) // add all subcommands for supported cluster type - cmd.AddCommand(buildCreateSubCmds(&o.CreateOptions)...) + cmd.AddCommand(BuildCreateSubCmds(&o.CreateOptions)...) o.Cmd = cmd diff --git a/pkg/cmd/cluster/create_subcmds.go b/pkg/cmd/cluster/create_subcmds.go index dc8407ba5..a86bd8596 100644 --- a/pkg/cmd/cluster/create_subcmds.go +++ b/pkg/cmd/cluster/create_subcmds.go @@ -68,7 +68,7 @@ func NewSubCmdsOptions(createOptions *action.CreateOptions, t cluster.ClusterTyp return o, nil } -func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { +func BuildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { var cmds []*cobra.Command for _, t := range cluster.SupportedTypes() { @@ -86,8 +86,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()) }, } @@ -102,7 +102,7 @@ func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { return cmds } -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 @@ -154,7 +154,7 @@ func (o *CreateSubCmdsOptions) complete(cmd *cobra.Command) error { return nil } -func (o *CreateSubCmdsOptions) validate() error { +func (o *CreateSubCmdsOptions) Validate() error { 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 e84269f45..882b9a22e 100644 --- a/pkg/cmd/cluster/create_subcmds_test.go +++ b/pkg/cmd/cluster/create_subcmds_test.go @@ -86,7 +86,7 @@ var _ = Describe("create cluster by cluster type", func() { It("create mysql cluster command", func() { By("create commands") - cmds := buildCreateSubCmds(createOptions) + cmds := BuildCreateSubCmds(createOptions) Expect(cmds).ShouldNot(BeNil()) Expect(cmds[0].HasFlags()).Should(BeTrue()) @@ -110,14 +110,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" diff --git a/pkg/cmd/playground/init.go b/pkg/cmd/playground/init.go index b5bb590c3..cf99794ba 100644 --- a/pkg/cmd/playground/init.go +++ b/pkg/cmd/playground/init.go @@ -20,6 +20,7 @@ along with this program. If not, see . package playground import ( + "context" "fmt" "os" "path/filepath" @@ -29,15 +30,19 @@ 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" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" "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" @@ -47,6 +52,7 @@ import ( "github.com/apecloud/kbcli/pkg/util/helm" "github.com/apecloud/kbcli/pkg/util/prompt" "github.com/apecloud/kbcli/version" + "github.com/apecloud/kubeblocks/pkg/constant" ) var ( @@ -419,6 +425,9 @@ func (o *initOptions) installKBAndCluster(info *cp.K8sClusterInfo) error { } s := spinner.New(o.Out, spinnerMsg("Create cluster %s (%s)", kbClusterName, clusterInfo)) defer s.Fail() + if err = o.waitClusterDef(); err != nil { + return errors.Wrapf(err, "failed to find clusterDefinition %s", o.clusterDef) + } if err = o.createCluster(); err != nil && !apierrors.IsAlreadyExists(err) { return errors.Wrapf(err, "failed to create cluster %s", kbClusterName) } @@ -519,7 +528,14 @@ func (o *initOptions) createCluster() error { // if we are running on cloud, create cluster with three replicas c.Values = append(c.Values, "replicas=3") } - + if err := o.checkClusterVersion(c); err != nil { + // if kubeblocks version greater than 1.0.0-alpha.0 or clusterVersion is not found + // within 30s, create cluster by create subcommand + if apierrors.IsNotFound(err) { + return o.createClusterBySubcmd(c) + } + return err + } if err := c.CreateOptions.Complete(); err != nil { return err } @@ -532,6 +548,99 @@ func (o *initOptions) createCluster() error { return c.Run() } +func (o *initOptions) checkClusterVersion(createOption *cmdcluster.CreateOptions) error { + v1, _ := gv.NewVersion("1.0.0-alpha.0") + version, _ := gv.NewVersion(o.kbVersion) + if len(o.kbVersion) == 0 || !version.LessThan(v1) { + // kubeblocks version greater than 1.0.0-alpha.0, there is no clusterVersion + return apierrors.NewNotFound(types.ClusterVersionGVR().GroupResource(), createOption.ClusterVersionRef) + } + // kubeblocks version less than 1.0.0-alpha.0, check clusterVersion + // assume clusterVersion is not defined if cannot find cluterVersion within 30s + dynamic, err := createOption.Factory.DynamicClient() + if err != nil { + return err + } + conditionFunc := func(_ context.Context) (bool, error) { + objs, err := dynamic.Resource(types.ClusterVersionGVR()).List(context.TODO(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", constant.ClusterDefLabelKey, createOption.ClusterDefRef), + }) + if err != nil && !apierrors.IsNotFound(err) { + return false, err + } + if objs == nil || len(objs.Items) == 0 { + return false, nil + } + return true, nil + } + // wait clusterVersion to be found, or a 30-second timeout + if err := wait.PollUntilContextTimeout(context.Background(), 5*time.Second, 30*time.Second, true, conditionFunc); err != nil { + if err == context.DeadlineExceeded { + return apierrors.NewNotFound(types.ClusterVersionGVR().GroupResource(), createOption.ClusterVersionRef) + } + return err + } + return nil +} + +func (o *initOptions) createClusterBySubcmd(createOption *cmdcluster.CreateOptions) error { + opt, err := cmdcluster.NewSubCmdsOptions(&createOption.CreateOptions, cluster.ClusterType(o.clusterDef)) + if err != nil { + return err + } + opt.CreateOptions.Format = "yaml" + if err := opt.CreateOptions.Complete(); err != nil { + return nil + } + if err := opt.Complete(&cobra.Command{}); err != nil { + return nil + } + if opt.Values == nil { + opt.Values = map[string]interface{}{} + } + // if we are running on local, create cluster with one replica + if o.cloudProvider == cp.Local { + opt.Values["replicas"] = int(1) + } else { + // if we are running on cloud, create cluster with three replicas + opt.Values["replicas"] = int(3) + if o.clusterDef == "apecloud-mysql" { + opt.Values["mode"] = "raftGroup" + } + } + if err := opt.Validate(); err != nil { + return err + } + if err := opt.Run(); err != nil { + return err + } + return nil +} + +func (o *initOptions) waitClusterDef() error { + f := util.NewFactory() + dynamic, err := f.DynamicClient() + if err != nil { + return err + } + conditionFunc := func(_ context.Context) (bool, error) { + _, err := dynamic.Resource(types.ClusterDefGVR()).Get(context.Background(), o.clusterDef, metav1.GetOptions{}) + if err == nil { + return true, nil + } + if apierrors.IsNotFound(err) { + return false, nil + } + return false, err + } + + // wait clusterDefinition to be found, or timeout + if err := wait.PollUntilContextTimeout(context.Background(), 5*time.Second, o.Timeout, true, conditionFunc); err != nil { + return err + } + return nil +} + // checkExistedCluster checks playground kubernetes cluster exists or not, a kbcli client only // support a single playground, they are bound to each other with a hidden context config file, // the hidden file ensures that when destroy the playground it always goes with the fixed context,