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"