diff --git a/addons b/addons index 306c3a978..2f7d6e970 160000 --- a/addons +++ b/addons @@ -1 +1 @@ -Subproject commit 306c3a9786dd3937e3fe5ff18491a5007e5dc1ec +Subproject commit 2f7d6e9708467422f18bc7fe07043f50a6c86089 diff --git a/docs/user_docs/cli/kbcli_class.md b/docs/user_docs/cli/kbcli_class.md deleted file mode 100644 index fc5536db4..000000000 --- a/docs/user_docs/cli/kbcli_class.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: kbcli class ---- - -Manage classes - -### Options - -``` - -h, --help help for class -``` - -### Options inherited from parent commands - -``` - --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "$HOME/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --disable-compression If true, opt-out of response compression for all requests to the server - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - --match-server-version Require server version to match client version - -n, --namespace string If present, the namespace scope for this CLI request - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### SEE ALSO - - -* [kbcli class create](kbcli_class_create.md) - Create a class -* [kbcli class list](kbcli_class_list.md) - List classes -* [kbcli class template](kbcli_class_template.md) - Generate class definition template - -#### Go Back to [CLI Overview](cli.md) Homepage. - diff --git a/docs/user_docs/cli/kbcli_class_create.md b/docs/user_docs/cli/kbcli_class_create.md deleted file mode 100644 index c45743c2a..000000000 --- a/docs/user_docs/cli/kbcli_class_create.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: kbcli class create ---- - -Create a class - -``` -kbcli class create [NAME] [flags] -``` - -### Examples - -``` - # Create a class for component mysql in cluster definition apecloud-mysql, which has 1 CPU core and 1Gi memory - kbcli class create custom-1c1g --cluster-definition apecloud-mysql --type mysql --cpu 1 --memory 1Gi - - # Create classes for component mysql in cluster definition apecloud-mysql, with classes defined in file - kbcli class create --cluster-definition apecloud-mysql --type mysql --file ./classes.yaml -``` - -### Options - -``` - --cluster-definition string Specify cluster definition, run "kbcli clusterdefinition list" to show all available cluster definitions - --cpu string Specify component CPU cores - --file string Specify file path of class definition YAML - -h, --help help for create - --memory string Specify component memory size - --type string Specify component type -``` - -### Options inherited from parent commands - -``` - --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "$HOME/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --disable-compression If true, opt-out of response compression for all requests to the server - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - --match-server-version Require server version to match client version - -n, --namespace string If present, the namespace scope for this CLI request - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### SEE ALSO - -* [kbcli class](kbcli_class.md) - Manage classes - -#### Go Back to [CLI Overview](cli.md) Homepage. - diff --git a/docs/user_docs/cli/kbcli_class_list.md b/docs/user_docs/cli/kbcli_class_list.md deleted file mode 100644 index c44de63f0..000000000 --- a/docs/user_docs/cli/kbcli_class_list.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: kbcli class list ---- - -List classes - -``` -kbcli class list [flags] -``` - -### Examples - -``` - # List all components classes in cluster definition apecloud-mysql - kbcli class list --cluster-definition apecloud-mysql -``` - -### Options - -``` - --cluster-definition string Specify cluster definition, run "kbcli clusterdefinition list" to show all available cluster definition - -h, --help help for list -``` - -### Options inherited from parent commands - -``` - --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "$HOME/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --disable-compression If true, opt-out of response compression for all requests to the server - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - --match-server-version Require server version to match client version - -n, --namespace string If present, the namespace scope for this CLI request - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### SEE ALSO - -* [kbcli class](kbcli_class.md) - Manage classes - -#### Go Back to [CLI Overview](cli.md) Homepage. - diff --git a/docs/user_docs/cli/kbcli_class_template.md b/docs/user_docs/cli/kbcli_class_template.md deleted file mode 100644 index 92af595ad..000000000 --- a/docs/user_docs/cli/kbcli_class_template.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: kbcli class template ---- - -Generate class definition template - -``` -kbcli class template [flags] -``` - -### Options - -``` - -h, --help help for template - -o, --output string Output class definition template to a file -``` - -### Options inherited from parent commands - -``` - --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. - --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. - --as-uid string UID to impersonate for the operation. - --cache-dir string Default cache directory (default "$HOME/.kube/cache") - --certificate-authority string Path to a cert file for the certificate authority - --client-certificate string Path to a client certificate file for TLS - --client-key string Path to a client key file for TLS - --cluster string The name of the kubeconfig cluster to use - --context string The name of the kubeconfig context to use - --disable-compression If true, opt-out of response compression for all requests to the server - --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure - --kubeconfig string Path to the kubeconfig file to use for CLI requests. - --match-server-version Require server version to match client version - -n, --namespace string If present, the namespace scope for this CLI request - --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") - -s, --server string The address and port of the Kubernetes API server - --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used - --token string Bearer token for authentication to the API server - --user string The name of the kubeconfig user to use -``` - -### SEE ALSO - -* [kbcli class](kbcli_class.md) - Manage classes - -#### Go Back to [CLI Overview](cli.md) Homepage. - diff --git a/docs/user_docs/cli/kbcli_cluster_create.md b/docs/user_docs/cli/kbcli_cluster_create.md index 0daafdfe2..1b1c5060f 100644 --- a/docs/user_docs/cli/kbcli_cluster_create.md +++ b/docs/user_docs/cli/kbcli_cluster_create.md @@ -125,7 +125,7 @@ kbcli cluster create [NAME] [flags] --rbac-enabled Specify whether rbac resources will be created by kbcli, otherwise KubeBlocks server will try to create rbac resources --restore-to-time string Set a time for point in time recovery --service-reference stringArray Set the other KubeBlocks cluster dependencies, each '--service-reference' corresponds to a cluster service. (e.g --service-reference name=pulsarZookeeper,cluster=zookeeper,namespace=default) - --set stringArray Set the cluster resource including cpu, memory, replicas and storage, each set corresponds to a component.(e.g. --set cpu=1,memory=1Gi,replicas=3,storage=20Gi or --set class=general-1c1g) + --set stringArray Set the cluster resource including cpu, memory, replicas and storage, each set corresponds to a component.(e.g. --set cpu=1,memory=1Gi,replicas=3,storage=20Gi) -f, --set-file string Use yaml file, URL, or stdin to set the cluster resource --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string Termination policy, one of: (DoNotTerminate, Halt, Delete, WipeOut) (default "Delete") diff --git a/docs/user_docs/cli/kbcli_cluster_create_llm.md b/docs/user_docs/cli/kbcli_cluster_create_llm.md index 9be93f12d..1b4344156 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_llm.md +++ b/docs/user_docs/cli/kbcli_cluster_create_llm.md @@ -23,7 +23,7 @@ kbcli cluster create llm NAME [flags] ``` --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0, 64]. - --cpu-mode Set to true if no GPU is available + --cpu-mode Set to true if no GPU is available, default true (default true) --extra-args string extra arguments that will be passed to run model (default "--trust-remote-code") --gpu float GPU cores. Value range [0, 64]. (default 1) -h, --help help for llm diff --git a/docs/user_docs/cli/kbcli_cluster_vscale.md b/docs/user_docs/cli/kbcli_cluster_vscale.md index 96cbb0453..c254d9af7 100644 --- a/docs/user_docs/cli/kbcli_cluster_vscale.md +++ b/docs/user_docs/cli/kbcli_cluster_vscale.md @@ -13,16 +13,12 @@ kbcli cluster vscale NAME [flags] ``` # scale the computing resources of specified components, separate with commas for multiple components kbcli cluster vscale mycluster --components=mysql --cpu=500m --memory=500Mi - - # scale the computing resources of specified components by class, run command 'kbcli class list --cluster-definition cluster-definition-name' to get available classes - kbcli cluster vscale mycluster --components=mysql --class=general-2c4g ``` ### Options ``` --auto-approve Skip interactive approval before vertically scaling the cluster - --class string Component class --components strings Component names to this operations --cpu string Request and limit size of component cpu --dry-run string[="unchanged"] Must be "client", or "server". If with client strategy, only print the object that would be sent, and no data is actually sent. If with server strategy, submit the server-side request, but no data is persistent. (default "none") diff --git a/pkg/action/template/cluster_operations_template.cue b/pkg/action/template/cluster_operations_template.cue index 295c77df0..453f51785 100644 --- a/pkg/action/template/cluster_operations_template.cue +++ b/pkg/action/template/cluster_operations_template.cue @@ -47,8 +47,6 @@ options: { ] cpu: string memory: string - class: string - classDefRef: {...} replicas: int storage: string vctNames: [...string] @@ -128,9 +126,6 @@ content: { if options.type == "VerticalScaling" { verticalScaling: [ for _, cName in options.componentNames { componentName: cName - if options.class != "" { - classDefRef: options.classDefRef - } requests: { if options.memory != "" { memory: options.memory diff --git a/pkg/cluster/charts/kafka-cluster.tgz b/pkg/cluster/charts/kafka-cluster.tgz index f303d970d..91e53c278 100644 Binary files a/pkg/cluster/charts/kafka-cluster.tgz and b/pkg/cluster/charts/kafka-cluster.tgz differ diff --git a/pkg/cluster/charts/llm-cluster.tgz b/pkg/cluster/charts/llm-cluster.tgz index 2fc5d32ea..9fb18281b 100644 Binary files a/pkg/cluster/charts/llm-cluster.tgz and b/pkg/cluster/charts/llm-cluster.tgz differ diff --git a/pkg/cluster/charts/redis-cluster.tgz b/pkg/cluster/charts/redis-cluster.tgz index 25c480d13..7e96981be 100644 Binary files a/pkg/cluster/charts/redis-cluster.tgz and b/pkg/cluster/charts/redis-cluster.tgz differ diff --git a/pkg/cmd/class/class.go b/pkg/cmd/class/class.go deleted file mode 100644 index 916de829b..000000000 --- a/pkg/cmd/class/class.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "github.com/spf13/cobra" - "k8s.io/cli-runtime/pkg/genericiooptions" - cmdutil "k8s.io/kubectl/pkg/cmd/util" -) - -func NewClassCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { - cmd := &cobra.Command{ - Use: "class", - Short: "Manage classes", - Deprecated: "This command is deprecated.", - } - - cmd.AddCommand(NewCreateCommand(f, streams)) - cmd.AddCommand(NewListCommand(f, streams)) - cmd.AddCommand(NewTemplateCmd(streams)) - - return cmd -} diff --git a/pkg/cmd/class/class_test.go b/pkg/cmd/class/class_test.go deleted file mode 100644 index 3a4afd918..000000000 --- a/pkg/cmd/class/class_test.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/cli-runtime/pkg/genericiooptions" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -var _ = Describe("class", func() { - var streams genericiooptions.IOStreams - var tf *cmdtesting.TestFactory - - BeforeEach(func() { - streams, _, _, _ = genericiooptions.NewTestIOStreams() - tf = cmdtesting.NewTestFactory() - }) - - AfterEach(func() { - tf.Cleanup() - }) - - It("command should succeed", func() { - cmd := NewClassCommand(tf, streams) - Expect(cmd).ShouldNot(BeNil()) - }) -}) diff --git a/pkg/cmd/class/create.go b/pkg/cmd/class/create.go deleted file mode 100644 index 3ef4c1332..000000000 --- a/pkg/cmd/class/create.go +++ /dev/null @@ -1,309 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/ghodss/yaml" - "github.com/spf13/cobra" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/client-go/dynamic" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - utilcomp "k8s.io/kubectl/pkg/util/completion" - "k8s.io/kubectl/pkg/util/templates" - - "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/constant" - "github.com/apecloud/kubeblocks/pkg/controller/component" - - "github.com/apecloud/kbcli/pkg/types" - "github.com/apecloud/kbcli/pkg/util" -) - -type CreateOptions struct { - genericiooptions.IOStreams - - // REVIEW: make this field a parameter which can be set by user - objectName string - - Factory cmdutil.Factory - dynamic dynamic.Interface - ClusterDefRef string - ComponentType string - ClassName string - CPU string - Memory string - File string -} - -var classCreateExamples = templates.Examples(` - # Create a class for component mysql in cluster definition apecloud-mysql, which has 1 CPU core and 1Gi memory - kbcli class create custom-1c1g --cluster-definition apecloud-mysql --type mysql --cpu 1 --memory 1Gi - - # Create classes for component mysql in cluster definition apecloud-mysql, with classes defined in file - kbcli class create --cluster-definition apecloud-mysql --type mysql --file ./classes.yaml -`) - -func NewCreateCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { - o := CreateOptions{IOStreams: streams} - cmd := &cobra.Command{ - Use: "create [NAME]", - Short: "Create a class", - Example: classCreateExamples, - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(o.complete(f)) - util.CheckErr(o.validate(args)) - util.CheckErr(o.run()) - }, - } - cmd.Flags().StringVar(&o.ClusterDefRef, "cluster-definition", "", "Specify cluster definition, run \"kbcli clusterdefinition list\" to show all available cluster definitions") - util.CheckErr(cmd.MarkFlagRequired("cluster-definition")) - cmd.Flags().StringVar(&o.ComponentType, "type", "", "Specify component type") - util.CheckErr(cmd.MarkFlagRequired("type")) - - cmd.Flags().StringVar(&o.CPU, corev1.ResourceCPU.String(), "", "Specify component CPU cores") - cmd.Flags().StringVar(&o.Memory, corev1.ResourceMemory.String(), "", "Specify component memory size") - - cmd.Flags().StringVar(&o.File, "file", "", "Specify file path of class definition YAML") - - // register flag completion func - registerFlagCompletionFunc(cmd, f) - - return cmd -} - -func (o *CreateOptions) validate(args []string) error { - // validate creating by resource arguments - if o.File != "" { - return nil - } - - // validate cpu and memory - if _, err := resource.ParseQuantity(o.CPU); err != nil { - return err - } - if _, err := resource.ParseQuantity(o.Memory); err != nil { - return err - } - - // validate class name - if len(args) == 0 { - return fmt.Errorf("missing class name") - } - o.ClassName = args[0] - - return nil -} - -func (o *CreateOptions) complete(f cmdutil.Factory) error { - var err error - o.dynamic, err = f.DynamicClient() - return err -} - -func (o *CreateOptions) run() error { - clsMgr, _, err := GetManager(o.dynamic, o.ClusterDefRef) - if err != nil { - return err - } - - constraints, err := GetResourceConstraints(o.dynamic) - if err != nil { - return err - } - - var ( - classInstances []*v1alpha1.ComponentClass - componentClassGroups []v1alpha1.ComponentClassGroup - ) - - if o.File != "" { - data, err := os.ReadFile(o.File) - if err != nil { - return err - } - if err := yaml.Unmarshal(data, &componentClassGroups); err != nil { - return err - } - classDefinition := v1alpha1.ComponentClassDefinition{ - Spec: v1alpha1.ComponentClassDefinitionSpec{Groups: componentClassGroups}, - } - newClasses, err := component.ParseComponentClasses(classDefinition) - if err != nil { - return err - } - for _, cls := range newClasses { - classInstances = append(classInstances, cls) - } - } else { - cls := v1alpha1.ComponentClass{Name: o.ClassName, CPU: resource.MustParse(o.CPU), Memory: resource.MustParse(o.Memory)} - if err != nil { - return err - } - componentClassGroups = []v1alpha1.ComponentClassGroup{ - { - Series: []v1alpha1.ComponentClassSeries{ - { - Classes: []v1alpha1.ComponentClass{cls}, - }, - }, - }, - } - classInstances = append(classInstances, &cls) - } - - var ( - classNames []string - objName = o.objectName - ) - if objName == "" { - objName = GetCustomClassObjectName(o.ClusterDefRef, o.ComponentType) - } - - var rules []v1alpha1.ResourceConstraintRule - for _, constraint := range constraints { - rules = append(rules, constraint.FindRules(o.ClusterDefRef, o.ComponentType)...) - } - - for _, item := range classInstances { - clsDefRef := v1alpha1.ClassDefRef{Name: objName, Class: item.Name} - if clsMgr.HasClass(o.ComponentType, clsDefRef) { - return fmt.Errorf("class name conflicted %s", item.Name) - } - classNames = append(classNames, item.Name) - - if len(rules) == 0 { - continue - } - - match := false - for _, rule := range rules { - if rule.ValidateResources(item.ToResourceRequirements().Requests) { - match = true - break - } - } - if !match { - return fmt.Errorf("class %s does not conform to its constraints", item.Name) - } - } - - obj, err := o.dynamic.Resource(types.ComponentClassDefinitionGVR()).Get(context.TODO(), objName, metav1.GetOptions{}) - if err != nil && !errors.IsNotFound(err) { - return err - } - - var classDefinition v1alpha1.ComponentClassDefinition - if err == nil { - if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &classDefinition); err != nil { - return err - } - classDefinition.Spec.Groups = append(classDefinition.Spec.Groups, componentClassGroups...) - unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&classDefinition) - if err != nil { - return err - } - if _, err = o.dynamic.Resource(types.ComponentClassDefinitionGVR()).Update( - context.Background(), &unstructured.Unstructured{Object: unstructuredMap}, metav1.UpdateOptions{}); err != nil { - return err - } - } else { - gvr := types.ComponentClassDefinitionGVR() - classDefinition = v1alpha1.ComponentClassDefinition{ - TypeMeta: metav1.TypeMeta{ - Kind: types.KindComponentClassDefinition, - APIVersion: gvr.Group + "/" + gvr.Version, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: GetCustomClassObjectName(o.ClusterDefRef, o.ComponentType), - Labels: map[string]string{ - constant.ClusterDefLabelKey: o.ClusterDefRef, - types.ClassProviderLabelKey: "user", - constant.KBAppComponentDefRefLabelKey: o.ComponentType, - }, - }, - Spec: v1alpha1.ComponentClassDefinitionSpec{ - Groups: componentClassGroups, - }, - } - unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&classDefinition) - if err != nil { - return err - } - if _, err = o.dynamic.Resource(types.ComponentClassDefinitionGVR()).Create( - context.Background(), &unstructured.Unstructured{Object: unstructuredMap}, metav1.CreateOptions{}); err != nil { - return err - } - } - _, _ = fmt.Fprintf(o.Out, "Successfully create class [%s].\n", strings.Join(classNames, ",")) - return nil -} - -func registerFlagCompletionFunc(cmd *cobra.Command, f cmdutil.Factory) { - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "cluster-definition", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return utilcomp.CompGetResource(f, util.GVRToString(types.ClusterDefGVR()), toComplete), cobra.ShellCompDirectiveNoFileComp - })) - util.CheckErr(cmd.RegisterFlagCompletionFunc( - "type", - func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var ( - componentTypes []string - selector string - compTypeLabel = "apps.kubeblocks.io/component-def-ref" - ) - - client, err := f.DynamicClient() - if err != nil { - return componentTypes, cobra.ShellCompDirectiveNoFileComp - } - - clusterDefinition, err := cmd.Flags().GetString("cluster-definition") - if err == nil && clusterDefinition != "" { - selector = fmt.Sprintf("%s=%s,%s", constant.ClusterDefLabelKey, clusterDefinition, types.ClassProviderLabelKey) - } - objs, err := client.Resource(types.ComponentClassDefinitionGVR()).List(context.Background(), metav1.ListOptions{LabelSelector: selector}) - if err != nil { - return componentTypes, cobra.ShellCompDirectiveNoFileComp - } - var classDefinitionList v1alpha1.ComponentClassDefinitionList - if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objs.UnstructuredContent(), &classDefinitionList); err != nil { - return componentTypes, cobra.ShellCompDirectiveNoFileComp - } - for _, item := range classDefinitionList.Items { - componentType := item.Labels[compTypeLabel] - if componentType != "" { - componentTypes = append(componentTypes, componentType) - } - } - return componentTypes, cobra.ShellCompDirectiveNoFileComp - })) -} diff --git a/pkg/cmd/class/create_test.go b/pkg/cmd/class/create_test.go deleted file mode 100644 index d95e66724..000000000 --- a/pkg/cmd/class/create_test.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "bytes" - "fmt" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/cli-runtime/pkg/genericiooptions" - - "k8s.io/client-go/kubernetes/scheme" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - - "github.com/apecloud/kbcli/pkg/testing" -) - -var _ = Describe("create", func() { - var ( - createOptions *CreateOptions - out *bytes.Buffer - tf *cmdtesting.TestFactory - streams genericiooptions.IOStreams - ) - - fillResources := func(o *CreateOptions, cpu string, memory string) { - o.CPU = cpu - o.Memory = memory - o.ClassName = fmt.Sprintf("custom-%s-%s", cpu, memory) - } - - BeforeEach(func() { - streams, _, out, _ = genericiooptions.NewTestIOStreams() - tf = testing.NewTestFactory(namespace) - _ = appsv1alpha1.AddToScheme(scheme.Scheme) - createOptions = &CreateOptions{ - IOStreams: streams, - ClusterDefRef: "apecloud-mysql", - ComponentType: "mysql", - } - }) - - AfterEach(func() { - tf.Cleanup() - }) - - It("should succeed to new command", func() { - cmd := NewCreateCommand(tf, streams) - Expect(cmd).ShouldNot(BeNil()) - }) - - Context("with resource arguments", func() { - - Context("without constraints", func() { - BeforeEach(func() { - tf.FakeDynamicClient = testing.FakeDynamicClient(&classDef, &generalResourceConstraintWithoutSelector) - createOptions.Factory = tf - Expect(createOptions.complete(tf)).ShouldNot(HaveOccurred()) - }) - - It("should succeed if component don't have any constraints", func() { - fillResources(createOptions, "1", "100Gi") - Expect(createOptions.run()).ShouldNot(HaveOccurred()) - }) - - }) - - Context("with constraints", func() { - BeforeEach(func() { - tf.FakeDynamicClient = testing.FakeDynamicClient(&classDef, &generalResourceConstraint, &memoryOptimizedResourceConstraint) - createOptions.Factory = tf - Expect(createOptions.complete(tf)).ShouldNot(HaveOccurred()) - }) - - It("should fail if required arguments is missing", func() { - fillResources(createOptions, "", "48Gi") - Expect(createOptions.validate([]string{"general-12c48g"})).Should(HaveOccurred()) - fillResources(createOptions, "12", "") - Expect(createOptions.validate([]string{"general-12c48g"})).Should(HaveOccurred()) - fillResources(createOptions, "12", "48g") - Expect(createOptions.validate([]string{})).Should(HaveOccurred()) - }) - - It("should succeed with required arguments", func() { - fillResources(createOptions, "96", "384Gi") - Expect(createOptions.validate([]string{"general-96c384g"})).ShouldNot(HaveOccurred()) - Expect(createOptions.run()).ShouldNot(HaveOccurred()) - Expect(out.String()).Should(ContainSubstring(createOptions.ClassName)) - }) - - It("should fail if not conformed to constraint", func() { - By("memory not conformed to constraint") - fillResources(createOptions, "2", "9Gi") - Expect(createOptions.run()).Should(HaveOccurred()) - - By("CPU with invalid step") - fillResources(createOptions, "0.6", "0.6Gi") - Expect(createOptions.run()).Should(HaveOccurred()) - }) - - It("should fail if class name is conflicted", func() { - // class may be conflict only within the same object, so we set the objectName to be consistent with the name of the object classDef - createOptions.objectName = "kb.classes.default.apecloud-mysql.mysql" - - fillResources(createOptions, "1", "1Gi") - createOptions.ClassName = "general-1c1g" - Expect(createOptions.run()).Should(HaveOccurred()) - - fillResources(createOptions, "0.5", "0.5Gi") - Expect(createOptions.run()).ShouldNot(HaveOccurred()) - - fillResources(createOptions, "0.5", "0.5Gi") - Expect(createOptions.run()).Should(HaveOccurred()) - }) - }) - }) - - Context("with class definitions file", func() { - BeforeEach(func() { - tf.FakeDynamicClient = testing.FakeDynamicClient(&classDef, &generalResourceConstraint, &memoryOptimizedResourceConstraint) - createOptions.Factory = tf - Expect(createOptions.complete(tf)).ShouldNot(HaveOccurred()) - }) - - It("should succeed", func() { - createOptions.File = testCustomClassDefsPath - Expect(createOptions.run()).ShouldNot(HaveOccurred()) - Expect(out.String()).Should(ContainSubstring("custom-1c1g")) - Expect(out.String()).Should(ContainSubstring("custom-4c16g")) - // memory optimized classes - Expect(out.String()).Should(ContainSubstring("custom-2c16g")) - Expect(out.String()).Should(ContainSubstring("custom-4c64g")) - }) - - }) - -}) diff --git a/pkg/cmd/class/list.go b/pkg/cmd/class/list.go deleted file mode 100644 index afdbc185c..000000000 --- a/pkg/cmd/class/list.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "fmt" - "sort" - "strings" - - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/client-go/dynamic" - cmdutil "k8s.io/kubectl/pkg/cmd/util" - "k8s.io/kubectl/pkg/util/templates" - - "github.com/apecloud/kubeblocks/pkg/controller/component" - - "github.com/apecloud/kbcli/pkg/printer" - "github.com/apecloud/kbcli/pkg/util" - "github.com/apecloud/kbcli/pkg/util/flags" -) - -type ListOptions struct { - ClusterDefRef string - Factory cmdutil.Factory - dynamic dynamic.Interface - genericiooptions.IOStreams -} - -var listClassExamples = templates.Examples(` - # List all components classes in cluster definition apecloud-mysql - kbcli class list --cluster-definition apecloud-mysql -`) - -func NewListCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { - o := &ListOptions{IOStreams: streams} - cmd := &cobra.Command{ - Use: "list", - Short: "List classes", - Example: listClassExamples, - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(o.complete(f)) - util.CheckErr(o.run()) - }, - } - flags.AddClusterDefinitionFlag(f, cmd, &o.ClusterDefRef) - util.CheckErr(cmd.MarkFlagRequired("cluster-definition")) - return cmd -} - -func (o *ListOptions) complete(f cmdutil.Factory) error { - var err error - o.dynamic, err = f.DynamicClient() - return err -} - -func (o *ListOptions) run() error { - clsMgr, _, err := GetManager(o.dynamic, o.ClusterDefRef) - if err != nil { - return err - } - for compName, classes := range clsMgr.GetClasses() { - o.printClass(compName, classes) - } - return nil -} - -func (o *ListOptions) printClass(compName string, classes []*component.ComponentClassWithRef) { - tbl := printer.NewTablePrinter(o.Out) - tbl.SetHeader("COMPONENT", "CLASS", "CPU", "MEMORY") - sort.Sort(component.ByClassResource(classes)) - for _, cls := range classes { - tbl.AddRow(compName, cls.Name, cls.CPU.String(), normalizeMemory(cls.Memory)) - } - tbl.Print() -} - -func normalizeMemory(mem resource.Quantity) string { - if !strings.HasSuffix(mem.String(), "m") { - return mem.String() - } - - var ( - value float64 - suffix string - bytes = float64(mem.MilliValue()) / 1000 - ) - switch { - case bytes < 1024: - value = bytes / 1024 - suffix = "Ki" - case bytes < 1024*1024: - value = bytes / 1024 / 1024 - suffix = "Mi" - case bytes < 1024*1024*1024: - value = bytes / 1024 / 1024 / 1024 - suffix = "Gi" - default: - value = bytes / 1024 / 1024 / 1024 / 1024 - suffix = "Ti" - } - return strings.TrimRight(fmt.Sprintf("%.3f", value), "0") + suffix -} diff --git a/pkg/cmd/class/list_test.go b/pkg/cmd/class/list_test.go deleted file mode 100644 index f549d8705..000000000 --- a/pkg/cmd/class/list_test.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "bytes" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/client-go/kubernetes/scheme" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" - - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - - "github.com/apecloud/kbcli/pkg/testing" -) - -var _ = Describe("list", func() { - var ( - out *bytes.Buffer - tf *cmdtesting.TestFactory - streams genericiooptions.IOStreams - ) - - BeforeEach(func() { - streams, _, out, _ = genericiooptions.NewTestIOStreams() - tf = testing.NewTestFactory(namespace) - _ = appsv1alpha1.AddToScheme(scheme.Scheme) - tf.FakeDynamicClient = testing.FakeDynamicClient(&classDef) - }) - - AfterEach(func() { - tf.Cleanup() - }) - - It("should succeed", func() { - cmd := NewListCommand(tf, streams) - Expect(cmd).ShouldNot(BeNil()) - _ = cmd.Flags().Set("cluster-definition", "apecloud-mysql") - cmd.Run(cmd, []string{}) - Expect(out.String()).To(ContainSubstring("general-1c1g")) - Expect(out.String()).To(ContainSubstring("mysql")) - }) - - It("memory should be normalized", func() { - cases := []struct { - memory string - normalized string - }{ - { - memory: "0.2Gi", - normalized: "0.2Gi", - }, - { - memory: "0.2Mi", - normalized: "0.2Mi", - }, - { - memory: "0.2Ki", - normalized: "0.2Ki", - }, - { - memory: "1024Mi", - normalized: "1Gi", - }, - { - memory: "1025Mi", - normalized: "1025Mi", - }, - { - memory: "1023Mi", - normalized: "1023Mi", - }, - { - memory: "1Gi", - normalized: "1Gi", - }, - { - memory: "512Mi", - normalized: "512Mi", - }, - } - for _, item := range cases { - Expect(normalizeMemory(resource.MustParse(item.memory))).Should(Equal(item.normalized)) - } - }) -}) diff --git a/pkg/cmd/class/suite_test.go b/pkg/cmd/class/suite_test.go deleted file mode 100644 index d80ed5c58..000000000 --- a/pkg/cmd/class/suite_test.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "os" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/client-go/kubernetes/scheme" - - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" -) - -const ( - namespace = "test" - testDefaultClassDefsPath = "../../testing/testdata/class.yaml" - testCustomClassDefsPath = "../../testing/testdata/custom_class.yaml" - testGeneralResourceConstraintPath = "../../testing/testdata/resource-constraint-general.yaml" - testMemoryOptimizedResourceConstraintPath = "../../testing/testdata/resource-constraint-memory-optimized.yaml" - testGeneralResourceConstraintWithoutSelectorPath = "../../testing/testdata/resource-constraint-general-without-selector.yaml" -) - -var ( - classDef appsv1alpha1.ComponentClassDefinition - generalResourceConstraint appsv1alpha1.ComponentResourceConstraint - generalResourceConstraintWithoutSelector appsv1alpha1.ComponentResourceConstraint - memoryOptimizedResourceConstraint appsv1alpha1.ComponentResourceConstraint -) - -var _ = BeforeSuite(func() { - var err error - - classDefBytes, err := os.ReadFile(testDefaultClassDefsPath) - Expect(err).ShouldNot(HaveOccurred()) - err = yaml.Unmarshal(classDefBytes, &classDef) - Expect(err).ShouldNot(HaveOccurred()) - - generalResourceConstraintBytes, err := os.ReadFile(testGeneralResourceConstraintPath) - Expect(err).ShouldNot(HaveOccurred()) - err = yaml.Unmarshal(generalResourceConstraintBytes, &generalResourceConstraint) - Expect(err).ShouldNot(HaveOccurred()) - - generalResourceConstraintWithoutSelectorBytes, err := os.ReadFile(testGeneralResourceConstraintWithoutSelectorPath) - Expect(err).ShouldNot(HaveOccurred()) - err = yaml.Unmarshal(generalResourceConstraintWithoutSelectorBytes, &generalResourceConstraintWithoutSelector) - Expect(err).ShouldNot(HaveOccurred()) - - memoryOptimizedResourceConstraintBytes, err := os.ReadFile(testMemoryOptimizedResourceConstraintPath) - Expect(err).ShouldNot(HaveOccurred()) - err = yaml.Unmarshal(memoryOptimizedResourceConstraintBytes, &memoryOptimizedResourceConstraint) - Expect(err).ShouldNot(HaveOccurred()) - - err = appsv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).ShouldNot(HaveOccurred()) - - err = corev1.AddToScheme(scheme.Scheme) - Expect(err).ShouldNot(HaveOccurred()) - -}) - -func TestClass(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Class Suite") -} diff --git a/pkg/cmd/class/template.go b/pkg/cmd/class/template.go deleted file mode 100644 index b380fd17d..000000000 --- a/pkg/cmd/class/template.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "k8s.io/cli-runtime/pkg/genericiooptions" - - "github.com/apecloud/kbcli/pkg/util" -) - -const ComponentClassTemplate = ` -- # class template, you can declare variables and set default values here - template: | - cpu: "{{ or .cpu 1 }}" - memory: "{{ or .memory 4 }}Gi" - # template variables used to define classes - vars: [cpu, memory] - series: - - # class naming template, you can reference variables in class template - # it's also ok to define static class name in the following class definitions - namingTemplate: "custom-{{ .cpu }}c{{ .memory }}g" - - # class definitions, we support two kinds of class definitions: - # 1. dynamically classes rendered with defined values & template variables - # 2. statically defined classes - classes: - - args: [1, 1] -` - -type TemplateOptions struct { - genericiooptions.IOStreams - - outputFile string -} - -func NewTemplateCmd(streams genericiooptions.IOStreams) *cobra.Command { - o := &TemplateOptions{IOStreams: streams} - cmd := &cobra.Command{ - Use: "template", - Short: "Generate class definition template", - Run: func(cmd *cobra.Command, args []string) { - util.CheckErr(o.run()) - }, - } - cmd.Flags().StringVarP(&o.outputFile, "output", "o", "", "Output class definition template to a file") - return cmd -} - -func (o *TemplateOptions) run() error { - if o.outputFile != "" { - return os.WriteFile(o.outputFile, []byte(ComponentClassTemplate), 0644) - } - - _, err := fmt.Fprint(o.Out, ComponentClassTemplate) - return err -} diff --git a/pkg/cmd/class/template_test.go b/pkg/cmd/class/template_test.go deleted file mode 100644 index d138ab7c3..000000000 --- a/pkg/cmd/class/template_test.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "bytes" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/cli-runtime/pkg/genericiooptions" -) - -var _ = Describe("template", func() { - var ( - out *bytes.Buffer - streams genericiooptions.IOStreams - ) - - BeforeEach(func() { - streams, _, out, _ = genericiooptions.NewTestIOStreams() - }) - - It("command should succeed", func() { - cmd := NewTemplateCmd(streams) - Expect(cmd).ShouldNot(BeNil()) - - cmd.Run(cmd, []string{}) - Expect(out.String()).ShouldNot(BeEmpty()) - }) -}) diff --git a/pkg/cmd/class/utils.go b/pkg/cmd/class/utils.go deleted file mode 100644 index 08e963fa7..000000000 --- a/pkg/cmd/class/utils.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package class - -import ( - "context" - "fmt" - - "github.com/apecloud/kubeblocks/pkg/controller/component" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/dynamic" - - "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/constant" - - "github.com/apecloud/kbcli/pkg/types" -) - -// GetManager gets a class manager which manages default classes and user custom classes -func GetManager(client dynamic.Interface, cdName string) (*component.Manager, *v1alpha1.ComponentResourceConstraintList, error) { - selector := fmt.Sprintf("%s=%s,%s", constant.ClusterDefLabelKey, cdName, types.ClassProviderLabelKey) - classObjs, err := client.Resource(types.ComponentClassDefinitionGVR()).Namespace("").List(context.TODO(), metav1.ListOptions{ - LabelSelector: selector, - }) - if err != nil { - return nil, nil, err - } - var classDefinitionList v1alpha1.ComponentClassDefinitionList - if err = runtime.DefaultUnstructuredConverter.FromUnstructured(classObjs.UnstructuredContent(), &classDefinitionList); err != nil { - return nil, nil, err - } - - constraintObjs, err := client.Resource(types.ComponentResourceConstraintGVR()).Namespace("").List(context.TODO(), metav1.ListOptions{}) - if err != nil { - return nil, nil, err - } - var resourceConstraintList v1alpha1.ComponentResourceConstraintList - if err = runtime.DefaultUnstructuredConverter.FromUnstructured(constraintObjs.UnstructuredContent(), &resourceConstraintList); err != nil { - return nil, nil, err - } - mgr, err := component.NewManager(classDefinitionList, resourceConstraintList) - return mgr, &resourceConstraintList, err -} - -// GetResourceConstraints gets all resource constraints -func GetResourceConstraints(dynamic dynamic.Interface) (map[string]*v1alpha1.ComponentResourceConstraint, error) { - objs, err := dynamic.Resource(types.ComponentResourceConstraintGVR()).List(context.TODO(), metav1.ListOptions{ - // LabelSelector: types.ResourceConstraintProviderLabelKey, - }) - if err != nil { - return nil, err - } - var constraintsList v1alpha1.ComponentResourceConstraintList - if err = runtime.DefaultUnstructuredConverter.FromUnstructured(objs.UnstructuredContent(), &constraintsList); err != nil { - return nil, err - } - - result := make(map[string]*v1alpha1.ComponentResourceConstraint) - for idx := range constraintsList.Items { - cf := constraintsList.Items[idx] - if _, ok := cf.GetLabels()[constant.ResourceConstraintProviderLabelKey]; !ok { - continue - } - result[cf.GetName()] = &cf - } - return result, nil -} - -// GetCustomClassObjectName returns the name of the ComponentClassDefinition object containing the custom classes -func GetCustomClassObjectName(cdName string, componentName string) string { - return fmt.Sprintf("kb.classes.custom.%s.%s", cdName, componentName) -} - -func ValidateResources(clsMGR *component.Manager, - resourceConstraintList *v1alpha1.ComponentResourceConstraintList, - clusterDefRef string, - comp *v1alpha1.ClusterComponentSpec) error { - if comp.ClassDefRef != nil && comp.ClassDefRef.Class != "" { - if clsMGR.HasClass(comp.ComponentDefRef, *comp.ClassDefRef) { - return nil - } - return fmt.Errorf("class not found") - } - - var rules []v1alpha1.ResourceConstraintRule - for _, constraint := range resourceConstraintList.Items { - rules = append(rules, constraint.FindRules(clusterDefRef, comp.ComponentDefRef)...) - } - if len(rules) == 0 { - return nil - } - - for _, rule := range rules { - if !rule.ValidateResources(comp.Resources.Requests) { - continue - } - - // validate volume - match := true - // all volumes should match the rules - for _, volume := range comp.VolumeClaimTemplates { - if !rule.ValidateStorage(volume.Spec.Resources.Requests.Storage()) { - match = false - break - } - } - if match { - return nil - } - } - return fmt.Errorf("resource is not conform to the constraints, please check the ComponentResourceConstraint API") -} diff --git a/pkg/cmd/cli.go b/pkg/cmd/cli.go index 3ae3ff5db..936e48e18 100644 --- a/pkg/cmd/cli.go +++ b/pkg/cmd/cli.go @@ -43,7 +43,6 @@ import ( "github.com/apecloud/kbcli/pkg/cmd/backuprepo" "github.com/apecloud/kbcli/pkg/cmd/bench" "github.com/apecloud/kbcli/pkg/cmd/builder" - "github.com/apecloud/kbcli/pkg/cmd/class" "github.com/apecloud/kbcli/pkg/cmd/cluster" "github.com/apecloud/kbcli/pkg/cmd/clusterdefinition" "github.com/apecloud/kbcli/pkg/cmd/clusterversion" @@ -202,7 +201,6 @@ A Command Line Interface for KubeBlocks`, dashboard.NewDashboardCmd(f, ioStreams), clusterversion.NewClusterVersionCmd(f, ioStreams), clusterdefinition.NewClusterDefinitionCmd(f, ioStreams), - class.NewClassCommand(f, ioStreams), alert.NewAlertCmd(f, ioStreams), addon.NewAddonCmd(f, ioStreams), migration.NewMigrationCmd(f, ioStreams), diff --git a/pkg/cmd/cluster/cluster_test.go b/pkg/cmd/cluster/cluster_test.go index 9c1601023..41f5e9fcd 100644 --- a/pkg/cmd/cluster/cluster_test.go +++ b/pkg/cmd/cluster/cluster_test.go @@ -43,12 +43,8 @@ import ( var _ = Describe("Cluster", func() { const ( - testComponentPath = "../../testing/testdata/component.yaml" - testComponentWithClassPath = "../../testing/testdata/component_with_class_1c1g.yaml" - testComponentWithInvalidClassPath = "../../testing/testdata/component_with_invalid_class.yaml" - testComponentWithResourcePath = "../../testing/testdata/component_with_resource_1c1g.yaml" - testComponentWithInvalidResourcePath = "../../testing/testdata/component_with_invalid_resource.yaml" - testClusterPath = "../../testing/testdata/cluster.yaml" + testComponentPath = "../../testing/testdata/component.yaml" + testClusterPath = "../../testing/testdata/cluster.yaml" ) const ( @@ -177,69 +173,6 @@ var _ = Describe("Cluster", func() { Expect(o.Validate()).Should(HaveOccurred()) }) - It("should succeed if component with valid class", func() { - o.Values = []string{fmt.Sprintf("type=%s,class=%s", testing.ComponentDefName, testapps.Class1c1gName)} - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should fail if component with invalid class", func() { - o.Values = []string{fmt.Sprintf("type=%s,class=class-not-exists", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - }) - - It("should succeed if component with resource meets the resource constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=1,memory=1Gi", testing.ComponentDefName)} - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should succeed if component with resource with smaller unit meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=1000m,memory=1024Mi", testing.ComponentDefName)} - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should fail if component with resource not meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=1,memory=100Gi", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - }) - - It("should succeed if component with cpu meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=1", testing.ComponentDefName)} - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should fail if component with cpu not meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=1024", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - }) - - It("should fail if component with memory not meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,memory=1Ti", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - }) - - It("should succeed if component doesn't have class definition", func() { - o.Values = []string{fmt.Sprintf("type=%s,cpu=3,memory=7Gi", testing.ExtraComponentDefName)} - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should fail if component with storage not meets the constraint", func() { - o.Values = []string{fmt.Sprintf("type=%s,storage=500Mi", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - - o.Values = []string{fmt.Sprintf("type=%s,storage=1Pi", testing.ComponentDefName)} - Expect(o.Complete()).Should(HaveOccurred()) - }) - It("should fail if create cluster by non-existed file", func() { o.SetFile = "test.yaml" Expect(o.Complete()).Should(HaveOccurred()) @@ -259,25 +192,6 @@ var _ = Describe("Cluster", func() { Run() }) - It("should succeed if create cluster by file with class", func() { - o.SetFile = testComponentWithClassPath - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should succeed if create cluster by file with resource", func() { - o.SetFile = testComponentWithResourcePath - Expect(o.Complete()).Should(Succeed()) - Expect(o.Validate()).Should(Succeed()) - Run() - }) - - It("should fail if create cluster by file with non-existed class", func() { - o.SetFile = testComponentWithInvalidClassPath - Expect(o.Complete()).Should(HaveOccurred()) - }) - It("should succeed if create cluster with a complete config file", func() { o.SetFile = testClusterPath Expect(o.Complete()).Should(Succeed()) diff --git a/pkg/cmd/cluster/create.go b/pkg/cmd/cluster/create.go index e2dfeacad..9c84ace77 100755 --- a/pkg/cmd/cluster/create.go +++ b/pkg/cmd/cluster/create.go @@ -31,7 +31,6 @@ import ( "strconv" "strings" - "github.com/apecloud/kubeblocks/pkg/controller/component" "github.com/ghodss/yaml" "github.com/robfig/cron/v3" "github.com/spf13/cobra" @@ -63,7 +62,6 @@ import ( "github.com/apecloud/kbcli/pkg/action" "github.com/apecloud/kbcli/pkg/cluster" - classutil "github.com/apecloud/kbcli/pkg/cmd/class" "github.com/apecloud/kbcli/pkg/printer" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" @@ -166,7 +164,6 @@ type setKey string const ( keyType setKey = "type" keyCPU setKey = "cpu" - keyClass setKey = "class" keyMemory setKey = "memory" keyReplicas setKey = "replicas" keyStorage setKey = "storage" @@ -293,7 +290,7 @@ func NewCreateCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. cmd.Flags().StringVar(&o.ClusterDefRef, "cluster-definition", "", "Specify cluster definition, run \"kbcli cd list\" to show all available cluster definitions") cmd.Flags().StringVar(&o.ClusterVersionRef, "cluster-version", "", "Specify cluster version, run \"kbcli cv list\" to show all available cluster versions, use the latest version if not specified") cmd.Flags().StringVarP(&o.SetFile, "set-file", "f", "", "Use yaml file, URL, or stdin to set the cluster resource") - cmd.Flags().StringArrayVar(&o.Values, "set", []string{}, "Set the cluster resource including cpu, memory, replicas and storage, each set corresponds to a component.(e.g. --set cpu=1,memory=1Gi,replicas=3,storage=20Gi or --set class=general-1c1g)") + cmd.Flags().StringArrayVar(&o.Values, "set", []string{}, "Set the cluster resource including cpu, memory, replicas and storage, each set corresponds to a component.(e.g. --set cpu=1,memory=1Gi,replicas=3,storage=20Gi)") cmd.Flags().BoolVar(&o.CreateOnlySet, "create-only-set", false, "Create components exclusively configured in 'set'") cmd.Flags().StringArrayVar(&o.Storages, "pvc", []string{}, "Set the cluster detail persistent volume claim, each '--pvc' corresponds to a component, and will override the simple configurations about storage by --set (e.g. --pvc type=mysql,name=data,mode=ReadWriteOnce,size=20Gi --pvc type=mysql,name=log,mode=ReadWriteOnce,size=1Gi)") cmd.Flags().StringArrayVar(&o.ServiceRef, "service-reference", []string{}, "Set the other KubeBlocks cluster dependencies, each '--service-reference' corresponds to a cluster service. (e.g --service-reference name=pulsarZookeeper,cluster=zookeeper,namespace=default)") @@ -589,10 +586,6 @@ func (o *CreateOptions) buildComponents(clusterCompSpecs []appsv1alpha1.ClusterC if err != nil { return nil, err } - clsMgr, resourceConstraintList, err := classutil.GetManager(o.Dynamic, o.ClusterDefRef) - if err != nil { - return nil, err - } compSets, err := buildCompSetsMap(o.Values, cd) if err != nil { @@ -613,8 +606,6 @@ func (o *CreateOptions) buildComponents(clusterCompSpecs []appsv1alpha1.ClusterC case keyCPU: comp.Resources.Requests[corev1.ResourceCPU] = setComp.Resources.Requests[corev1.ResourceCPU] comp.Resources.Limits[corev1.ResourceCPU] = setComp.Resources.Limits[corev1.ResourceCPU] - case keyClass: - comp.ClassDefRef = setComp.ClassDefRef case keyMemory: comp.Resources.Requests[corev1.ResourceMemory] = setComp.Resources.Requests[corev1.ResourceMemory] comp.Resources.Limits[corev1.ResourceMemory] = setComp.Resources.Limits[corev1.ResourceMemory] @@ -633,7 +624,7 @@ func (o *CreateOptions) buildComponents(clusterCompSpecs []appsv1alpha1.ClusterC } if clusterCompSpecs != nil { - setsCompSpecs, err := buildClusterComp(cd, compSets, clsMgr, o.MonitoringInterval, o.CreateOnlySet) + setsCompSpecs, err := buildClusterComp(cd, compSets, o.MonitoringInterval, o.CreateOnlySet) if err != nil { return nil, err } @@ -647,7 +638,7 @@ func (o *CreateOptions) buildComponents(clusterCompSpecs []appsv1alpha1.ClusterC compSpecs = append(compSpecs, &comp) } } else { - compSpecs, err = buildClusterComp(cd, compSets, clsMgr, o.MonitoringInterval, o.CreateOnlySet) + compSpecs, err = buildClusterComp(cd, compSets, o.MonitoringInterval, o.CreateOnlySet) if err != nil { return nil, err } @@ -667,11 +658,6 @@ func (o *CreateOptions) buildComponents(clusterCompSpecs []appsv1alpha1.ClusterC var comps []map[string]interface{} for _, compSpec := range compSpecs { - // validate component classes - if err = classutil.ValidateResources(clsMgr, resourceConstraintList, o.ClusterDefRef, compSpec); err != nil { - return nil, err - } - // cpu oversell if o.CPUOversellRatio > 1 { cpuRequest := compSpec.Resources.Requests[corev1.ResourceCPU] @@ -1006,7 +992,6 @@ func setEnableAllLogs(c *appsv1alpha1.Cluster, cd *appsv1alpha1.ClusterDefinitio func buildClusterComp(cd *appsv1alpha1.ClusterDefinition, setsMap map[string]map[setKey]string, - clsMgr *component.Manager, monitoringInterval uint8, createOnlySet bool) ([]*appsv1alpha1.ClusterComponentSpec, error) { // get value from set values and environment variables, the second return value is @@ -1106,33 +1091,9 @@ func buildClusterComp(cd *appsv1alpha1.ClusterDefinition, Replicas: int32(setReplicas), } - // class has higher priority than other resource related parameters - resourceList := make(corev1.ResourceList) - if clsMgr.HasClass(compObj.ComponentDefRef, component.Any) { - if className := getVal(&c, keyClass, sets); className != "" { - clsDefRef := appsv1alpha1.ClassDefRef{} - parts := strings.SplitN(className, ":", 2) - if len(parts) == 1 { - clsDefRef.Class = parts[0] - } else { - clsDefRef.Name = parts[0] - clsDefRef.Class = parts[1] - } - compObj.ClassDefRef = &clsDefRef - } else { - resourceList = corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse(getVal(&c, keyCPU, sets)), - corev1.ResourceMemory: resource.MustParse(getVal(&c, keyMemory, sets)), - } - } - } else { - if className := getVal(&c, keyClass, sets); className != "" { - return nil, fmt.Errorf("can not find class %s for component type %s", className, c.Name) - } - resourceList = corev1.ResourceList{ - corev1.ResourceCPU: resource.MustParse(getVal(&c, keyCPU, sets)), - corev1.ResourceMemory: resource.MustParse(getVal(&c, keyMemory, sets)), - } + resourceList := corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse(getVal(&c, keyCPU, sets)), + corev1.ResourceMemory: resource.MustParse(getVal(&c, keyMemory, sets)), } compObj.Resources = corev1.ResourceRequirements{ // deepcopy to make requests and limits point to different resources @@ -1621,7 +1582,6 @@ func setKeys() []string { string(keyStorage), string(keyMemory), string(keyReplicas), - string(keyClass), string(keyStorageClass), string(keySwitchPolicy), string(keyMonitor), diff --git a/pkg/cmd/cluster/create_test.go b/pkg/cmd/cluster/create_test.go index 284eec4ea..d4419afd0 100644 --- a/pkg/cmd/cluster/create_test.go +++ b/pkg/cmd/cluster/create_test.go @@ -28,7 +28,6 @@ import ( "strings" "time" - "github.com/apecloud/kubeblocks/pkg/controller/component" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -65,8 +64,6 @@ func getResource(res corev1.ResourceRequirements, name corev1.ResourceName) inte } var _ = Describe("create", func() { - var clsMgr = &component.Manager{} - Context("setEnableAllLogs Test", func() { var cluster *appsv1alpha1.Cluster var clusterDef *appsv1alpha1.ClusterDefinition @@ -140,13 +137,12 @@ var _ = Describe("create", func() { } else { Expect(*comp.VolumeClaimTemplates[0].Spec.StorageClassName).Should(Equal(storageClassName)) } - } It("build default cluster component without environment", func() { dynamic := testing.FakeDynamicClient(testing.FakeClusterDef()) cd, _ := cluster.GetClusterDefByName(dynamic, testing.ClusterDefName) - comps, err := buildClusterComp(cd, nil, clsMgr, 15, false) + comps, err := buildClusterComp(cd, nil, 15, false) Expect(err).ShouldNot(HaveOccurred()) checkComponent(comps, "20Gi", 1, "1", "1Gi", "", 0) }) @@ -158,7 +154,7 @@ var _ = Describe("create", func() { viper.Set(types.CfgKeyClusterDefaultMemory, "2Gi") dynamic := testing.FakeDynamicClient(testing.FakeClusterDef()) cd, _ := cluster.GetClusterDefByName(dynamic, testing.ClusterDefName) - comps, err := buildClusterComp(cd, nil, clsMgr, 15, false) + comps, err := buildClusterComp(cd, nil, 15, false) Expect(err).ShouldNot(HaveOccurred()) checkComponent(comps, "5Gi", 1, "2", "2Gi", "", 0) }) @@ -175,13 +171,13 @@ var _ = Describe("create", func() { keyStorageClass: "test", }, } - comps, err := buildClusterComp(cd, setsMap, clsMgr, 0, false) + comps, err := buildClusterComp(cd, setsMap, 0, false) Expect(err).Should(Succeed()) checkComponent(comps, "10Gi", 10, "10", "2Gi", "test", 0) setsMap[testing.ComponentDefName][keySwitchPolicy] = "invalid" cd.Spec.ComponentDefs[0].WorkloadType = appsv1alpha1.Replication - _, err = buildClusterComp(cd, setsMap, clsMgr, 0, false) + _, err = buildClusterComp(cd, setsMap, 0, false) Expect(err).Should(HaveOccurred()) }) @@ -203,13 +199,13 @@ var _ = Describe("create", func() { keyStorageClass: "test-other", }, } - comps, err := buildClusterComp(cd, setsMap, clsMgr, 15, false) + comps, err := buildClusterComp(cd, setsMap, 15, false) Expect(err).Should(Succeed()) checkComponent(comps, "10Gi", 10, "10", "2Gi", "test", 0) checkComponent(comps, "5Gi", 5, "5", "1Gi", "test-other", 1) setsMap[testing.ComponentDefName][keySwitchPolicy] = "invalid" cd.Spec.ComponentDefs[0].WorkloadType = appsv1alpha1.Replication - _, err = buildClusterComp(cd, setsMap, clsMgr, 15, false) + _, err = buildClusterComp(cd, setsMap, 15, false) Expect(err).Should(HaveOccurred()) }) @@ -250,14 +246,13 @@ var _ = Describe("create", func() { true, }, { - []string{"cpu=1,memory=2Gi,storage=10Gi,class=general-1c2g"}, + []string{"cpu=1,memory=2Gi,storage=10Gi"}, []string{"my-comp"}, map[string]map[setKey]string{ "my-comp": { keyCPU: "1", keyMemory: "2Gi", keyStorage: "10Gi", - keyClass: "general-1c2g", }, }, true, @@ -316,20 +311,18 @@ var _ = Describe("create", func() { true, }, { - []string{"type=comp1,cpu=1,memory=2Gi,class=general-2c4g", "type=comp2,storage=10Gi,cpu=2,class=mo-1c8g,replicas=3"}, + []string{"type=comp1,cpu=1,memory=2Gi", "type=comp2,storage=10Gi,cpu=2,replicas=3"}, []string{"comp1", "comp2"}, map[string]map[setKey]string{ "comp1": { keyType: "comp1", keyCPU: "1", keyMemory: "2Gi", - keyClass: "general-2c4g", }, "comp2": { keyType: "comp2", keyCPU: "2", keyStorage: "10Gi", - keyClass: "mo-1c8g", keyReplicas: "3", }, }, @@ -650,7 +643,7 @@ var _ = Describe("create", func() { }) It("rebuild clusterComponentSpec VolumeClaimTemplates by --pvc", func() { - comps, err := buildClusterComp(mockCD([]string{"comp1", "comp2"}), nil, clsMgr, 0, false) + comps, err := buildClusterComp(mockCD([]string{"comp1", "comp2"}), nil, 0, false) Expect(err).Should(Succeed()) Expect(comps).ShouldNot(BeNil()) diff --git a/pkg/cmd/cluster/describe_ops.go b/pkg/cmd/cluster/describe_ops.go index 4f9db51f5..f9822f0c8 100644 --- a/pkg/cmd/cluster/describe_ops.go +++ b/pkg/cmd/cluster/describe_ops.go @@ -272,18 +272,9 @@ func (o *describeOpsOptions) getVerticalScalingCommand(spec appsv1alpha1.OpsRequ for i := range componentNameSlice { commands[i] = fmt.Sprintf("kbcli cluster vscale %s --components=%s", spec.ClusterRef, strings.Join(componentNameSlice[i], ",")) - clsRef := spec.VerticalScalingList[i].ClassDefRef - if clsRef != nil { - class := clsRef.Class - if clsRef.Name != "" { - class = fmt.Sprintf("%s:%s", clsRef.Name, class) - } - commands[i] += fmt.Sprintf("--class=%s", class) - } else { - resource := resourceSlice[i].(corev1.ResourceRequirements) - commands[i] += o.addResourceFlag("cpu", resource.Limits.Cpu()) - commands[i] += o.addResourceFlag("memory", resource.Limits.Memory()) - } + resource := resourceSlice[i].(corev1.ResourceRequirements) + commands[i] += o.addResourceFlag("cpu", resource.Limits.Cpu()) + commands[i] += o.addResourceFlag("memory", resource.Limits.Memory()) } return commands } diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index d8978bae9..366a82ef4 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -49,7 +49,6 @@ import ( "github.com/apecloud/kbcli/pkg/action" "github.com/apecloud/kbcli/pkg/cluster" - classutil "github.com/apecloud/kbcli/pkg/cmd/class" "github.com/apecloud/kbcli/pkg/printer" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" @@ -79,10 +78,8 @@ type OperationsOptions struct { ClusterVersionRef string `json:"clusterVersionRef"` // VerticalScaling options - CPU string `json:"cpu"` - Memory string `json:"memory"` - Class string `json:"class"` - ClassDefRef appsv1alpha1.ClassDefRef `json:"classDefRef,omitempty"` + CPU string `json:"cpu"` + Memory string `json:"memory"` // HorizontalScaling options Replicas int `json:"replicas"` @@ -374,50 +371,28 @@ func (o *OperationsOptions) validateVolumeExpansion() error { } func (o *OperationsOptions) validateVScale(cluster *appsv1alpha1.Cluster) error { - if o.Class != "" && (o.CPU != "" || o.Memory != "") { - return fmt.Errorf("class and cpu/memory cannot be both specified") - } - if o.Class == "" && o.CPU == "" && o.Memory == "" { - return fmt.Errorf("class or cpu/memory must be specified") - } - - clsMgr, resourceConstraintList, err := classutil.GetManager(o.Dynamic, cluster.Spec.ClusterDefRef) - if err != nil { - return err + if o.CPU == "" && o.Memory == "" { + return fmt.Errorf("cpu or memory must be specified") } - fillClassParams := func(comp *appsv1alpha1.ClusterComponentSpec) error { - if o.Class != "" { - clsDefRef := appsv1alpha1.ClassDefRef{} - parts := strings.SplitN(o.Class, ":", 2) - if len(parts) == 1 { - clsDefRef.Class = parts[0] - } else { - clsDefRef.Name = parts[0] - clsDefRef.Class = parts[1] - } - comp.ClassDefRef = &clsDefRef - comp.Resources = corev1.ResourceRequirements{} - } else { - comp.ClassDefRef = &appsv1alpha1.ClassDefRef{} - requests := make(corev1.ResourceList) - if o.CPU != "" { - cpu, err := resource.ParseQuantity(o.CPU) - if err != nil { - return fmt.Errorf("cannot parse '%v', %v", o.CPU, err) - } - requests[corev1.ResourceCPU] = cpu + fillResource := func(comp *appsv1alpha1.ClusterComponentSpec) error { + requests := make(corev1.ResourceList) + if o.CPU != "" { + cpu, err := resource.ParseQuantity(o.CPU) + if err != nil { + return fmt.Errorf("cannot parse '%v', %v", o.CPU, err) } - if o.Memory != "" { - memory, err := resource.ParseQuantity(o.Memory) - if err != nil { - return fmt.Errorf("cannot parse '%v', %v", o.Memory, err) - } - requests[corev1.ResourceMemory] = memory + requests[corev1.ResourceCPU] = cpu + } + if o.Memory != "" { + memory, err := resource.ParseQuantity(o.Memory) + if err != nil { + return fmt.Errorf("cannot parse '%v', %v", o.Memory, err) } - requests.DeepCopyInto(&comp.Resources.Requests) - requests.DeepCopyInto(&comp.Resources.Limits) + requests[corev1.ResourceMemory] = memory } + requests.DeepCopyInto(&comp.Resources.Requests) + requests.DeepCopyInto(&comp.Resources.Limits) return nil } @@ -426,11 +401,7 @@ func (o *OperationsOptions) validateVScale(cluster *appsv1alpha1.Cluster) error if comp.Name != name { continue } - if err := fillClassParams(&comp); err != nil { - return err - } - // validate component classes - if err = classutil.ValidateResources(clsMgr, resourceConstraintList, cluster.Spec.ClusterDefRef, &comp); err != nil { + if err := fillResource(&comp); err != nil { return err } } @@ -732,9 +703,6 @@ func NewUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra var verticalScalingExample = templates.Examples(` # scale the computing resources of specified components, separate with commas for multiple components kbcli cluster vscale mycluster --components=mysql --cpu=500m --memory=500Mi - - # scale the computing resources of specified components by class, run command 'kbcli class list --cluster-definition cluster-definition-name' to get available classes - kbcli cluster vscale mycluster --components=mysql --class=general-2c4g `) // NewVerticalScalingCmd creates a vertical scaling command @@ -757,7 +725,6 @@ func NewVerticalScalingCmd(f cmdutil.Factory, streams genericiooptions.IOStreams o.addCommonFlags(cmd, f) cmd.Flags().StringVar(&o.CPU, "cpu", "", "Request and limit size of component cpu") cmd.Flags().StringVar(&o.Memory, "memory", "", "Request and limit size of component memory") - cmd.Flags().StringVar(&o.Class, "class", "", "Component class") cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before vertically scaling the cluster") _ = cmd.MarkFlagRequired("components") return cmd diff --git a/pkg/cmd/cluster/operations_test.go b/pkg/cmd/cluster/operations_test.go index f886b293d..39d9065ac 100644 --- a/pkg/cmd/cluster/operations_test.go +++ b/pkg/cmd/cluster/operations_test.go @@ -37,7 +37,6 @@ import ( appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" "github.com/apecloud/kubeblocks/pkg/constant" - testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" "github.com/apecloud/kbcli/pkg/testing" ) @@ -67,22 +66,6 @@ var _ = Describe("operations", func() { clusterWithOneComp.Spec.ComponentSpecs = []appsv1alpha1.ClusterComponentSpec{ clusterWithOneComp.Spec.ComponentSpecs[0], } - clusterWithOneComp.Spec.ComponentSpecs[0].ClassDefRef = &appsv1alpha1.ClassDefRef{Class: testapps.Class1c1gName} - classDef := testapps.NewComponentClassDefinitionFactory("custom", clusterWithOneComp.Spec.ClusterDefRef, testing.ComponentDefName). - AddClasses([]appsv1alpha1.ComponentClass{testapps.Class1c1g}). - GetObject() - resourceConstraint := testapps.NewComponentResourceConstraintFactory(testapps.DefaultResourceConstraintName). - AddConstraints(testapps.ProductionResourceConstraint). - AddSelector(appsv1alpha1.ClusterResourceConstraintSelector{ - ClusterDefRef: testing.ClusterDefName, - Components: []appsv1alpha1.ComponentResourceConstraintSelector{ - { - ComponentDefRef: testing.ComponentDefName, - Rules: []string{"c1"}, - }, - }, - }). - GetObject() // init cluster with one component and componentDefinition clusterWithCompDef := clusterWithOneComp.DeepCopy() @@ -115,7 +98,7 @@ var _ = Describe("operations", func() { tf.Client = &clientfake.RESTClient{} tf.FakeDynamicClient = testing.FakeDynamicClient(testing.FakeClusterDef(), testing.FakeClusterVersion(), testing.FakeCompDef(), clusterWithTwoComps, clusterWithOneComp, clusterWithCompDef, - classDef, &pods.Items[0], &pods.Items[1], &podsWithCompDef.Items[0], &podsWithCompDef.Items[1], resourceConstraint, opsDef) + &pods.Items[0], &pods.Items[1], &podsWithCompDef.Items[0], &podsWithCompDef.Items[1], opsDef) }) AfterEach(func() { @@ -231,43 +214,17 @@ var _ = Describe("operations", func() { Expect(o.CompleteComponentsFlag()).Should(Succeed()) Expect(o.ComponentNames[0]).Should(Equal(testing.ComponentName)) - By("validate invalid class") - o.Class = "class-not-exists" - in.Write([]byte(o.Name + "\n")) - Expect(o.Validate()).Should(HaveOccurred()) - - By("expect to validate success with class") - o.Class = testapps.Class1c1gName - in.Write([]byte(o.Name + "\n")) - Expect(o.Validate()).ShouldNot(HaveOccurred()) - By("validate invalid resource") - o.Class = "" - o.CPU = "100" - o.Memory = "100Gi" - in.Write([]byte(o.Name + "\n")) - Expect(o.Validate()).Should(HaveOccurred()) - - By("validate invalid resource") - o.Class = "" o.CPU = "1g" o.Memory = "100Gi" in.Write([]byte(o.Name + "\n")) Expect(o.Validate()).Should(HaveOccurred()) By("validate invalid resource") - o.Class = "" o.CPU = "1" o.Memory = "100MB" in.Write([]byte(o.Name + "\n")) Expect(o.Validate()).Should(HaveOccurred()) - - By("expect to validate success with resource") - o.Class = "" - o.CPU = "1" - o.Memory = "1Gi" - in.Write([]byte(o.Name + "\n")) - Expect(o.Validate()).ShouldNot(HaveOccurred()) }) It("Hscale Ops", func() { diff --git a/pkg/scheme/install.go b/pkg/scheme/install.go index 67fb8f1b4..31152cd91 100644 --- a/pkg/scheme/install.go +++ b/pkg/scheme/install.go @@ -28,7 +28,7 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" workloadsv1alpha1 "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1" diff --git a/pkg/testing/testdata/class.yaml b/pkg/testing/testdata/class.yaml deleted file mode 100644 index 882b18789..000000000 --- a/pkg/testing/testdata/class.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1alpha1 -kind: ComponentClassDefinition -metadata: - name: kb.classes.default.apecloud-mysql.mysql - labels: - class.kubeblocks.io/provider: kubeblocks - apps.kubeblocks.io/component-def-ref: mysql - clusterdefinition.kubeblocks.io/name: apecloud-mysql -spec: - groups: - - # class schema template, you can set default resource values here - template: | - cpu: "{{ .cpu }}" - memory: "{{ .memory }}Gi" - # class schema template variables - vars: [ cpu, memory] - series: - - # class name generator, you can reference variables in class schema template - # it's also ok to define static class name in following class definitions - namingTemplate: "general-{{ .cpu }}c{{ .memory }}g" - - # class definitions, we support two kinds of class definitions: - # 1. define arguments for class schema variables, class schema will be dynamically generated - # 2. statically define complete class schema - classes: - - args: [ "1", "1"] - - args: [ "2", "2"] - - args: [ "2", "4"] - - args: [ "2", "8"] - - args: [ "4", "16"] - - args: [ "8", "32"] - - args: [ "16", "64"] - - args: [ "32", "128"] - - args: [ "64", "256"] - - args: [ "128", "512"] - - - template: | - cpu: "{{ .cpu }}" - memory: "{{ .memory }}Gi" - vars: [ cpu, memory] - series: - - namingTemplate: "mo-{{ .cpu }}c{{ .memory }}g" - classes: - - args: [ "2", "16"] - - args: [ "4", "32"] - - args: [ "8", "64"] - - args: [ "12", "96"] - - args: [ "24", "192"] - - args: [ "48", "384"] - - args: [ "2", "32"] - - args: [ "4", "64"] - - args: [ "8", "128"] - - args: [ "16", "256"] - - args: [ "32", "512"] - - args: [ "48", "768"] - - args: [ "64", "1024"] - - args: [ "128", "2048"] \ No newline at end of file diff --git a/pkg/testing/testdata/component_with_class_1c1g.yaml b/pkg/testing/testdata/component_with_class_1c1g.yaml deleted file mode 100644 index a5adfde07..000000000 --- a/pkg/testing/testdata/component_with_class_1c1g.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- name: test - componentDefRef: mysql - monitor: true - enabledLogs: [error, slow] - replicas: 1 - classDefRef: - class: general-1c1g - volumeClaimTemplates: - - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - volumeMode: Filesystem \ No newline at end of file diff --git a/pkg/testing/testdata/component_with_invalid_class.yaml b/pkg/testing/testdata/component_with_invalid_class.yaml deleted file mode 100644 index d9f359f45..000000000 --- a/pkg/testing/testdata/component_with_invalid_class.yaml +++ /dev/null @@ -1,16 +0,0 @@ -- name: test - componentDefRef: mysql - monitor: true - enabledLogs: [error, slow] - replicas: 1 - classDefRef: - class: class-not-exists - volumeClaimTemplates: - - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - volumeMode: Filesystem \ No newline at end of file diff --git a/pkg/testing/testdata/component_with_invalid_resource.yaml b/pkg/testing/testdata/component_with_invalid_resource.yaml deleted file mode 100644 index efdc476b1..000000000 --- a/pkg/testing/testdata/component_with_invalid_resource.yaml +++ /dev/null @@ -1,18 +0,0 @@ -- name: test - componentDefRef: mysql - monitor: true - enabledLogs: [error, slow] - replicas: 1 - resources: - requests: - cpu: 3 - memory: 7Gi - volumeClaimTemplates: - - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - volumeMode: Filesystem \ No newline at end of file diff --git a/pkg/testing/testdata/component_with_resource_1c1g.yaml b/pkg/testing/testdata/component_with_resource_1c1g.yaml deleted file mode 100644 index cfdf53ae8..000000000 --- a/pkg/testing/testdata/component_with_resource_1c1g.yaml +++ /dev/null @@ -1,18 +0,0 @@ -- name: test - componentDefRef: mysql - monitor: true - enabledLogs: [error, slow] - replicas: 1 - resources: - requests: - cpu: 1 - memory: 1Gi - volumeClaimTemplates: - - name: data - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - volumeMode: Filesystem \ No newline at end of file diff --git a/pkg/testing/testdata/custom_class.yaml b/pkg/testing/testdata/custom_class.yaml deleted file mode 100644 index 3173d4181..000000000 --- a/pkg/testing/testdata/custom_class.yaml +++ /dev/null @@ -1,21 +0,0 @@ - - template: | - cpu: "{{ or .cpu 1 }}" - memory: "{{ or .memory 4 }}Gi" - vars: [cpu, memory] - series: - - namingTemplate: "custom-{{ .cpu }}c{{ .memory }}g" - classes: - - args: ["1", "1"] - - name: custom-4c16g - cpu: 4 - memory: 16Gi - - - template: | - cpu: "{{ or .cpu 1 }}" - memory: "{{ or .memory 4 }}Gi" - vars: [cpu, memory] - series: - - namingTemplate: "custom-{{ .cpu }}c{{ .memory }}g" - classes: - - args: ["2", "16"] - - args: ["4", "64"] diff --git a/pkg/testing/testdata/resource-constraint-general-without-selector.yaml b/pkg/testing/testdata/resource-constraint-general-without-selector.yaml deleted file mode 100644 index e8c0d7dd8..000000000 --- a/pkg/testing/testdata/resource-constraint-general-without-selector.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1alpha1 -kind: ComponentResourceConstraint -metadata: - name: kb-resource-constraint-general-without-selector - labels: - resourceconstraint.kubeblocks.io/provider: kubeblocks -spec: - rules: - - name: c1 - cpu: - min: 0.5 - max: 2 - step: 0.5 - memory: - sizePerCPU: 1Gi - - name: c2 - cpu: - min: 2 - max: 2 - memory: - sizePerCPU: 2Gi - - name: c3 - cpu: - slots: [2, 4, 8, 16, 24, 32, 48, 64, 96, 128] - memory: - sizePerCPU: 4Gi diff --git a/pkg/testing/testdata/resource-constraint-general.yaml b/pkg/testing/testdata/resource-constraint-general.yaml deleted file mode 100644 index fadbc67b0..000000000 --- a/pkg/testing/testdata/resource-constraint-general.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1alpha1 -kind: ComponentResourceConstraint -metadata: - name: kb-resource-constraint-general - labels: - resourceconstraint.kubeblocks.io/provider: kubeblocks -spec: - rules: - - name: c1 - cpu: - min: 0.5 - max: 2 - step: 0.5 - memory: - sizePerCPU: 1Gi - - name: c2 - cpu: - min: 2 - max: 2 - memory: - sizePerCPU: 2Gi - - name: c3 - cpu: - slots: [2, 4, 8, 16, 24, 32, 48, 64, 96, 128] - memory: - sizePerCPU: 4Gi - selector: - - clusterDefRef: apecloud-mysql - components: - - componentDefRef: mysql - rules: - - "c1" - - "c2" - - "c3" diff --git a/pkg/testing/testdata/resource-constraint-memory-optimized.yaml b/pkg/testing/testdata/resource-constraint-memory-optimized.yaml deleted file mode 100644 index 89db7f021..000000000 --- a/pkg/testing/testdata/resource-constraint-memory-optimized.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1alpha1 -kind: ComponentResourceConstraint -metadata: - name: kb-resource-constraint-memory-optimized - labels: - resourceconstraint.kubeblocks.io/provider: kubeblocks -spec: - rules: - - name: c1 - cpu: - slots: [2, 4, 8, 12, 24, 48] - memory: - sizePerCPU: 8Gi - - name: c2 - cpu: - min: 2 - max: 128 - step: 2 - memory: - sizePerCPU: 16Gi - selector: - - clusterDefRef: apecloud-mysql - components: - - componentDefRef: mysql - rules: - - "c1" - - "c2" diff --git a/pkg/types/types.go b/pkg/types/types.go index f98ac1942..b3386da07 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -155,8 +155,6 @@ const ( // Labels const ( - ClassProviderLabelKey = "class.kubeblocks.io/provider" - AddonProviderLabelKey = "addon.kubeblocks.io/provider" // ProviderLabelKey was used as the label for addon providers before version 0.8.0 ProviderLabelKey = "kubeblocks.io/provider"