From edabe28d87aa4a5ae10821b4a2bcec7a21b91193 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:24:01 +0800 Subject: [PATCH 01/34] fix: fix specifying namespace is not working (#489) Co-authored-by: yipeng1030 (cherry picked from commit 9584c24c32168008e98c2b75fd3bff8f7353a733) --- .../cli/kbcli_cluster_create_apecloud-mysql.md | 5 +---- .../cli/kbcli_cluster_create_elasticsearch.md | 5 +---- docs/user_docs/cli/kbcli_cluster_create_kafka.md | 5 +---- .../cli/kbcli_cluster_create_mongodb.md | 5 +---- docs/user_docs/cli/kbcli_cluster_create_mysql.md | 7 ++----- .../cli/kbcli_cluster_create_postgresql.md | 5 +---- .../user_docs/cli/kbcli_cluster_create_qdrant.md | 5 +---- docs/user_docs/cli/kbcli_cluster_create_redis.md | 5 +---- pkg/cmd/cluster/operations.go | 16 +++++++++++++++- 9 files changed, 24 insertions(+), 34 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_create_apecloud-mysql.md b/docs/user_docs/cli/kbcli_cluster_create_apecloud-mysql.md index b8ca2c109..ffb088d5c 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_apecloud-mysql.md +++ b/docs/user_docs/cli/kbcli_cluster_create_apecloud-mysql.md @@ -21,25 +21,22 @@ kbcli cluster create apecloud-mysql NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 0.5) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for apecloud-mysql - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) --mode string Cluster topology mode. Legal values [standalone, raftGroup]. (default "standalone") --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") --proxy-enabled Enable proxy or not. - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas, for standalone mode, the replicas is 1, for raftGroup mode, the default replicas is 3. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --storage-class-name string Storage class name of the data volume - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md b/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md index 32188fa57..cdb523001 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md +++ b/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md @@ -21,23 +21,20 @@ kbcli cluster create elasticsearch NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 1) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for elasticsearch - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [1, 1000]. (default 2) --mode string Mode for ElasticSearch Legal values [single-node, multi-node]. (default "multi-node") --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. (default true) --replicas int The number of replicas, for single-node mode, the replicas is 1, for multi-node mode, the default replicas is 3. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_kafka.md b/docs/user_docs/cli/kbcli_cluster_create_kafka.md index 8d7998ea0..eeec66611 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_kafka.md +++ b/docs/user_docs/cli/kbcli_cluster_create_kafka.md @@ -21,7 +21,6 @@ kbcli cluster create kafka NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --broker-heap string Kafka broker's jvm heap setting. (default "-XshowSettings:vm -XX:MaxRAMPercentage=100 -Ddepth=64") --broker-replicas int The number of Kafka broker replicas for separated mode. Value range [1, 100]. (default 1) --controller-heap string Kafka controller's jvm heap setting for separated mode (default "-XshowSettings:vm -XX:MaxRAMPercentage=100 -Ddepth=64") @@ -31,7 +30,6 @@ kbcli cluster create kafka NAME [flags] --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") --edit Edit the API resource before creating -h, --help help for kafka - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) --meta-storage float Metadata Storage size, the unit is Gi. Value range [1, 10000]. (default 5) --meta-storage-class string The StorageClass for Kafka Metadata Storage. @@ -42,14 +40,13 @@ kbcli cluster create kafka NAME [flags] --node-port-enabled Whether NodePort service is enabled, default is false -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of Kafka broker replicas for combined mode. Legal values [1, 3, 5]. (default 1) --sasl-enable Enable authentication using SASL/PLAIN for Kafka. --storage float Data Storage size, the unit is Gi. Value range [1, 10000]. (default 10) --storage-class string The StorageClass for Kafka Data Storage. --storage-enable Enable storage for Kafka. - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_mongodb.md b/docs/user_docs/cli/kbcli_cluster_create_mongodb.md index 38e2d2bcc..93cc87672 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_mongodb.md +++ b/docs/user_docs/cli/kbcli_cluster_create_mongodb.md @@ -21,24 +21,21 @@ kbcli cluster create mongodb NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 0.5) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for mongodb - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) --mode string Cluster topology mode. Legal values [standalone, replicaset]. (default "standalone") --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas, for standalone mode, the replicas is 1, for replicaset mode, the default replicas is 3. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --storage-class-name string Storage class name of the data volume - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_mysql.md b/docs/user_docs/cli/kbcli_cluster_create_mysql.md index 34b5ad97f..d326dae8f 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_mysql.md +++ b/docs/user_docs/cli/kbcli_cluster_create_mysql.md @@ -21,27 +21,24 @@ kbcli cluster create mysql NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 1) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for mysql - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 1) --mode string Cluster topology mode. Legal values [standalone, replication, raftGroup]. (default "standalone") --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity - --version string Cluster version, run "kbcli cv list --devel" to see all versions. Legal values [mysql-8.4, mysql-8.0, mysql-5.7]. (default "mysql-8.0") + --version string Cluster version, run "kbcli cv list --devel" to see all versions. Legal values [mysql-8.4, mysql-8.0, mysql-5.7, mysql-orc-8.0, mysql-orc-5.7]. (default "mysql-8.0") ``` ### Options inherited from parent commands diff --git a/docs/user_docs/cli/kbcli_cluster_create_postgresql.md b/docs/user_docs/cli/kbcli_cluster_create_postgresql.md index 8d8ed6042..2dc70539c 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_postgresql.md +++ b/docs/user_docs/cli/kbcli_cluster_create_postgresql.md @@ -21,24 +21,21 @@ kbcli cluster create postgresql NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 0.5) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for postgresql - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) --mode string Cluster topology mode. Legal values [standalone, replication]. (default "standalone") --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas, for standalone mode, the replicas is 1, for replication mode, the default replicas is 2. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --storage-class-name string Storage class name of the data volume - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_qdrant.md b/docs/user_docs/cli/kbcli_cluster_create_qdrant.md index 49e0bfdd6..9afe4e4a0 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_qdrant.md +++ b/docs/user_docs/cli/kbcli_cluster_create_qdrant.md @@ -21,23 +21,20 @@ kbcli cluster create qdrant NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 1) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for qdrant - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 2) --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas. Value range [1, 16]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --storage-class-name string Storage class name of the data volume - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/docs/user_docs/cli/kbcli_cluster_create_redis.md b/docs/user_docs/cli/kbcli_cluster_create_redis.md index 3d3e24ee8..0526e0705 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_redis.md +++ b/docs/user_docs/cli/kbcli_cluster_create_redis.md @@ -21,20 +21,17 @@ kbcli cluster create redis NAME [flags] ### Options ``` - --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") --cpu float CPU cores. Value range [0.5, 64]. (default 0.5) --disable-exporter Enable or disable monitor. (default true) --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") --edit Edit the API resource before creating -h, --help help for redis - --host-network-accessible Specify whether the cluster can be accessed from within the VPC. --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) --mode string Cluster topology mode. Legal values [standalone, replication, cluster, replication-twemproxy]. (default "replication") --node-labels stringToString Node label selector (default []) --node-port-enabled Whether NodePort service is enabled, default is true -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --publicly-accessible Specify whether the cluster can be accessed from the public internet. --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --redis-cluster.shard-count float The number of shards in the redis cluster Value range [3, 2048]. (default 3) --replicas int The number of replicas, for standalone mode, the replicas is 1, for replication mode, the default replicas is 2. Value range [1, 5]. (default 1) @@ -45,7 +42,7 @@ kbcli cluster create redis NAME [flags] --sentinel.storage float Sentinel component storage size, the unit is Gi. Value range [1, 1024]. (default 20) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --storage-class-name string Storage class name of the data volume - --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' --topology-keys stringArray Topology keys for affinity diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index bfab81346..2d0db6577 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -38,8 +38,10 @@ import ( apiruntime "k8s.io/apimachinery/pkg/runtime" apitypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/json" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/kube-openapi/pkg/validation/spec" + "k8s.io/kubectl/pkg/cmd/testing" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" "sigs.k8s.io/controller-runtime/pkg/client" @@ -133,7 +135,7 @@ func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStre HasComponentNamesFlag: hasComponentNamesFlag, AutoApprove: false, CreateOptions: action.CreateOptions{ - Factory: f, + Factory: getFactory(f), IOStreams: streams, CueTemplateName: "cluster_operations_template.cue", GVR: types.OpsGVR(), @@ -146,6 +148,18 @@ func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStre return o } +// getFactory get a new factory when given factory isn't a TestFactory. +func getFactory(f cmdutil.Factory) cmdutil.Factory { + if factory, ok := f.(*testing.TestFactory); ok { + return factory + } else { + kubeConfigFlags := genericclioptions.NewConfigFlags(true) + matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) + newFactory := cmdutil.NewFactory(matchVersionKubeConfigFlags) + return newFactory + } +} + // addCommonFlags adds common flags for operations command func (o *OperationsOptions) addCommonFlags(cmd *cobra.Command, f cmdutil.Factory) { o.AddCommonFlags(cmd) From a8b97a0894cff358aada3113c73b02e604b40e02 Mon Sep 17 00:00:00 2001 From: apecloud-bot Date: Mon, 18 Nov 2024 09:31:12 +0000 Subject: [PATCH 02/34] chore: auto update kbcli embed chart changes --- addons | 2 +- pkg/cluster/charts/apecloud-mysql.tgz | Bin 5463 -> 5020 bytes pkg/cluster/charts/elasticsearch.tgz | Bin 4515 -> 4084 bytes pkg/cluster/charts/kafka.tgz | Bin 5979 -> 5568 bytes pkg/cluster/charts/mongodb.tgz | Bin 4905 -> 4383 bytes pkg/cluster/charts/mysql.tgz | Bin 4915 -> 4339 bytes pkg/cluster/charts/postgresql.tgz | Bin 9085 -> 8492 bytes pkg/cluster/charts/qdrant.tgz | Bin 4562 -> 3960 bytes pkg/cluster/charts/redis.tgz | Bin 5936 -> 5354 bytes 9 files changed, 1 insertion(+), 1 deletion(-) diff --git a/addons b/addons index 81459e520..e40264704 160000 --- a/addons +++ b/addons @@ -1 +1 @@ -Subproject commit 81459e520c4d796faa3ce0260d3883132df5e1d4 +Subproject commit e402647042a0ce837905ddc7f953c8daf3fa6757 diff --git a/pkg/cluster/charts/apecloud-mysql.tgz b/pkg/cluster/charts/apecloud-mysql.tgz index 1194a46699fe90f56bb91dab04dce612dddc867e..907e62be5312b182cd2cccbd989d3c424b2f1ebe 100644 GIT binary patch delta 4987 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 F007wB=}-Uw delta 5434 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 diff --git a/pkg/cluster/charts/elasticsearch.tgz b/pkg/cluster/charts/elasticsearch.tgz index 6d6bfabf80cb30cf6bd458071c6e489a4fb3d1b1..4863d8de289491ed8c2cf92d82d2b71fbbc1ba9c 100644 GIT binary patch delta 4063 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 R{{jF2|NlPRYf1oe003RJOS}L8 delta 4498 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 diff --git a/pkg/cluster/charts/kafka.tgz b/pkg/cluster/charts/kafka.tgz index 2627e2849379bab1cbbd3acea64d94f326b5b906..22a366a255962c7c5d99f50b192759c73438ec22 100644 GIT binary patch delta 5558 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 E0E<61HUIzs delta 5972 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 delta 4891 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 O0RR71g^n=*asU9qWv|2l delta 4901 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 XUD~BxT9f`a00960Tt-mU0Du4heBHq_ diff --git a/pkg/cluster/charts/postgresql.tgz b/pkg/cluster/charts/postgresql.tgz index d2813938fc936673eccc012f24dbabce9efa3e36..10211c9aaf0f4ba0dcf4ddebdb87b6b3b628a068 100644 GIT binary patch literal 8492 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 a|0#!ZD2IP8{9gb70RR8g({EY;iU0t056P+k literal 9085 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 delta 4545 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 fhCIUKy?L}R`?4?p%;ocu^0DJ%dNVE%? diff --git a/pkg/cluster/charts/redis.tgz b/pkg/cluster/charts/redis.tgz index 493a6aeebb39fb43175e862bf61b0a443b363241..0ebc6bffccf9f7c04ff4c343bafa2e9f98bf49d3 100644 GIT binary patch delta 5342 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 Date: Tue, 19 Nov 2024 17:25:18 +0800 Subject: [PATCH 03/34] fix: specifying namespace on ops cmds. (#490) (cherry picked from commit 0c1c1e371c3e35fa1984319f1a8d99e5b34f2dc8) --- pkg/cmd/cluster/operations.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index 2d0db6577..109a25735 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -135,7 +135,7 @@ func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStre HasComponentNamesFlag: hasComponentNamesFlag, AutoApprove: false, CreateOptions: action.CreateOptions{ - Factory: getFactory(f), + Factory: f, IOStreams: streams, CueTemplateName: "cluster_operations_template.cue", GVR: types.OpsGVR(), @@ -148,18 +148,6 @@ func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStre return o } -// getFactory get a new factory when given factory isn't a TestFactory. -func getFactory(f cmdutil.Factory) cmdutil.Factory { - if factory, ok := f.(*testing.TestFactory); ok { - return factory - } else { - kubeConfigFlags := genericclioptions.NewConfigFlags(true) - matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) - newFactory := cmdutil.NewFactory(matchVersionKubeConfigFlags) - return newFactory - } -} - // addCommonFlags adds common flags for operations command func (o *OperationsOptions) addCommonFlags(cmd *cobra.Command, f cmdutil.Factory) { o.AddCommonFlags(cmd) @@ -1083,8 +1071,20 @@ func buildCustomOpsExamples(t unstructured.Unstructured) string { return templates.Examples(baseCommand) } +// getTempFactory get a new factory when given factory isn't a TestFactory. +func getTempFactory(f cmdutil.Factory) cmdutil.Factory { + if factory, ok := f.(*testing.TestFactory); ok { + return factory + } else { + kubeConfigFlags := genericclioptions.NewConfigFlags(true) + matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags) + newFactory := cmdutil.NewFactory(matchVersionKubeConfigFlags) + return newFactory + } +} + func buildCustomOpsCmds(option *OperationsOptions) []*cobra.Command { - dynamic, _ := option.Factory.DynamicClient() + dynamic, _ := getTempFactory(option.Factory).DynamicClient() opsDefs, _ := dynamic.Resource(types.OpsDefinitionGVR()).List(context.TODO(), metav1.ListOptions{}) if opsDefs == nil { return nil From b27729bff2d0b3c21b97a62c5bd74d6877dccb64 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:51:05 +0800 Subject: [PATCH 04/34] chore: fix check upgrade release status (#492) (cherry picked from commit 11e98bef8b7c3f480b6cf15c7a23b58b11e7747c) --- pkg/cmd/kubeblocks/upgrade.go | 14 +----- pkg/cmd/kubeblocks/upgrade_test.go | 76 +----------------------------- pkg/util/helm/errors.go | 1 + pkg/util/helm/helm.go | 26 +++++++++- pkg/util/helm/helm_test.go | 62 +++++++++++++----------- 5 files changed, 63 insertions(+), 116 deletions(-) diff --git a/pkg/cmd/kubeblocks/upgrade.go b/pkg/cmd/kubeblocks/upgrade.go index 773c5acc6..53445a0ee 100644 --- a/pkg/cmd/kubeblocks/upgrade.go +++ b/pkg/cmd/kubeblocks/upgrade.go @@ -104,24 +104,12 @@ func (o *InstallOptions) getKBRelease() (*release.Release, error) { } o.HelmCfg.SetNamespace(ns) } - // check helm release status + // get helm release KBRelease, err := helm.GetHelmRelease(o.HelmCfg, types.KubeBlocksChartName) if err != nil { return nil, fmt.Errorf("failed to get Helm release: %v in namespace %s", err, o.Namespace) } - // intercept status of pending, unknown, uninstalling and uninstalled. - var status release.Status - if KBRelease != nil && KBRelease.Info != nil { - status = KBRelease.Info.Status - } else { - return nil, fmt.Errorf("failed to get Helm release status: release or release info is nil") - } - if status.IsPending() { - return nil, fmt.Errorf("helm release status is %s. Please wait until the release status changes to ‘deployed’ before upgrading KubeBlocks", status.String()) - } else if status != release.StatusDeployed && status != release.StatusFailed && status != release.StatusSuperseded { - return nil, fmt.Errorf("helm release status is %s. Please fix the release before upgrading KubeBlocks", status.String()) - } return KBRelease, nil } diff --git a/pkg/cmd/kubeblocks/upgrade_test.go b/pkg/cmd/kubeblocks/upgrade_test.go index 9cb012830..868651099 100644 --- a/pkg/cmd/kubeblocks/upgrade_test.go +++ b/pkg/cmd/kubeblocks/upgrade_test.go @@ -22,11 +22,10 @@ package kubeblocks import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/spf13/cobra" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/release" - - "github.com/spf13/cobra" appsv1 "k8s.io/api/apps/v1" "k8s.io/cli-runtime/pkg/genericiooptions" clientfake "k8s.io/client-go/rest/fake" @@ -175,77 +174,4 @@ var _ = Describe("kubeblocks upgrade", func() { }) }) - Context("upgrade from different status", func() { - BeforeEach(func() { - streams, _, _, _ = genericiooptions.NewTestIOStreams() - tf = cmdtesting.NewTestFactory().WithNamespace(namespace) - tf.Client = &clientfake.RESTClient{} - cfg = helm.NewFakeConfig(namespace) - actionCfg, _ = helm.NewActionConfig(cfg) - }) - - AfterEach(func() { - helm.ResetFakeActionConfig() - tf.Cleanup() - }) - - mockKubeBlocksDeploy := func() *appsv1.Deployment { - deploy := &appsv1.Deployment{} - deploy.SetLabels(map[string]string{ - "app.kubernetes.io/component": "apps", - "app.kubernetes.io/name": types.KubeBlocksChartName, - "app.kubernetes.io/version": "0.3.0", - }) - return deploy - } - It("run upgrade", func() { - testCase := []struct { - status release.Status - checkResult bool - }{ - {release.StatusDeployed, true}, - {release.StatusSuperseded, true}, - {release.StatusFailed, true}, - {release.StatusUnknown, false}, - {release.StatusUninstalled, false}, - {release.StatusUninstalling, false}, - {release.StatusPendingInstall, false}, - {release.StatusPendingUpgrade, false}, - {release.StatusPendingRollback, false}, - } - - for i := range testCase { - actionCfg, _ = helm.NewActionConfig(cfg) - err := actionCfg.Releases.Create(&release.Release{ - Name: testing.KubeBlocksChartName, - Namespace: namespace, - Version: 1, - Info: &release.Info{ - Status: testCase[i].status, - }, - Chart: &chart.Chart{}, - }) - Expect(err).Should(BeNil()) - o := &InstallOptions{ - Options: Options{ - IOStreams: streams, - HelmCfg: cfg, - Namespace: "default", - Client: testing.FakeClientSet(mockKubeBlocksDeploy()), - Dynamic: testing.FakeDynamicClient(), - }, - Version: version.DefaultKubeBlocksVersion, - Check: false, - } - if testCase[i].checkResult { - Expect(o.Upgrade()).Should(Succeed()) - } else { - Expect(o.Upgrade()).Should(HaveOccurred()) - } - helm.ResetFakeActionConfig() - } - - }) - }) - }) diff --git a/pkg/util/helm/errors.go b/pkg/util/helm/errors.go index 1da1965ea..67c92109b 100644 --- a/pkg/util/helm/errors.go +++ b/pkg/util/helm/errors.go @@ -33,6 +33,7 @@ import ( // Implementing errors should be more friendly to downstream handlers var ErrReleaseNotDeployed = fmt.Errorf("release: not in deployed status") +var ErrReleaseNotReadyForUpgrade = fmt.Errorf("release: not in deployed, failed or superseded status") func ReleaseNotFound(err error) bool { if err == nil { diff --git a/pkg/util/helm/helm.go b/pkg/util/helm/helm.go index e9b0bbbeb..f43c54317 100644 --- a/pkg/util/helm/helm.go +++ b/pkg/util/helm/helm.go @@ -154,6 +154,30 @@ func RemoveRepo(r *repo.Entry) error { return nil } +// GetInstalledForUpgrade gets helm package release info and check the status. +func (i *InstallOpts) GetInstalledForUpgrade(cfg *action.Configuration) (*release.Release, error) { + res, err := action.NewGet(cfg).Run(i.Name) + if err != nil { + return nil, err + } + if res == nil { + return nil, driver.ErrReleaseNotFound + } + // intercept status of pending, unknown, uninstalling and uninstalled. + var status release.Status + if res.Info != nil { + status = res.Info.Status + } else { + return nil, fmt.Errorf("failed to get Helm release status: release or release info is nil") + } + if status.IsPending() { + return nil, errors.Wrapf(ErrReleaseNotReadyForUpgrade, "helm release status is %s. Please wait until the release status changes to ‘deployed’ before upgrading KubeBlocks", status.String()) + } else if status != release.StatusDeployed && status != release.StatusFailed && status != release.StatusSuperseded { + return nil, errors.Wrapf(ErrReleaseNotReadyForUpgrade, "helm release status is %s. Please fix the release before upgrading KubeBlocks,uninstall and install kubeblocks could be a way to fix error", status.String()) + } + return res, nil +} + // GetInstalled gets helm package release info if installed. func (i *InstallOpts) GetInstalled(cfg *action.Configuration) (*release.Release, error) { res, err := action.NewGet(cfg).Run(i.Name) @@ -525,7 +549,7 @@ func (i *InstallOpts) Upgrade(cfg *Config) error { } func (i *InstallOpts) tryUpgrade(cfg *action.Configuration) (*release.Release, error) { - installed, err := i.GetInstalled(cfg) + installed, err := i.GetInstalledForUpgrade(cfg) if err != nil { return nil, err } diff --git a/pkg/util/helm/helm_test.go b/pkg/util/helm/helm_test.go index 42651dbe9..c3fb5dd3d 100644 --- a/pkg/util/helm/helm_test.go +++ b/pkg/util/helm/helm_test.go @@ -132,34 +132,42 @@ var _ = Describe("helm util", func() { Expect(o.Uninstall(cfg)).Should(HaveOccurred()) // release not found }) - It("should fail at fetching charts when release is already deployed", func() { - err := actionCfg.Releases.Create(&release.Release{ - Name: o.Name, - Version: 1, - Info: &release.Info{ - Status: release.StatusDeployed, - }, - Chart: &chart.Chart{}, - }) - Expect(err).Should(BeNil()) - _, err = o.tryUpgrade(actionCfg) - Expect(err).Should(HaveOccurred()) // failed at fetching charts - Expect(o.tryUninstall(actionCfg)).Should(BeNil()) // release exists - }) + It("should fail when status is not one of deployed, failed and superseded.", func() { + testCase := []struct { + status release.Status + checkResult bool + }{ + // deployed, failed and superseded + {release.StatusDeployed, true}, + {release.StatusSuperseded, true}, + {release.StatusFailed, true}, + // others + {release.StatusUnknown, false}, + {release.StatusUninstalled, false}, + {release.StatusUninstalling, false}, + {release.StatusPendingInstall, false}, + {release.StatusPendingUpgrade, false}, + {release.StatusPendingRollback, false}, + } - It("should fail when chart is already deployed", func() { - err := actionCfg.Releases.Create(&release.Release{ - Name: o.Name, - Version: 1, - Info: &release.Info{ - Status: release.StatusFailed, - }, - Chart: &chart.Chart{}, - }) - Expect(err).Should(BeNil()) - _, err = o.tryUpgrade(actionCfg) - Expect(errors.Is(err, ErrReleaseNotDeployed)).Should(BeTrue()) - Expect(o.tryUninstall(actionCfg)).Should(BeNil()) // release exists + for i := range testCase { + err := actionCfg.Releases.Create(&release.Release{ + Name: o.Name, + Version: 1, + Info: &release.Info{ + Status: testCase[i].status, + }, + Chart: &chart.Chart{}, + }) + Expect(err).Should(BeNil()) + _, err = o.tryUpgrade(actionCfg) + if testCase[i].checkResult { + Expect(errors.Is(err, ErrReleaseNotReadyForUpgrade)).Should(BeFalse()) + } else { + Expect(errors.Is(err, ErrReleaseNotReadyForUpgrade)).Should(BeTrue()) + } + Expect(o.tryUninstall(actionCfg)).Should(BeNil()) // release exists + } }) }) From f39ff7c43be9c9963a51a8bfe8d4573d63143fe3 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:07:03 +0800 Subject: [PATCH 05/34] chore: cluster stop and start support components (#495) Co-authored-by: yipeng1030 (cherry picked from commit e6282c98d3bda861a8ee0f8b5ca919a90f51fb29) --- docs/user_docs/cli/kbcli_cluster_start.md | 4 ++++ docs/user_docs/cli/kbcli_cluster_stop.md | 4 ++++ .../template/cluster_operations_template.cue | 10 ++++++++++ pkg/cmd/cluster/operations.go | 16 +++++++++++++--- pkg/cmd/cluster/register.go | 2 +- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_start.md b/docs/user_docs/cli/kbcli_cluster_start.md index c6e2ae88e..dbc15d913 100644 --- a/docs/user_docs/cli/kbcli_cluster_start.md +++ b/docs/user_docs/cli/kbcli_cluster_start.md @@ -13,11 +13,15 @@ kbcli cluster start NAME [flags] ``` # start the cluster when cluster is stopped kbcli cluster start mycluster + + # start the component of the cluster when cluster is stopped + kbcli cluster start mycluster --components=mysql ``` ### Options ``` + --components strings Component names to this operations --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") --edit Edit the API resource before creating --force skip the pre-checks of the opsRequest to run the opsRequest forcibly diff --git a/docs/user_docs/cli/kbcli_cluster_stop.md b/docs/user_docs/cli/kbcli_cluster_stop.md index a4a6f2e21..2acbb5253 100644 --- a/docs/user_docs/cli/kbcli_cluster_stop.md +++ b/docs/user_docs/cli/kbcli_cluster_stop.md @@ -13,12 +13,16 @@ kbcli cluster stop NAME [flags] ``` # stop the cluster and release all the pods of the cluster kbcli cluster stop mycluster + + # stop the component of the cluster and release all the pods of the component + kbcli cluster stop mycluster --components=mysql ``` ### Options ``` --auto-approve Skip interactive approval before stopping the cluster + --components strings Component names to this operations --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") --edit Edit the API resource before creating --force skip the pre-checks of the opsRequest to run the opsRequest forcibly diff --git a/pkg/action/template/cluster_operations_template.cue b/pkg/action/template/cluster_operations_template.cue index 57a67d8ec..e35c26acb 100644 --- a/pkg/action/template/cluster_operations_template.cue +++ b/pkg/action/template/cluster_operations_template.cue @@ -122,6 +122,16 @@ content: { type: options.type ttlSecondsAfterSucceed: options.ttlSecondsAfterSucceed force: options.force + if options.type == "Stop" { + stop: [ for _, cName in options.componentNames { + componentName: cName + }] + } + if options.type == "Start" { + start: [ for _, cName in options.componentNames { + componentName: cName + }] + } if options.type == "Upgrade" { upgrade: #upgrade } diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index 109a25735..309c639c2 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -116,6 +116,9 @@ type OperationsOptions struct { Nodes []string `json:"-"` RebuildInstanceFrom []opsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"` Env []string `json:"-"` + + // Stop and Start options + isComponentsFlagOptional bool } func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStreams, @@ -141,6 +144,7 @@ func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStre GVR: types.OpsGVR(), CustomOutPut: customOutPut, }, + isComponentsFlagOptional: opsType == opsv1alpha1.StopType || opsType == opsv1alpha1.StartType, } o.OpsTypeLower = strings.ToLower(string(o.OpsType)) @@ -339,7 +343,7 @@ func (o *OperationsOptions) Validate() error { } // common validate for componentOps - if o.HasComponentNamesFlag && len(o.ComponentNames) == 0 { + if o.HasComponentNamesFlag && !o.isComponentsFlagOptional && len(o.ComponentNames) == 0 { return fmt.Errorf(`missing components, please specify the "--components" flag for the cluster`) } if err = o.validateComponents(cluster); err != nil { @@ -861,11 +865,14 @@ func NewExposeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. var stopExample = templates.Examples(` # stop the cluster and release all the pods of the cluster kbcli cluster stop mycluster + + # stop the component of the cluster and release all the pods of the component + kbcli cluster stop mycluster --components=mysql `) // NewStopCmd creates a stop command func NewStopCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { - o := newBaseOperationsOptions(f, streams, opsv1alpha1.StopType, false) + o := newBaseOperationsOptions(f, streams, opsv1alpha1.StopType, true) cmd := &cobra.Command{ Use: "stop NAME", Short: "Stop the cluster and release all the pods of the cluster.", @@ -887,11 +894,14 @@ func NewStopCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Co var startExample = templates.Examples(` # start the cluster when cluster is stopped kbcli cluster start mycluster + + # start the component of the cluster when cluster is stopped + kbcli cluster start mycluster --components=mysql `) // NewStartCmd creates a start command func NewStartCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { - o := newBaseOperationsOptions(f, streams, opsv1alpha1.StartType, false) + o := newBaseOperationsOptions(f, streams, opsv1alpha1.StartType, true) o.AutoApprove = true cmd := &cobra.Command{ Use: "start NAME", diff --git a/pkg/cmd/cluster/register.go b/pkg/cmd/cluster/register.go index 1d605324b..9c32b291e 100644 --- a/pkg/cmd/cluster/register.go +++ b/pkg/cmd/cluster/register.go @@ -241,5 +241,5 @@ Use "kbcli cluster create {{ .ClusterType }}" to create a {{ .ClusterType }} clu _ = util.PrintGoTemplate(&builder, exampleTpl, map[string]interface{}{ "ClusterType": t.String(), }) - return templates.Examples(builder.String()) + return templates.Examples(builder.String()) + "\n" } From 660cd3fdcc70c4f50e46ab3b22c664393a9213fa Mon Sep 17 00:00:00 2001 From: wangyelei Date: Thu, 21 Nov 2024 16:33:04 +0800 Subject: [PATCH 06/34] chore: support to upgrade addon to 1.0 version (#494) (cherry picked from commit 2e1f11f278495d4072abda96bd811211eca74da4) --- pkg/cmd/addon/install.go | 11 +++++---- pkg/cmd/addon/upgrade.go | 39 +++++++++++++++++++++++++++++++ pkg/cmd/kubeblocks/install_1.0.go | 21 ++++------------- pkg/util/util.go | 12 ++++++++++ 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/pkg/cmd/addon/install.go b/pkg/cmd/addon/install.go index 83cdd359d..5200e9c0e 100644 --- a/pkg/cmd/addon/install.go +++ b/pkg/cmd/addon/install.go @@ -241,15 +241,18 @@ func (o *installOption) Validate() error { fmt.Fprint(o.Out, printer.BoldYellow("Warning: --force flag will skip version checks, which may result in the cluster not running correctly.\n")) return nil } - if o.addon.Annotations == nil || len(o.addon.Annotations[types.KBVersionValidateAnnotationKey]) == 0 { fmt.Fprint(o.Out, printer.BoldYellow(fmt.Sprintf(`Warning: The addon %s is missing annotations to validate KubeBlocks versions. It will automatically skip version checks, which may result in the cluster not running correctly. `, o.name))) - } else if ok, err = validateVersion(o.addon.Annotations[types.KBVersionValidateAnnotationKey], v.KubeBlocks); err == nil && !ok { - return fmt.Errorf("KubeBlocks version %s does not meet the requirements \"%s\" for addon installation\nUse --force option to skip this check", v.KubeBlocks, o.addon.Annotations[types.KBVersionValidateAnnotationKey]) + } else { + kbVersions := strings.Split(v.KubeBlocks, ",") + for _, kbVersion := range kbVersions { + if ok, err = validateVersion(o.addon.Annotations[types.KBVersionValidateAnnotationKey], kbVersion); err == nil && !ok { + return fmt.Errorf("KubeBlocks version %s does not meet the requirements \"%s\" for addon installation\nUse --force option to skip this check", v.KubeBlocks, o.addon.Annotations[types.KBVersionValidateAnnotationKey]) + } + } } - return err } diff --git a/pkg/cmd/addon/upgrade.go b/pkg/cmd/addon/upgrade.go index ed7d7dac9..1c038e08b 100644 --- a/pkg/cmd/addon/upgrade.go +++ b/pkg/cmd/addon/upgrade.go @@ -23,10 +23,13 @@ import ( "context" "encoding/json" "fmt" + "strings" "github.com/Masterminds/semver/v3" + "github.com/apecloud/dbctl/constant" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" ktypes "k8s.io/apimachinery/pkg/types" "k8s.io/cli-runtime/pkg/genericiooptions" cmdutil "k8s.io/kubectl/pkg/cmd/util" @@ -98,6 +101,7 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra o.name = args[0] util.CheckErr(o.Complete()) util.CheckErr(o.Validate()) + util.CheckErr(o.takeOver09AddonGlobalResources()) util.CheckErr(o.Run(f, streams)) }, } @@ -111,6 +115,41 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra cmd.Flags().StringVar(&o.path, "path", "", "specify the local path contains addon CRs and needs to be specified when operating offline") return cmd } +func (o *upgradeOption) takeOver09AddonGlobalResources() error { + kbDeploys, err := util.GetKBDeploys(o.Client, util.KubeblocksAppComponent, metav1.NamespaceAll) + if err != nil || len(kbDeploys) < 2 { + return err + } + if !strings.HasPrefix(o.version, "1.0") { + return nil + } + var newKBNamespace string + for _, v := range kbDeploys { + if strings.HasPrefix(v.Labels[constant.AppVersionLabelKey], "1.0") { + newKBNamespace = v.Namespace + break + } + } + selector := metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", constant.AppNameLabelKey, o.name), + } + takeOverRelease := func(gvr schema.GroupVersionResource) error { + cdList, err := o.Dynamic.Resource(gvr).Namespace("").List(context.TODO(), selector) + if err != nil { + return err + } + for _, v := range cdList.Items { + if err = util.SetHelmOwner(o.Dynamic, gvr, "kb-addon-"+o.name, newKBNamespace, []string{v.GetName()}); err != nil { + return err + } + } + return nil + } + if err := takeOverRelease(types.ClusterDefGVR()); err != nil { + return err + } + return takeOverRelease(types.ComponentVersionsGVR()) +} func (o *upgradeOption) Complete() error { if err := o.installOption.Complete(); err != nil { diff --git a/pkg/cmd/kubeblocks/install_1.0.go b/pkg/cmd/kubeblocks/install_1.0.go index 3f837dfe9..9730482bd 100644 --- a/pkg/cmd/kubeblocks/install_1.0.go +++ b/pkg/cmd/kubeblocks/install_1.0.go @@ -30,11 +30,9 @@ import ( "helm.sh/helm/v3/pkg/cli/values" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/apecloud/kbcli/pkg/spinner" "github.com/apecloud/kbcli/pkg/types" @@ -167,20 +165,9 @@ func (o *InstallOptions) stopDeployment(s spinner.Interface, deploy *appsv1.Depl func (o *InstallOptions) setGlobalResourcesHelmOwner() error { fmt.Fprintf(o.Out, "Change the release owner for the global resources\n") - setHelmOwner := func(gvr schema.GroupVersionResource, names []string) error { - patchOP := fmt.Sprintf(`[{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-name", "value": "%s"}`+ - `,{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-namespace", "value": "%s"}]`, types.KubeBlocksChartName, o.HelmCfg.Namespace()) - for _, name := range names { - if _, err := o.Dynamic.Resource(gvr).Namespace("").Patch(context.TODO(), name, - k8stypes.JSONPatchType, []byte(patchOP), metav1.PatchOptions{}); client.IgnoreNotFound(err) != nil { - return err - } - } - return nil - } // update ClusterRoles - if err := setHelmOwner(types.ClusterRoleGVR(), []string{ + if err := util.SetHelmOwner(o.Dynamic, types.ClusterRoleGVR(), types.KubeBlocksChartName, o.HelmCfg.Namespace(), []string{ "kubeblocks-cluster-pod-role", types.KubeBlocksChartName, fmt.Sprintf("%s-cluster-editor-role", types.KubeBlocksChartName), @@ -205,7 +192,7 @@ func (o *InstallOptions) setGlobalResourcesHelmOwner() error { return err } // update Addons - if err := setHelmOwner(types.AddonGVR(), []string{ + if err := util.SetHelmOwner(o.Dynamic, types.AddonGVR(), types.KubeBlocksChartName, o.HelmCfg.Namespace(), []string{ "apecloud-mysql", "etcd", "kafka", "llm", "mongodb", "mysql", "postgresql", "pulsar", "qdrant", "redis", "alertmanager-webhook-adaptor", @@ -216,12 +203,12 @@ func (o *InstallOptions) setGlobalResourcesHelmOwner() error { return err } // update StorageProviders - if err := setHelmOwner(types.StorageProviderGVR(), []string{ + if err := util.SetHelmOwner(o.Dynamic, types.StorageProviderGVR(), types.KubeBlocksChartName, o.HelmCfg.Namespace(), []string{ "cos", "ftp", "gcs-s3comp", "minio", "nfs", "obs", "oss", "pvc", "s3", }); err != nil { return err } // update BackupRepo - return setHelmOwner(types.BackupRepoGVR(), []string{fmt.Sprintf("%s-backuprepo", types.KubeBlocksChartName)}) + return util.SetHelmOwner(o.Dynamic, types.BackupRepoGVR(), types.KubeBlocksChartName, o.HelmCfg.Namespace(), []string{fmt.Sprintf("%s-backuprepo", types.KubeBlocksChartName)}) } diff --git a/pkg/util/util.go b/pkg/util/util.go index 4ad27ea90..62e6ef327 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1350,3 +1350,15 @@ func GetClusterNameFromArgsOrFlag(cmd *cobra.Command, args []string) string { } return "" } + +func SetHelmOwner(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, releaseName, namespace string, names []string) error { + patchOP := fmt.Sprintf(`[{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-name", "value": "%s"}`+ + `,{"op": "replace", "path": "/metadata/annotations/meta.helm.sh~1release-namespace", "value": "%s"}]`, releaseName, namespace) + for _, name := range names { + if _, err := dynamicClient.Resource(gvr).Namespace("").Patch(context.TODO(), name, + k8sapitypes.JSONPatchType, []byte(patchOP), metav1.PatchOptions{}); client.IgnoreNotFound(err) != nil { + return err + } + } + return nil +} From 749f096b3d1abc93ecd117fe4848809fc6c867ad Mon Sep 17 00:00:00 2001 From: wangyelei Date: Mon, 25 Nov 2024 10:38:53 +0800 Subject: [PATCH 07/34] feat: support etcd Add-on and tidy up docs --- Makefile | 3 +- .../kbcli_backuprepo_list-storage-provider.md | 56 +++++++++++++ docs/user_docs/cli/kbcli_cluster_create.md | 1 + .../cli/kbcli_cluster_create_minio.md | 75 ++++++++++++++++++ .../cli/kbcli_cluster_describe-restore.md | 53 +++++++++++++ .../cli/kbcli_cluster_list-backup.md | 64 +++++++++++++++ .../cli/kbcli_cluster_list-restore.md | 64 +++++++++++++++ docs/user_docs/cli/kbcli_cluster_scale-in.md | 69 ++++++++++++++++ docs/user_docs/cli/kbcli_cluster_scale-out.md | 69 ++++++++++++++++ .../cli/kbcli_componentdefinition.md | 44 ++++++++++ .../cli/kbcli_componentdefinition_describe.md | 53 +++++++++++++ .../cli/kbcli_componentdefinition_list.md | 59 ++++++++++++++ docs/user_docs/cli/kbcli_componentversion.md | 44 ++++++++++ .../cli/kbcli_componentversion_describe.md | 53 +++++++++++++ .../cli/kbcli_componentversion_list.md | 59 ++++++++++++++ .../kbcli_dataprotection_describe-restore.md | 53 +++++++++++++ ...kbcli_dataprotection_edit-backup-policy.md | 53 +++++++++++++ .../kbcli_dataprotection_list-action-set.md | 56 +++++++++++++ ...aprotection_list-backup-policy-template.md | 56 +++++++++++++ .../cli/kbcli_dataprotection_list-backup.md | 60 ++++++++++++++ .../cli/kbcli_dataprotection_list-restore.md | 57 +++++++++++++ docs/user_docs/cli/kbcli_ops-definition.md | 44 ++++++++++ .../cli/kbcli_ops-definition_describe.md | 53 +++++++++++++ .../cli/kbcli_ops-definition_list.md | 60 ++++++++++++++ pkg/cluster/charts/apecloud-mysql.tgz | Bin 5020 -> 4969 bytes pkg/cluster/charts/elasticsearch.tgz | Bin 4084 -> 4086 bytes pkg/cluster/charts/kafka.tgz | Bin 5568 -> 5565 bytes pkg/cluster/charts/mongodb.tgz | Bin 4383 -> 4387 bytes pkg/cluster/charts/mysql.tgz | Bin 4339 -> 4337 bytes pkg/cluster/charts/postgresql.tgz | Bin 8492 -> 8496 bytes pkg/cluster/charts/qdrant.tgz | Bin 3960 -> 3960 bytes 31 files changed, 1257 insertions(+), 1 deletion(-) create mode 100644 docs/user_docs/cli/kbcli_backuprepo_list-storage-provider.md create mode 100644 docs/user_docs/cli/kbcli_cluster_create_minio.md create mode 100644 docs/user_docs/cli/kbcli_cluster_describe-restore.md create mode 100644 docs/user_docs/cli/kbcli_cluster_list-backup.md create mode 100644 docs/user_docs/cli/kbcli_cluster_list-restore.md create mode 100644 docs/user_docs/cli/kbcli_cluster_scale-in.md create mode 100644 docs/user_docs/cli/kbcli_cluster_scale-out.md create mode 100644 docs/user_docs/cli/kbcli_componentdefinition.md create mode 100644 docs/user_docs/cli/kbcli_componentdefinition_describe.md create mode 100644 docs/user_docs/cli/kbcli_componentdefinition_list.md create mode 100644 docs/user_docs/cli/kbcli_componentversion.md create mode 100644 docs/user_docs/cli/kbcli_componentversion_describe.md create mode 100644 docs/user_docs/cli/kbcli_componentversion_list.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_describe-restore.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_edit-backup-policy.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_list-action-set.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_list-backup-policy-template.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_list-backup.md create mode 100644 docs/user_docs/cli/kbcli_dataprotection_list-restore.md create mode 100644 docs/user_docs/cli/kbcli_ops-definition.md create mode 100644 docs/user_docs/cli/kbcli_ops-definition_describe.md create mode 100644 docs/user_docs/cli/kbcli_ops-definition_list.md diff --git a/Makefile b/Makefile index 9fe8b2e0f..f529d268f 100644 --- a/Makefile +++ b/Makefile @@ -236,7 +236,8 @@ build-kbcli-embed-chart: helmtool fetch-addons create-kbcli-embed-charts-dir \ build-single-kbcli-embed-chart.kafka \ build-single-kbcli-embed-chart.mongodb \ build-single-kbcli-embed-chart.elasticsearch \ - build-single-kbcli-embed-chart.qdrant + build-single-kbcli-embed-chart.qdrant \ + build-single-kbcli-embed-chart.etcd .PHONY: kbcli kbcli: build-checks kbcli-fast ## Build bin/kbcli. diff --git a/docs/user_docs/cli/kbcli_backuprepo_list-storage-provider.md b/docs/user_docs/cli/kbcli_backuprepo_list-storage-provider.md new file mode 100644 index 000000000..af3e70e2a --- /dev/null +++ b/docs/user_docs/cli/kbcli_backuprepo_list-storage-provider.md @@ -0,0 +1,56 @@ +--- +title: kbcli backuprepo list-storage-provider +--- + +List storage providers. + +``` +kbcli backuprepo list-storage-provider [flags] +``` + +### Examples + +``` + # List all storage provider + kbcli backuprepo list-sp +``` + +### Options + +``` + -h, --help help for list-storage-provider + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 backuprepo](kbcli_backuprepo.md) - BackupRepo command. + +#### 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 a6092d010..d8cbc96ac 100644 --- a/docs/user_docs/cli/kbcli_cluster_create.md +++ b/docs/user_docs/cli/kbcli_cluster_create.md @@ -58,6 +58,7 @@ kbcli cluster create [NAME] [flags] * [kbcli cluster create elasticsearch](kbcli_cluster_create_elasticsearch.md) - Create a elasticsearch cluster. * [kbcli cluster create kafka](kbcli_cluster_create_kafka.md) - Create a kafka cluster. * [kbcli cluster create llm](kbcli_cluster_create_llm.md) - Create a llm cluster. +* [kbcli cluster create minio](kbcli_cluster_create_minio.md) - Create a minio cluster. * [kbcli cluster create mongodb](kbcli_cluster_create_mongodb.md) - Create a mongodb cluster. * [kbcli cluster create mysql](kbcli_cluster_create_mysql.md) - Create a mysql cluster. * [kbcli cluster create postgresql](kbcli_cluster_create_postgresql.md) - Create a postgresql cluster. diff --git a/docs/user_docs/cli/kbcli_cluster_create_minio.md b/docs/user_docs/cli/kbcli_cluster_create_minio.md new file mode 100644 index 000000000..b73fbfb33 --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_create_minio.md @@ -0,0 +1,75 @@ +--- +title: kbcli cluster create minio +--- + +Create a minio cluster. + +``` +kbcli cluster create minio NAME [flags] +``` + +### Examples + +``` + # Create a cluster with the default values + kbcli cluster create minio + + # Create a cluster with the specified cpu, memory and storage + kbcli cluster create minio --cpu 1 --memory 2 --storage 10 +``` + +### Options + +``` + --availability-policy string The availability policy of cluster. Legal values [none, node, zone]. (default "node") + --cpu float CPU cores. Value range [0.5, 64]. (default 0.5) + --disable-exporter Enable or disable monitor. (default true) + --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") + --edit Edit the API resource before creating + -h, --help help for minio + --host-network-accessible Specify whether the cluster can be accessed from within the VPC. + --memory float Memory, the unit is Gi. Value range [0.5, 1000]. (default 0.5) + --node-labels stringToString Node label selector (default []) + -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) + --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") + --publicly-accessible Specify whether the cluster can be accessed from the public internet. + --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. + --replicas int The number of replicas Value range [1, 5]. (default 1) + --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) + --storage-class-name string Storage class name of the data volume + --tenancy string The tenancy of cluster. Legal values [SharedNode, DedicatedNode]. (default "SharedNode") + --termination-policy string The termination policy of cluster. Legal values [DoNotTerminate, Halt, Delete, WipeOut]. (default "Delete") + --tolerations strings Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"' + --topology-keys stringArray Topology keys for affinity +``` + +### 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 cluster create](kbcli_cluster_create.md) - Create a cluster. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_cluster_describe-restore.md b/docs/user_docs/cli/kbcli_cluster_describe-restore.md new file mode 100644 index 000000000..047a85776 --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_describe-restore.md @@ -0,0 +1,53 @@ +--- +title: kbcli cluster describe-restore +--- + +Describe a restore + +``` +kbcli cluster describe-restore NAME [flags] +``` + +### Examples + +``` + # describe a restore + kbcli cluster describe-restore +``` + +### Options + +``` + -h, --help help for describe-restore +``` + +### 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 cluster](kbcli_cluster.md) - Cluster command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_cluster_list-backup.md b/docs/user_docs/cli/kbcli_cluster_list-backup.md new file mode 100644 index 000000000..4049d5999 --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_list-backup.md @@ -0,0 +1,64 @@ +--- +title: kbcli cluster list-backup +--- + +List backups. + +``` +kbcli cluster list-backup [flags] +``` + +### Examples + +``` + # list all backups + kbcli cluster list-backup + + # list all backups of the cluster + kbcli cluster list-backup + + # list the specified backups + kbcli cluster list-backup --names b1,b2 +``` + +### Options + +``` + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + -h, --help help for list-backup + --names strings The backup name to get the details. + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 + --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 cluster](kbcli_cluster.md) - Cluster command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_cluster_list-restore.md b/docs/user_docs/cli/kbcli_cluster_list-restore.md new file mode 100644 index 000000000..4ae2d5bde --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_list-restore.md @@ -0,0 +1,64 @@ +--- +title: kbcli cluster list-restore +--- + +List restores. + +``` +kbcli cluster list-restore [flags] +``` + +### Examples + +``` + # list all restores + kbcli cluster list-restore + + # list all restores of the cluster + kbcli cluster list-restore + + # list the specified restores + kbcli cluster list-restore --names r1,r2 +``` + +### Options + +``` + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + -h, --help help for list-restore + --names strings List restores in the specified cluster + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 + --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 cluster](kbcli_cluster.md) - Cluster command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_cluster_scale-in.md b/docs/user_docs/cli/kbcli_cluster_scale-in.md new file mode 100644 index 000000000..b6d37547e --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_scale-in.md @@ -0,0 +1,69 @@ +--- +title: kbcli cluster scale-in +--- + +scale in replicas of the specified components in the cluster. + +``` +kbcli cluster scale-in Replicas [flags] +``` + +### Examples + +``` + # scale in 2 replicas + kbcli cluster scale-in mycluster --components=mysql --replicas=2 + + # offline specified instances + kbcli cluster scale-in mycluster --components=mysql --offline-instances pod1 + + # scale in 2 replicas, one of them is specified by "--offline-instances". + kbcli cluster scale-out mycluster --components=mysql --replicas=2 --offline-instances pod1 +``` + +### Options + +``` + --auto-approve Skip interactive approval before horizontally scaling the cluster + --components strings Component names to this operations + --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") + --edit Edit the API resource before creating + --force skip the pre-checks of the opsRequest to run the opsRequest forcibly + -h, --help help for scale-in + --name string OpsRequest name. if not specified, it will be randomly generated + --offline-instances strings offline the specified instances + -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) + --replicas string Replicas with the specified components + --ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed +``` + +### 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 cluster](kbcli_cluster.md) - Cluster command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_cluster_scale-out.md b/docs/user_docs/cli/kbcli_cluster_scale-out.md new file mode 100644 index 000000000..e737c8d66 --- /dev/null +++ b/docs/user_docs/cli/kbcli_cluster_scale-out.md @@ -0,0 +1,69 @@ +--- +title: kbcli cluster scale-out +--- + +scale out replicas of the specified components in the cluster. + +``` +kbcli cluster scale-out Replicas [flags] +``` + +### Examples + +``` + # scale out 2 replicas + kbcli cluster scale-out mycluster --components=mysql --replicas=2 + + # to bring the offline instances specified in compSpec.offlineInstances online. + kbcli cluster scale-out mycluster --components=mysql --online-instances pod1 + + # scale out 2 replicas, one of which is an instance that has already been taken offline. + kbcli cluster scale-out mycluster --components=mysql --replicas=2 --online-instances pod1 +``` + +### Options + +``` + --auto-approve Skip interactive approval before horizontally scaling the cluster + --components strings Component names to this operations + --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") + --edit Edit the API resource before creating + --force skip the pre-checks of the opsRequest to run the opsRequest forcibly + -h, --help help for scale-out + --name string OpsRequest name. if not specified, it will be randomly generated + --online-instances strings online the specified instances which have been offline + -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) + --replicas string Replica changes with the specified components + --ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed +``` + +### 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 cluster](kbcli_cluster.md) - Cluster command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentdefinition.md b/docs/user_docs/cli/kbcli_componentdefinition.md new file mode 100644 index 000000000..66476b89a --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentdefinition.md @@ -0,0 +1,44 @@ +--- +title: kbcli componentdefinition +--- + +ComponentDefinition command. + +### Options + +``` + -h, --help help for componentdefinition +``` + +### 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 componentdefinition describe](kbcli_componentdefinition_describe.md) - Describe ComponentDefinition. +* [kbcli componentdefinition list](kbcli_componentdefinition_list.md) - List ComponentDefinition. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentdefinition_describe.md b/docs/user_docs/cli/kbcli_componentdefinition_describe.md new file mode 100644 index 000000000..7c191f54e --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentdefinition_describe.md @@ -0,0 +1,53 @@ +--- +title: kbcli componentdefinition describe +--- + +Describe ComponentDefinition. + +``` +kbcli componentdefinition describe [flags] +``` + +### Examples + +``` + # describe a specified component definition + kbcli componentdefinition describe mycomponentdef +``` + +### Options + +``` + -h, --help help for describe +``` + +### 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 componentdefinition](kbcli_componentdefinition.md) - ComponentDefinition command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentdefinition_list.md b/docs/user_docs/cli/kbcli_componentdefinition_list.md new file mode 100644 index 000000000..9fa47063a --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentdefinition_list.md @@ -0,0 +1,59 @@ +--- +title: kbcli componentdefinition list +--- + +List ComponentDefinition. + +``` +kbcli componentdefinition list [flags] +``` + +### Examples + +``` + # list all ComponentDefinitions + kbcli componentdefinition list + + # list all ComponentDefinitions by alias + kbcli cmpd list +``` + +### Options + +``` + -h, --help help for list + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 componentdefinition](kbcli_componentdefinition.md) - ComponentDefinition command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentversion.md b/docs/user_docs/cli/kbcli_componentversion.md new file mode 100644 index 000000000..0e9c31526 --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentversion.md @@ -0,0 +1,44 @@ +--- +title: kbcli componentversion +--- + +ComponentVersions command. + +### Options + +``` + -h, --help help for componentversion +``` + +### 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 componentversion describe](kbcli_componentversion_describe.md) - Describe ComponentVersion. +* [kbcli componentversion list](kbcli_componentversion_list.md) - List ComponentVersion. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentversion_describe.md b/docs/user_docs/cli/kbcli_componentversion_describe.md new file mode 100644 index 000000000..2e65ea6fa --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentversion_describe.md @@ -0,0 +1,53 @@ +--- +title: kbcli componentversion describe +--- + +Describe ComponentVersion. + +``` +kbcli componentversion describe [flags] +``` + +### Examples + +``` + # describe a specified componentversion + kbcli componentversion describe mycomponentversion +``` + +### Options + +``` + -h, --help help for describe +``` + +### 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 componentversion](kbcli_componentversion.md) - ComponentVersions command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_componentversion_list.md b/docs/user_docs/cli/kbcli_componentversion_list.md new file mode 100644 index 000000000..6bf3dec04 --- /dev/null +++ b/docs/user_docs/cli/kbcli_componentversion_list.md @@ -0,0 +1,59 @@ +--- +title: kbcli componentversion list +--- + +List ComponentVersion. + +``` +kbcli componentversion list [flags] +``` + +### Examples + +``` + # list all ComponentVersions + kbcli componentversion list + + # list all ComponentVersions by alias + kbcli cmpv list +``` + +### Options + +``` + -h, --help help for list + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 componentversion](kbcli_componentversion.md) - ComponentVersions command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_describe-restore.md b/docs/user_docs/cli/kbcli_dataprotection_describe-restore.md new file mode 100644 index 000000000..474db84a6 --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_describe-restore.md @@ -0,0 +1,53 @@ +--- +title: kbcli dataprotection describe-restore +--- + +Describe a restore + +``` +kbcli dataprotection describe-restore NAME [flags] +``` + +### Examples + +``` + # describe a restore + kbcli dp describe-restore +``` + +### Options + +``` + -h, --help help for describe-restore +``` + +### 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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_edit-backup-policy.md b/docs/user_docs/cli/kbcli_dataprotection_edit-backup-policy.md new file mode 100644 index 000000000..6a77bf016 --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_edit-backup-policy.md @@ -0,0 +1,53 @@ +--- +title: kbcli dataprotection edit-backup-policy +--- + +Edit backup policy + +``` +kbcli dataprotection edit-backup-policy +``` + +### Examples + +``` + # edit backup policy + kbcli dp edit-backup-policy +``` + +### Options + +``` + -h, --help help for edit-backup-policy +``` + +### 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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_list-action-set.md b/docs/user_docs/cli/kbcli_dataprotection_list-action-set.md new file mode 100644 index 000000000..24f81f0bf --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_list-action-set.md @@ -0,0 +1,56 @@ +--- +title: kbcli dataprotection list-action-set +--- + +List actionsets + +``` +kbcli dataprotection list-action-set [flags] +``` + +### Examples + +``` + # list all action sets + kbcli dp list-as +``` + +### Options + +``` + -h, --help help for list-action-set + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_list-backup-policy-template.md b/docs/user_docs/cli/kbcli_dataprotection_list-backup-policy-template.md new file mode 100644 index 000000000..6a85c92c7 --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_list-backup-policy-template.md @@ -0,0 +1,56 @@ +--- +title: kbcli dataprotection list-backup-policy-template +--- + +List backup policy template + +``` +kbcli dataprotection list-backup-policy-template [flags] +``` + +### Examples + +``` + # list all backup policy template + kbcli dp list-bpt +``` + +### Options + +``` + -h, --help help for list-backup-policy-template + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_list-backup.md b/docs/user_docs/cli/kbcli_dataprotection_list-backup.md new file mode 100644 index 000000000..ecdcc8698 --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_list-backup.md @@ -0,0 +1,60 @@ +--- +title: kbcli dataprotection list-backup +--- + +List backups. + +``` +kbcli dataprotection list-backup [flags] +``` + +### Examples + +``` + # list all backups + kbcli dp list-backup + + # list all backups of specified cluster + kbcli dp list-backup --cluster mycluster +``` + +### Options + +``` + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + --cluster string List backups in the specified cluster + -h, --help help for list-backup + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 + --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 + --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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_dataprotection_list-restore.md b/docs/user_docs/cli/kbcli_dataprotection_list-restore.md new file mode 100644 index 000000000..fdf82712c --- /dev/null +++ b/docs/user_docs/cli/kbcli_dataprotection_list-restore.md @@ -0,0 +1,57 @@ +--- +title: kbcli dataprotection list-restore +--- + +List restores. + +``` +kbcli dataprotection list-restore [flags] +``` + +### Examples + +``` + # list all restores + kbcli dp list-restore +``` + +### Options + +``` + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + --cluster string List restores in the specified cluster + -h, --help help for list-restore + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 + --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 + --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 dataprotection](kbcli_dataprotection.md) - Data protection command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_ops-definition.md b/docs/user_docs/cli/kbcli_ops-definition.md new file mode 100644 index 000000000..a7b927256 --- /dev/null +++ b/docs/user_docs/cli/kbcli_ops-definition.md @@ -0,0 +1,44 @@ +--- +title: kbcli ops-definition +--- + +ops-definitions command. + +### Options + +``` + -h, --help help for ops-definition +``` + +### 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 ops-definition describe](kbcli_ops-definition_describe.md) - Describe OpsDefinition. +* [kbcli ops-definition list](kbcli_ops-definition_list.md) - List OpsDefinition. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_ops-definition_describe.md b/docs/user_docs/cli/kbcli_ops-definition_describe.md new file mode 100644 index 000000000..f61dcc338 --- /dev/null +++ b/docs/user_docs/cli/kbcli_ops-definition_describe.md @@ -0,0 +1,53 @@ +--- +title: kbcli ops-definition describe +--- + +Describe OpsDefinition. + +``` +kbcli ops-definition describe [flags] +``` + +### Examples + +``` + # describe a specified ops-definition + kbcli ops-definition describe my-ops-definition +``` + +### Options + +``` + -h, --help help for describe +``` + +### 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 ops-definition](kbcli_ops-definition.md) - ops-definitions command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/docs/user_docs/cli/kbcli_ops-definition_list.md b/docs/user_docs/cli/kbcli_ops-definition_list.md new file mode 100644 index 000000000..831bad017 --- /dev/null +++ b/docs/user_docs/cli/kbcli_ops-definition_list.md @@ -0,0 +1,60 @@ +--- +title: kbcli ops-definition list +--- + +List OpsDefinition. + +``` +kbcli ops-definition list [flags] +``` + +### Examples + +``` + # list all ops-definitions + kbcli ops-definition list + + # list all ops-definitions by alias + kbcli ops-def list +``` + +### Options + +``` + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + -h, --help help for list + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) +``` + +### 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 + --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 ops-definition](kbcli_ops-definition.md) - ops-definitions command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/pkg/cluster/charts/apecloud-mysql.tgz b/pkg/cluster/charts/apecloud-mysql.tgz index 907e62be5312b182cd2cccbd989d3c424b2f1ebe..9d428206781c975d44922fe2e13190ed2ef1af59 100644 GIT binary patch delta 4955 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 ZPy4h_`?MkbUjP6A|NrAuP}l&1008sg)|CJN delta 5007 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 Zv`_oAPaD$z1ONd4{|GH678?MB004}w@*@BM diff --git a/pkg/cluster/charts/elasticsearch.tgz b/pkg/cluster/charts/elasticsearch.tgz index 4863d8de289491ed8c2cf92d82d2b71fbbc1ba9c..594ce6883aa2f9333079dd9a98194387169a579a 100644 GIT binary patch delta 3966 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 YP!8o#zTWbG0RRC1|7`4;^#F1J0QdF*fB*mh delta 3964 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 TH2i-600960bgnbk0CoTX@aSVj delta 3645 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 P00960FUO(t0CoTX;SXr^ diff --git a/pkg/cluster/charts/mysql.tgz b/pkg/cluster/charts/mysql.tgz index fa504b09d65f094bf0e02e0d1db33d504d923d93..f044ba8f4f43a2e4dc7549a0a006812b999ec775 100644 GIT binary patch delta 4305 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 delta 4307 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 Bs6GGy diff --git a/pkg/cluster/charts/postgresql.tgz b/pkg/cluster/charts/postgresql.tgz index 10211c9aaf0f4ba0dcf4ddebdb87b6b3b628a068..64d678123a158b3b44a81436af7e866fbe0c414b 100644 GIT binary patch delta 4559 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 delta 4561 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 diff --git a/pkg/cluster/charts/qdrant.tgz b/pkg/cluster/charts/qdrant.tgz index 2388b7d2d9fa74eda06a09e97d91d1fee670d53e..73c9412149128540465982af0922e53d7335ec2b 100644 GIT binary patch delta 3842 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 delta 3842 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 E02>v*Q~&?~ From b0f3f4623e283ffef2dacb76a7d3ca16786787f1 Mon Sep 17 00:00:00 2001 From: loomt Date: Mon, 25 Nov 2024 11:05:14 +0800 Subject: [PATCH 08/34] chore: pick etcd to release-1.0 (#497) Co-authored-by: loomts --- docs/user_docs/cli/kbcli_cluster_create.md | 2 +- pkg/cluster/builtin_charts.go | 8 ++++++++ pkg/cluster/charts/etcd.tgz | Bin 0 -> 4336 bytes 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 pkg/cluster/charts/etcd.tgz diff --git a/docs/user_docs/cli/kbcli_cluster_create.md b/docs/user_docs/cli/kbcli_cluster_create.md index d8cbc96ac..21ff6a420 100644 --- a/docs/user_docs/cli/kbcli_cluster_create.md +++ b/docs/user_docs/cli/kbcli_cluster_create.md @@ -56,9 +56,9 @@ kbcli cluster create [NAME] [flags] * [kbcli cluster](kbcli_cluster.md) - Cluster command. * [kbcli cluster create apecloud-mysql](kbcli_cluster_create_apecloud-mysql.md) - Create a apecloud-mysql cluster. * [kbcli cluster create elasticsearch](kbcli_cluster_create_elasticsearch.md) - Create a elasticsearch cluster. +* [kbcli cluster create etcd](kbcli_cluster_create_etcd.md) - Create a etcd cluster. * [kbcli cluster create kafka](kbcli_cluster_create_kafka.md) - Create a kafka cluster. * [kbcli cluster create llm](kbcli_cluster_create_llm.md) - Create a llm cluster. -* [kbcli cluster create minio](kbcli_cluster_create_minio.md) - Create a minio cluster. * [kbcli cluster create mongodb](kbcli_cluster_create_mongodb.md) - Create a mongodb cluster. * [kbcli cluster create mysql](kbcli_cluster_create_mysql.md) - Create a mysql cluster. * [kbcli cluster create postgresql](kbcli_cluster_create_postgresql.md) - Create a postgresql cluster. diff --git a/pkg/cluster/builtin_charts.go b/pkg/cluster/builtin_charts.go index 2e08ffde8..be877da3e 100644 --- a/pkg/cluster/builtin_charts.go +++ b/pkg/cluster/builtin_charts.go @@ -78,6 +78,8 @@ var ( elasticsearchChart embed.FS //go:embed charts/qdrant.tgz qdrantChart embed.FS + //go:embed charts/etcd.tgz + etcdChart embed.FS ) var builtinClusterTypes = map[ClusterType]bool{} @@ -147,6 +149,12 @@ func init() { name: "qdrant.tgz", alias: "", }, + + "etcd": { + chartFS: etcdChart, + name: "etcd.tgz", + alias: "", + }, } for k, v := range embedChartConfigs { diff --git a/pkg/cluster/charts/etcd.tgz b/pkg/cluster/charts/etcd.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8591f79e1c73cf5c1f33fda96de33bdd5e1f9ac7 GIT binary patch literal 4336 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 literal 0 HcmV?d00001 From 8f1efde85fdd29e84ae96ebc5ee53105da134c7f Mon Sep 17 00:00:00 2001 From: wangyelei Date: Mon, 25 Nov 2024 18:07:53 +0800 Subject: [PATCH 09/34] fix: upgrade kb failed because no KB deployment was found. (#498) (cherry picked from commit 8413ce3cb34ac45dfe521292c7291765d02cb7f5) --- pkg/cmd/kubeblocks/install_1.0.go | 6 ++--- pkg/cmd/kubeblocks/install_1.0_test.go | 2 +- pkg/cmd/kubeblocks/upgrade.go | 37 +++----------------------- 3 files changed, 8 insertions(+), 37 deletions(-) diff --git a/pkg/cmd/kubeblocks/install_1.0.go b/pkg/cmd/kubeblocks/install_1.0.go index 9730482bd..83e8666d2 100644 --- a/pkg/cmd/kubeblocks/install_1.0.go +++ b/pkg/cmd/kubeblocks/install_1.0.go @@ -83,7 +83,7 @@ func (o *InstallOptions) preInstallWhenUpgradeFrom09() error { kbVersion := deploy.Labels[constant.AppVersionLabelKey] o.kb09Namespace = deploy.Namespace s := spinner.New(o.Out, spinnerMsg(fmt.Sprintf("Stop %s %s", msgKey, kbVersion))) - if err = o.stopDeployment(s, deploy); err != nil { + if err = o.stopDeploymentObject(s, deploy); err != nil { return err } } @@ -128,10 +128,10 @@ func (o *InstallOptions) configKB09() error { return configKBRelease(configOpt) } -func (o *InstallOptions) stopDeployment(s spinner.Interface, deploy *appsv1.Deployment) error { +func (o *InstallOptions) stopDeploymentObject(s spinner.Interface, deploy *appsv1.Deployment) error { defer s.Fail() ctx := context.TODO() - if *deploy.Spec.Replicas != 0 { + if deploy.Spec.Replicas != nil && *deploy.Spec.Replicas != 0 { patch := []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, 0)) _, err := o.Client.AppsV1().Deployments(deploy.Namespace).Patch( ctx, diff --git a/pkg/cmd/kubeblocks/install_1.0_test.go b/pkg/cmd/kubeblocks/install_1.0_test.go index e9ab7161a..49ce6b2b5 100644 --- a/pkg/cmd/kubeblocks/install_1.0_test.go +++ b/pkg/cmd/kubeblocks/install_1.0_test.go @@ -120,7 +120,7 @@ func TestStopDeployment(t *testing.T) { } s := spinner.New(o.Out, spinnerMsg("Stop KubeBlocks Deployment")) - err := o.stopDeployment(s, deploy) + err := o.stopDeploymentObject(s, deploy) assert.NoError(t, err) updatedDeploy, err := client.AppsV1().Deployments(namespace).Get(context.TODO(), "test-deploy", metav1.GetOptions{}) diff --git a/pkg/cmd/kubeblocks/upgrade.go b/pkg/cmd/kubeblocks/upgrade.go index 53445a0ee..f8c1f2ff2 100644 --- a/pkg/cmd/kubeblocks/upgrade.go +++ b/pkg/cmd/kubeblocks/upgrade.go @@ -36,7 +36,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" apitypes "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" @@ -202,19 +201,15 @@ func (o *InstallOptions) Upgrade() error { // new version resources, which may be not compatible. helm will start the new version // KubeBlocks after upgrade. s = spinner.New(o.Out, spinnerMsg("Stop KubeBlocks "+kbVersion)) - defer s.Fail() - if err = o.deleteDeployment(util.GetKubeBlocksDeploy); err != nil { + if err = o.stopDeployment(s, util.GetKubeBlocksDeploy); err != nil { return err } - s.Success() // stop the data protection deployment s = spinner.New(o.Out, spinnerMsg("Stop DataProtection")) - defer s.Fail() - if err = o.deleteDeployment(util.GetDataProtectionDeploy); err != nil { + if err = o.stopDeployment(s, util.GetDataProtectionDeploy); err != nil { return err } - s.Success() msg = "to " + o.Version } @@ -296,7 +291,7 @@ func (o *InstallOptions) upgradeChart() error { } // deleteDeployment deletes deployment. -func (o *InstallOptions) deleteDeployment(getter deploymentGetter) error { +func (o *InstallOptions) stopDeployment(s spinner.Interface, getter deploymentGetter) error { deploy, err := getter(o.Client) if err != nil { if apierrors.IsNotFound(err) { @@ -323,31 +318,7 @@ func (o *InstallOptions) deleteDeployment(getter deploymentGetter) error { ------------------ Deployment %s end ----------------`, deploy.Name, string(bytes), deploy.Name) - if err = o.Client.AppsV1().Deployments(deploy.Namespace).Delete(context.TODO(), - deploy.Name, - metav1.DeleteOptions{ - GracePeriodSeconds: func() *int64 { - seconds := int64(0) - return &seconds - }(), - }); err != nil { - return err - } - - // wait for deployment to be deleted - return wait.PollUntilContextTimeout(context.Background(), 5*time.Second, 1*time.Minute, true, - func(_ context.Context) (bool, error) { - deploy, err = getter(o.Client) - if err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } - return false, err - } else if deploy == nil { - return true, nil - } - return false, err - }) + return o.stopDeploymentObject(s, deploy) } // keepAddons set the addons to keep when upgrade KubeBlocks avoid the addons been deleted From 1f1646de57717478f3f6b0094d64f864ac215ba8 Mon Sep 17 00:00:00 2001 From: free6om Date: Tue, 26 Nov 2024 15:56:41 +0800 Subject: [PATCH 10/34] feat: Support Reconciliation Tracing (#454) (cherry picked from commit 83f3123fd13fa43f3598de1d78336893e65e8970) --- docs/user_docs/cli/cli.md | 11 + docs/user_docs/cli/kbcli.md | 1 + go.mod | 26 +- go.sum | 83 ++- pkg/action/template/trace_template.cue | 57 ++ pkg/cmd/cli.go | 2 + pkg/cmd/cluster/describe_ops_test.go | 2 +- pkg/cmd/trace/chart/objecttree/tree_chart.go | 172 +++++ .../trace/chart/reconciliation_trace_chart.go | 389 ++++++++++++ pkg/cmd/trace/chart/richviewport/viewport.go | 119 ++++ pkg/cmd/trace/chart/summary/summary_chart.go | 111 ++++ .../chart/timeserieslinechart/options.go | 139 +++++ .../timeserieslinechart.go | 586 ++++++++++++++++++ .../timeserieslinechart/updatehandler.go | 77 +++ pkg/cmd/trace/create.go | 94 +++ pkg/cmd/trace/delete.go | 65 ++ pkg/cmd/trace/list.go | 54 ++ pkg/cmd/trace/update.go | 102 +++ pkg/cmd/trace/view.go | 42 ++ pkg/cmd/trace/watch.go | 133 ++++ pkg/types/types.go | 11 + 21 files changed, 2253 insertions(+), 23 deletions(-) create mode 100644 pkg/action/template/trace_template.cue create mode 100644 pkg/cmd/trace/chart/objecttree/tree_chart.go create mode 100644 pkg/cmd/trace/chart/reconciliation_trace_chart.go create mode 100644 pkg/cmd/trace/chart/richviewport/viewport.go create mode 100644 pkg/cmd/trace/chart/summary/summary_chart.go create mode 100644 pkg/cmd/trace/chart/timeserieslinechart/options.go create mode 100644 pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go create mode 100644 pkg/cmd/trace/chart/timeserieslinechart/updatehandler.go create mode 100644 pkg/cmd/trace/create.go create mode 100644 pkg/cmd/trace/delete.go create mode 100644 pkg/cmd/trace/list.go create mode 100644 pkg/cmd/trace/update.go create mode 100644 pkg/cmd/trace/view.go create mode 100644 pkg/cmd/trace/watch.go diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 5d04aed88..58b64aaa4 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -191,6 +191,17 @@ Report kubeblocks or cluster info. * [kbcli report kubeblocks](kbcli_report_kubeblocks.md) - Report KubeBlocks information, including deployments, events, logs, etc. +## [trace](kbcli_trace.md) + +trace management command + +* [kbcli trace create](kbcli_trace_create.md) - create a trace. +* [kbcli trace delete](kbcli_trace_delete.md) - Delete a trace. +* [kbcli trace list](kbcli_trace_list.md) - list all traces. +* [kbcli trace update](kbcli_trace_update.md) - update a trace. +* [kbcli trace watch](kbcli_trace_watch.md) - watch a trace. + + ## [version](kbcli_version.md) Print the version information, include kubernetes, KubeBlocks and kbcli version. diff --git a/docs/user_docs/cli/kbcli.md b/docs/user_docs/cli/kbcli.md index bdfb949cd..1d61d3ec2 100644 --- a/docs/user_docs/cli/kbcli.md +++ b/docs/user_docs/cli/kbcli.md @@ -68,6 +68,7 @@ kbcli [flags] * [kbcli playground](kbcli_playground.md) - Bootstrap or destroy a playground KubeBlocks in local host or cloud. * [kbcli plugin](kbcli_plugin.md) - Provides utilities for interacting with plugins. * [kbcli report](kbcli_report.md) - Report kubeblocks or cluster info. +* [kbcli trace](kbcli_trace.md) - trace management command * [kbcli version](kbcli_version.md) - Print the version information, include kubernetes, KubeBlocks and kbcli version. #### Go Back to [CLI Overview](cli.md) Homepage. diff --git a/go.mod b/go.mod index 989f92f53..eb6dabc91 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,17 @@ go 1.22.4 require ( cuelang.org/go v0.8.0 + github.com/76creates/stickers v1.4.0 github.com/Masterminds/semver/v3 v3.3.0 + github.com/NimbleMarkets/ntcharts v0.1.2 github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 - github.com/apecloud/kubeblocks v1.0.0-alpha.11 + github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/briandowns/spinner v1.23.0 github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v1.1.1 + github.com/charmbracelet/lipgloss v0.13.0 github.com/containerd/stargz-snapshotter/estargz v0.15.1 github.com/containers/common v0.60.2 github.com/docker/go-connections v0.5.0 @@ -28,6 +33,7 @@ require ( github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0 github.com/kubernetes-csi/external-snapshotter/client/v6 v6.2.0 github.com/leaanthony/debme v1.2.1 + github.com/lrstanley/bubblezone v0.0.0-20240914071701-b48c55a5e78e github.com/manifoldco/promptui v0.9.0 github.com/onsi/ginkgo/v2 v2.20.0 github.com/onsi/gomega v1.34.1 @@ -36,7 +42,7 @@ require ( github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851 github.com/replicatedhq/troubleshoot v0.57.0 github.com/robfig/cron/v3 v3.0.1 - github.com/sahilm/fuzzy v0.1.0 + github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f github.com/sethvargo/go-password v0.2.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cast v1.7.0 @@ -91,8 +97,10 @@ require ( github.com/acomagu/bufpipe v1.0.4 // indirect github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect github.com/aws/aws-sdk-go v1.50.8 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bhmj/jsonslice v1.1.2 // indirect @@ -102,6 +110,8 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/charmbracelet/keygen v0.5.1 // indirect + github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/term v0.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/clbanning/mxj/v2 v2.5.7 // indirect github.com/cloudflare/circl v1.3.7 // indirect @@ -136,8 +146,10 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/proto v1.10.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fasthttp/router v1.4.20 // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -212,11 +224,13 @@ require ( github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lithammer/dedent v1.1.0 // indirect github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect @@ -238,9 +252,12 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/montanaflynn/stats v0.6.6 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect + github.com/nsf/termbox-go v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/runc v1.1.13 // indirect @@ -264,6 +281,7 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/shirou/gopsutil/v3 v3.24.5 // indirect @@ -281,6 +299,8 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/ulikunitz/xz v0.5.12 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.50.0 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/vmware-tanzu/velero v1.13.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect diff --git a/go.sum b/go.sum index a84966892..dfe89c400 100644 --- a/go.sum +++ b/go.sum @@ -612,6 +612,8 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= +github.com/76creates/stickers v1.4.0 h1:UD1ShH0mndxzvuyO4Ho4Ct3+EB6FnTbCRecwPs/WFSo= +github.com/76creates/stickers v1.4.0/go.mod h1:OnGyCp42wnTwuZv2Ewh4dkvMuaiWMoH4I80yU2IJVmI= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -639,6 +641,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0= github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NimbleMarkets/ntcharts v0.1.2 h1:iW1aiOif/Dm74sQd18opi10RMED5589cVhy9SGp98Tw= +github.com/NimbleMarkets/ntcharts v0.1.2/go.mod h1:WcHS7kc8oQctN1543DeV9a+gOrS4DDVfKp1N9RZFUqc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= @@ -661,6 +665,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -671,8 +677,8 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys= -github.com/apecloud/kubeblocks v1.0.0-alpha.11 h1:LaOxEPJxM3wegH9j1Y2d27lXFy+XlKU8jnA+Y4OUNKU= -github.com/apecloud/kubeblocks v1.0.0-alpha.11/go.mod h1:7CCfj7PP5hW0Fkl4ZNL3LspOdTMeBm++odUUt1H8o38= +github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7 h1:7IHywp4JkW0B2MZAfFOyXPclcCOuzLw1eb6KyzAxze0= +github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7/go.mod h1:WdpL00VvZuQMW35oP+I1EoWV6qdhILK54CnfFlfyEAI= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -683,6 +689,10 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.50.8 h1:gY0WoOW+/Wz6XmYSgDH9ge3wnAevYDSQWPxxJvqAkP4= github.com/aws/aws-sdk-go v1.50.8/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -722,8 +732,8 @@ github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40 github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f h1:tRk+aBit+q3oqnj/1mF5HHhP2yxJM2lSa0afOJxQ3nE= github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -736,8 +746,20 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad h1:DVxCvjXlmkm4idu4bAbI9P+D99BsVHTKOKbzRYTlFwU= github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad/go.mod h1:Yi/tSmvDrnFgyZN4bsXm3gfXrp3zo1uytHmnPEYfquM= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= +github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI= github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= +github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= +github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= @@ -873,6 +895,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= @@ -881,6 +905,8 @@ github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0 github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fasthttp/router v1.4.20 h1:yPeNxz5WxZGojzolKqiP15DTXnxZce9Drv577GBrDgU= +github.com/fasthttp/router v1.4.20/go.mod h1:um867yNQKtERxBm+C+yzgWxjspTiQoA8z86Ec3fK/tc= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -1159,8 +1185,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1300,6 +1326,10 @@ github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e h1:hz4quJkaJWDo+xW+G6wTF6d6/95QvJ+o2D0+bB/tJ1U= github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e/go.mod h1:9z/y9glKmWEdV50tjlUPxFwi1goQfIrrsoZbnMyIZbY= github.com/longhorn/nsfilelock v0.0.0-20200723175406-fa7c83ad0003/go.mod h1:0CLeXlf59Lg6C0kjLSDf47ft73Dh37CwymYRKWwAn04= +github.com/lrstanley/bubblezone v0.0.0-20240914071701-b48c55a5e78e h1:OLwZ8xVaeVrru0xyeuOX+fne0gQTFEGlzfNjipCbxlU= +github.com/lrstanley/bubblezone v0.0.0-20240914071701-b48c55a5e78e/go.mod h1:NQ34EGeu8FAYGBMDzwhfNJL8YQYoWZP5xYJPRDAwN3E= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1323,8 +1353,11 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -1388,6 +1421,12 @@ github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v6 github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -1395,8 +1434,9 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -1550,8 +1590,10 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= +github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc= github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= @@ -1643,6 +1685,10 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1 github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -1726,21 +1772,21 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIX go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= +go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -2035,6 +2081,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2579,8 +2626,6 @@ k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= k8s.io/code-generator v0.29.2/go.mod h1:FwFi3C9jCrmbPjekhaCYcYG1n07CYiW1+PAPCockaos= k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= -k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= -k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/component-helpers v0.29.2 h1:1kTIanIdqUVG2nW3e2ENVEaYbZKphqPgEdCmJvk71aw= k8s.io/component-helpers v0.29.2/go.mod h1:gFc/p60rYtpD8UCcNfPCmbokHT2uy0yDpmr/KKUMNAw= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= diff --git a/pkg/action/template/trace_template.cue b/pkg/action/template/trace_template.cue new file mode 100644 index 000000000..c047c9fab --- /dev/null +++ b/pkg/action/template/trace_template.cue @@ -0,0 +1,57 @@ +//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 . + +// required, command line input options for parameters and flags +options: { + name: string + namespace: string + clusterName: string | *null + depth: int64 | *null + locale: string | *null + celStateEvaluationExpression: string | *null +} + +// required, k8s api resource content +content: { + apiVersion: "trace.kubeblocks.io/v1" + kind: "ReconciliationTrace" + metadata: { + name: options.name + namespace: options.namespace + } + spec: { + if options.clusterName != null { + targetObject: { + namespace: options.namespace + name: options.clusterName + } + } + if options.depth != null { + depth: options.depth + } + if options.locale != null { + locale: options.locale + } + if options.celStateEvaluationExpression != null { + stateEvaluationExpression: { + celExpression: { + expression: options.celStateEvaluationExpression + } + } + } + } +} diff --git a/pkg/cmd/cli.go b/pkg/cmd/cli.go index 6f26f84e1..e22702f57 100644 --- a/pkg/cmd/cli.go +++ b/pkg/cmd/cli.go @@ -52,6 +52,7 @@ import ( "github.com/apecloud/kbcli/pkg/cmd/playground" "github.com/apecloud/kbcli/pkg/cmd/plugin" "github.com/apecloud/kbcli/pkg/cmd/report" + "github.com/apecloud/kbcli/pkg/cmd/trace" "github.com/apecloud/kbcli/pkg/cmd/version" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" @@ -181,6 +182,7 @@ A Command Line Interface for KubeBlocks`, report.NewReportCmd(f, ioStreams), backuprepo.NewBackupRepoCmd(f, ioStreams), dataprotection.NewDataProtectionCmd(f, ioStreams), + trace.NewTraceCmd(f, ioStreams), ) filters := []string{"options"} diff --git a/pkg/cmd/cluster/describe_ops_test.go b/pkg/cmd/cluster/describe_ops_test.go index 809efa557..fde61641d 100644 --- a/pkg/cmd/cluster/describe_ops_test.go +++ b/pkg/cmd/cluster/describe_ops_test.go @@ -138,7 +138,7 @@ var _ = Describe("Expose", func() { Phase: opsv1alpha1.OpsFailedPhase, Components: map[string]opsv1alpha1.OpsRequestComponentStatus{ componentName: { - Phase: appsv1.FailedClusterCompPhase, + Phase: appsv1.FailedComponentPhase, ProgressDetails: []opsv1alpha1.ProgressStatusDetail{ { ObjectKey: objectKey, diff --git a/pkg/cmd/trace/chart/objecttree/tree_chart.go b/pkg/cmd/trace/chart/objecttree/tree_chart.go new file mode 100644 index 000000000..d2e181f4f --- /dev/null +++ b/pkg/cmd/trace/chart/objecttree/tree_chart.go @@ -0,0 +1,172 @@ +/* +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 objecttree + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/tree" + zone "github.com/lrstanley/bubblezone" + corev1 "k8s.io/api/core/v1" + + tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" +) + +var ( + enumeratorStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("63")). // purple + MarginRight(1) + + workloadStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("35")) + + noneWorkloadStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("212")) + + selectedBackgroundColor = lipgloss.Color("4") +) + +type Model struct { + tree *tree.Tree + zoneID string + zoneManager *zone.Manager + + data *tracev1.ObjectTreeNode + + selected *int +} + +func (m *Model) Init() tea.Cmd { + return nil +} + +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if mm, ok := msg.(tea.MouseMsg); ok && mm.Action == tea.MouseActionPress { + if m.zoneManager == nil { + return m, nil + } + if m.zoneManager.Get(m.zoneID).InBounds(mm) { + m.setSelectedObject(mm) + } + } + return m, nil +} + +func (m *Model) View() string { + m.tree = buildObjectTree(m.data) + if m.tree == nil { + return "" + } + + depthObjectMap := make(map[int]string) + buildDepth2ObjectMap(m.data, 0, depthObjectMap) + + setBackgroundColor := func(style lipgloss.Style, nodeValue string) lipgloss.Style { + if m.selected != nil { + if obj, ok := depthObjectMap[*m.selected]; ok && nodeValue == obj { + return style.Background(selectedBackgroundColor) + } + } + return style + } + + rootStyle := setBackgroundColor(workloadStyle, m.tree.Value()) + m.tree.RootStyle(rootStyle) + m.tree.EnumeratorStyle(enumeratorStyle) + + isWorkload := func(node string) bool { + if strings.Contains(node, "Cluster") || + strings.Contains(node, "Component") || + strings.Contains(node, "InstanceSet") || + strings.Contains(node, "Pod") { + return true + } + return false + } + + m.tree.ItemStyleFunc(func(children tree.Children, i int) lipgloss.Style { + style := noneWorkloadStyle + if isWorkload(children.At(i).Value()) { + style = workloadStyle + } + return setBackgroundColor(style, children.At(i).Value()) + }) + + return m.zoneManager.Mark(m.zoneID, m.tree.String()) +} + +func (m *Model) SetData(data *tracev1.ObjectTreeNode) { + m.data = data +} + +func (m *Model) GetSelected() int { + if m.selected == nil { + return -1 + } + return *m.selected +} + +func (m *Model) setSelectedObject(mm tea.MouseMsg) { + _, y := m.zoneManager.Get(m.zoneID).Pos(mm) + if y >= 0 { + m.selected = &y + } +} + +func buildDepth2ObjectMap(objectTree *tracev1.ObjectTreeNode, depth int, depthObjectMap map[int]string) int { + if objectTree == nil { + return depth + } + obj := formatNode(&objectTree.Primary) + depthObjectMap[depth] = obj + for _, secondary := range objectTree.Secondaries { + depth = buildDepth2ObjectMap(secondary, depth+1, depthObjectMap) + } + return depth +} + +func formatNode(reference *corev1.ObjectReference) string { + return fmt.Sprintf("%s/%s", reference.Kind, reference.Name) +} + +func buildObjectTree(objectTree *tracev1.ObjectTreeNode) *tree.Tree { + if objectTree == nil { + return nil + } + + treeNode := tree.New() + treeNode.Root(formatNode(&objectTree.Primary)) + for _, secondary := range objectTree.Secondaries { + child := buildObjectTree(secondary) + treeNode.Child(child) + } + return treeNode +} + +func NewTree(data *tracev1.ObjectTreeNode, manager *zone.Manager) *Model { + return &Model{ + data: data, + zoneManager: manager, + zoneID: manager.NewPrefix(), + } +} diff --git a/pkg/cmd/trace/chart/reconciliation_trace_chart.go b/pkg/cmd/trace/chart/reconciliation_trace_chart.go new file mode 100644 index 000000000..da458d9ed --- /dev/null +++ b/pkg/cmd/trace/chart/reconciliation_trace_chart.go @@ -0,0 +1,389 @@ +/* +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 chart + +import ( + "fmt" + + "github.com/76creates/stickers/flexbox" + "github.com/NimbleMarkets/ntcharts/barchart" + "github.com/NimbleMarkets/ntcharts/canvas" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + + "github.com/apecloud/kbcli/pkg/cmd/trace/chart/objecttree" + "github.com/apecloud/kbcli/pkg/cmd/trace/chart/richviewport" + "github.com/apecloud/kbcli/pkg/cmd/trace/chart/summary" + "github.com/apecloud/kbcli/pkg/cmd/trace/chart/timeserieslinechart" + tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" +) + +var ( + axisStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("3")) // yellow + + labelStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("6")) // cyan + + totalBlockStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("6")) // cyan + + addedBlockStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("2")) // green + + updatedBlockStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("3")) // yellow + + deletedBlockStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("9")) // red + + eventBlockStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("6")) // cyan + + columnStyle = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder(), true, true, false, true). + BorderForeground(lipgloss.Color("#2bcbba")) + + changesLineStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("4")) // blue + + summaryNLatestChangeSeparator = " " +) + +type TraceUpdateMsg struct { + Trace *tracev1.ReconciliationTrace +} + +// Model defines the BubbleTea Model of the ReconciliationTrace. +type Model struct { + // base framework + base *flexbox.HorizontalFlexBox + + // summary + summary *summary.Model + + // latest change + latestChange *richviewport.Model + + // current object tree + objectTree *objecttree.Model + + // changes + changes *timeserieslinechart.Model + + zoneManager *zone.Manager + + trace *tracev1.ReconciliationTrace + selectedChange *tracev1.ObjectChange +} + +func (m *Model) Init() tea.Cmd { + m.zoneManager = zone.New() + + m.base = flexbox.NewHorizontal(0, 0) + columns := []*flexbox.Column{ + m.base.NewColumn().AddCells( + flexbox.NewCell(1, 1).SetStyle(columnStyle), + flexbox.NewCell(1, 2).SetStyle(columnStyle), + ), + } + m.base.AddColumns(columns) + + m.objectTree = objecttree.NewTree(nil, m.zoneManager) + + return nil +} + +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q", "ctrl+c": + return m, tea.Quit + } + case tea.MouseMsg: + if msg.Action == tea.MouseActionPress { + if m.summary != nil { + m.summary.Update(msg) + } + if m.objectTree != nil { + m.objectTree.Update(msg) + } + if m.changes != nil { + if m.zoneManager.Get(m.changes.ZoneID()).InBounds(msg) { + m.setSelectedChange(msg) + } + } + } + case tea.WindowSizeMsg: + m.base.SetWidth(msg.Width) + m.base.SetHeight(msg.Height) + m.base.ForceRecalculate() + case TraceUpdateMsg: + m.trace = msg.Trace + } + return m, nil +} + +func (m *Model) View() string { + m.updateSummaryView() + m.updateLatestChangeView() + m.updateObjectTreeView() + m.updateChangesView() + + m.updateStatusBarView() + m.updateMainContentView() + + return m.zoneManager.Scan(m.base.Render()) +} + +func (m *Model) updateSummaryView() { + if m.trace == nil { + return + } + dataSet := buildSummaryDataSet(&m.trace.Status.CurrentState.Summary) + if len(dataSet) == 0 { + return + } + m.summary = summary.New(m.base.GetColumn(0).GetCell(0).GetHeight(), + dataSet, + barchart.WithZoneManager(m.zoneManager), + barchart.WithStyles(axisStyle, labelStyle)) +} + +func (m *Model) updateLatestChangeView() { + if m.trace == nil { + return + } + if m.summary == nil { + return + } + formatChange := func(change *tracev1.ObjectChange) string { + desc := change.Description + if change.LocalDescription != nil { + desc = *change.LocalDescription + } + name := types.NamespacedName{Namespace: change.ObjectReference.Namespace, Name: change.ObjectReference.Name}.String() + return fmt.Sprintf("GVK: %s/%s\nObject: %s\nDescription: %s", change.ObjectReference.GroupVersionKind().GroupVersion(), change.ObjectReference.Kind, name, desc) + } + changeText := "" + if l := len(m.trace.Status.CurrentState.Changes); l > 0 { + changeText = formatChange(&m.trace.Status.CurrentState.Changes[l-1]) + } + if m.selectedChange != nil { + changeText = formatChange(m.selectedChange) + } + w := lipgloss.Width(m.summary.View()) + baseBorder := 1 + m.latestChange = richviewport.NewViewPort( + m.base.GetWidth()-2*baseBorder-w-len(summaryNLatestChangeSeparator), + m.base.GetColumn(0).GetCell(0).GetHeight()-baseBorder, + "Latest Change", + changeText) +} + +func (m *Model) updateObjectTreeView() { + if m.trace == nil { + return + } + m.objectTree.SetData(m.trace.Status.CurrentState.ObjectTree) +} + +func (m *Model) updateChangesView() { + if m.trace == nil { + return + } + if m.objectTree == nil { + return + } + + depthMap := make(map[corev1.ObjectReference]float64) + depth := buildDepthMap(m.trace.Status.CurrentState.ObjectTree, 0, depthMap) + minYValue := 0.0 + maxYValue := float64(len(depthMap)) + w, h := lipgloss.Size(m.objectTree.View()) + changesChart := timeserieslinechart.New(m.base.GetWidth()-2-w, h+2, timeserieslinechart.WithZoneManager(m.zoneManager)) + changesChart.AxisStyle = axisStyle + changesChart.LabelStyle = labelStyle + changesChart.XLabelFormatter = timeserieslinechart.HourTimeLabelFormatter() + changesChart.YLabelFormatter = func(i int, f float64) string { + return "" + } + changesChart.UpdateHandler = timeserieslinechart.SecondUpdateHandler(1) + changesChart.SetYRange(minYValue, maxYValue) + changesChart.SetViewYRange(minYValue, maxYValue) + changesChart.SetStyle(changesLineStyle) + m.changes = &changesChart + if len(m.trace.Status.CurrentState.Changes) > 0 { + change := m.trace.Status.CurrentState.Changes[0] + minX := change.Timestamp.Time.Unix() + maxX := minX + 1 + m.changes.SetViewXRange(float64(minX), float64(maxX)) + m.changes.SetXRange(float64(minX), float64(maxX)) + } + for _, change := range m.trace.Status.CurrentState.Changes { + objRef := normalizeObjectRef(&change.ObjectReference) + m.changes.Push(timeserieslinechart.TimePoint{Time: change.Timestamp.Time, Value: depth - depthMap[*objRef] + 1}) + } + m.changes.SetDataSetStyleFunc(func(tp *timeserieslinechart.TimePoint) lipgloss.Style { + change := m.findSelectedChange(tp) + if change == nil { + return lipgloss.NewStyle() + } + switch change.ChangeType { + case tracev1.ObjectCreationType: + return addedBlockStyle + case tracev1.ObjectUpdateType: + return updatedBlockStyle + case tracev1.ObjectDeletionType: + return deletedBlockStyle + case tracev1.EventType: + return eventBlockStyle + } + return lipgloss.NewStyle() + }) + m.changes.DrawRect() + m.changes.HighlightLine(m.objectTree.GetSelected(), lipgloss.Color("4")) +} + +func (m *Model) updateStatusBarView() { + if m.trace == nil { + return + } + if m.summary == nil { + return + } + if m.latestChange == nil { + return + } + summary := m.summary.View() + latestChange := m.latestChange.View() + status := lipgloss.JoinHorizontal(lipgloss.Left, summary, summaryNLatestChangeSeparator, latestChange) + m.base.GetColumn(0).GetCell(0).SetContent(status) +} + +func (m *Model) updateMainContentView() { + if m.trace == nil { + return + } + if m.objectTree == nil { + return + } + if m.changes == nil { + return + } + objectTree := m.objectTree.View() + changes := m.changes.View() + mainContent := lipgloss.JoinHorizontal(lipgloss.Left, objectTree, changes) + m.base.GetColumn(0).GetCell(1).SetContent(mainContent) +} + +func (m *Model) setSelectedChange(msg tea.MouseMsg) { + m.selectedChange = nil + x, y := m.zoneManager.Get(m.changes.ZoneID()).Pos(msg) + point := m.changes.TimePointFromPoint(canvas.Point{X: x, Y: y}) + if point == nil { + return + } + change := m.findSelectedChange(point) + if change != nil { + m.selectedChange = change + } +} + +func (m *Model) findSelectedChange(point *timeserieslinechart.TimePoint) *tracev1.ObjectChange { + depthMap := make(map[corev1.ObjectReference]float64) + depth := buildDepthMap(m.trace.Status.CurrentState.ObjectTree, 0, depthMap) + for i := range m.trace.Status.CurrentState.Changes { + change := &m.trace.Status.CurrentState.Changes[i] + if change.Timestamp.Time.Unix() != point.Time.Unix() { + continue + } + objRef := normalizeObjectRef(&change.ObjectReference) + if depthMap[*objRef] == (depth + 1 - point.Value) { + return change + } + } + return nil +} + +func buildSummaryDataSet(summary *tracev1.ObjectTreeDiffSummary) []barchart.BarData { + var dataSet []barchart.BarData + for i := range summary.ObjectSummaries { + n := normalizeObjectSummary(&summary.ObjectSummaries[i]) + d := barchart.BarData{ + Label: n.ObjectType.Kind, + Values: []barchart.BarValue{ + {Name: "Total", Value: float64(n.Total), Style: totalBlockStyle}, + {Name: "Added", Value: float64(*n.ChangeSummary.Added), Style: addedBlockStyle}, + {Name: "Updated", Value: float64(*n.ChangeSummary.Updated), Style: updatedBlockStyle}, + {Name: "Deleted", Value: float64(*n.ChangeSummary.Deleted), Style: deletedBlockStyle}, + }, + } + dataSet = append(dataSet, d) + } + return dataSet +} + +func normalizeObjectRef(ref *corev1.ObjectReference) *corev1.ObjectReference { + objRef := *ref + objRef.UID = "" + objRef.ResourceVersion = "" + return &objRef +} + +func normalizeObjectSummary(s *tracev1.ObjectSummary) *tracev1.ObjectSummary { + if s == nil { + return nil + } + if s.ChangeSummary == nil { + s.ChangeSummary = &tracev1.ObjectChangeSummary{} + } + if s.ChangeSummary.Added == nil { + s.ChangeSummary.Added = pointer.Int32(0) + } + if s.ChangeSummary.Updated == nil { + s.ChangeSummary.Updated = pointer.Int32(0) + } + if s.ChangeSummary.Deleted == nil { + s.ChangeSummary.Deleted = pointer.Int32(0) + } + return s +} + +func buildDepthMap(objectTree *tracev1.ObjectTreeNode, depth float64, depthMap map[corev1.ObjectReference]float64) float64 { + if objectTree == nil { + return depth + } + objRef := normalizeObjectRef(&objectTree.Primary) + depthMap[*objRef] = depth + for _, secondary := range objectTree.Secondaries { + depth = buildDepthMap(secondary, depth+1, depthMap) + } + return depth +} + +func NewReconciliationTraceChart() *Model { + return &Model{} +} diff --git a/pkg/cmd/trace/chart/richviewport/viewport.go b/pkg/cmd/trace/chart/richviewport/viewport.go new file mode 100644 index 000000000..dc89ec217 --- /dev/null +++ b/pkg/cmd/trace/chart/richviewport/viewport.go @@ -0,0 +1,119 @@ +/* +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 richviewport + +import ( + "fmt" + "strings" + + "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +var ( + borderColor = lipgloss.Color("63") // purple + + borderStyle = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder(), false, true). + BorderForeground(borderColor) + + lineStyle = lipgloss.NewStyle(). + Foreground(borderColor) + + titleStyle = func() lipgloss.Style { + b := lipgloss.NormalBorder() + b.Right = "├" + return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1). + BorderForeground(borderColor) + }() + + infoStyle = func() lipgloss.Style { + b := lipgloss.NormalBorder() + b.Left = "┤" + return titleStyle.BorderStyle(b). + BorderForeground(borderColor) + }() +) + +type Model struct { + header string + content string + width int + height int + viewport viewport.Model +} + +func (m *Model) Init() tea.Cmd { + return nil +} + +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + var ( + cmd tea.Cmd + cmds []tea.Cmd + ) + m.viewport, cmd = m.viewport.Update(msg) + cmds = append(cmds, cmd) + + return m, tea.Batch(cmds...) +} + +func (m *Model) View() string { + return lipgloss.JoinVertical(lipgloss.Top, + m.headerView(), + borderStyle.Render(m.viewport.View()), + m.footerView()) +} + +func (m *Model) headerView() string { + title := titleStyle.Render(m.header) + line := strings.Repeat("─", max(0, m.width-lipgloss.Width(title)-1)) + return lipgloss.JoinHorizontal(lipgloss.Center, title, lineStyle.Render(line), lineStyle.Render(" \n┐\n│")) +} + +func (m *Model) footerView() string { + info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100)) + line := strings.Repeat("─", max(0, m.width-lipgloss.Width(info)-1)) + return lipgloss.JoinHorizontal(lipgloss.Center, lineStyle.Render("│\n└\n "), lineStyle.Render(line), info) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func NewViewPort(w, h int, header, content string) *Model { + m := &Model{ + header: header, + content: content, + width: w, + height: h, + } + headerHeight := lipgloss.Height(m.headerView()) + footerHeight := lipgloss.Height(m.footerView()) + verticalMarginHeight := headerHeight + footerHeight + m.viewport = viewport.New(m.width-2, m.height-verticalMarginHeight) + m.viewport.SetContent(m.content) + m.viewport.YPosition = headerHeight + 1 + return m +} diff --git a/pkg/cmd/trace/chart/summary/summary_chart.go b/pkg/cmd/trace/chart/summary/summary_chart.go new file mode 100644 index 000000000..48b984eeb --- /dev/null +++ b/pkg/cmd/trace/chart/summary/summary_chart.go @@ -0,0 +1,111 @@ +/* +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 summary + +import ( + "fmt" + + "github.com/NimbleMarkets/ntcharts/barchart" + "github.com/NimbleMarkets/ntcharts/canvas" + "github.com/NimbleMarkets/ntcharts/canvas/runes" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +var borderStyle = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("6")) // cyan + +var selectedBarData barchart.BarData + +type Model struct { + summary barchart.Model + data []barchart.BarData +} + +func (m *Model) Init() tea.Cmd { + return nil +} + +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if mm, ok := msg.(tea.MouseMsg); ok && mm.Action == tea.MouseActionPress { + if m.summary.ZoneManager() == nil { + return m, nil + } + if m.summary.ZoneManager().Get(m.summary.ZoneID()).InBounds(mm) { + m.setBarData(&m.summary, mm) + } + } + return m, nil +} + +func (m *Model) View() string { + m.summary.Draw() + return lipgloss.JoinHorizontal(lipgloss.Top, + m.summary.View(), + borderStyle.Render(totals(m.data)), + borderStyle.Render(legend(m.data[0])), + borderStyle.Render(selectedData()), + ) +} + +func (m *Model) setBarData(b *barchart.Model, msg tea.MouseMsg) { + x, y := m.summary.ZoneManager().Get(b.ZoneID()).Pos(msg) + selectedBarData = b.BarDataFromPoint(canvas.Point{x, y}) +} + +func legend(bd barchart.BarData) (r string) { + r = "Legend\n" + for _, bv := range bd.Values { + r += "\n" + bv.Style.Render(fmt.Sprintf("%c %s", runes.FullBlock, bv.Name)) + } + return +} +func totals(lv []barchart.BarData) (r string) { + r = "Totals\n" + for _, bd := range lv { + sum := bd.Values[0].Value + r += "\n" + fmt.Sprintf("%s %d", bd.Label, int(sum)) + } + return +} + +func selectedData() (r string) { + r = "Selected\n" + if len(selectedBarData.Values) == 0 { + return + } + r += selectedBarData.Label + for _, bv := range selectedBarData.Values { + r += " " + bv.Style.Render(fmt.Sprintf("%d", int(bv.Value))) + } + return +} + +func New(h int, dataSet []barchart.BarData, opts ...barchart.Option) *Model { + gap := 1 + barWidth := 4 + opts = append(opts, barchart.WithDataSet(dataSet), barchart.WithBarWidth(barWidth), barchart.WithBarGap(gap)) + bar := barchart.New(len(dataSet)*(barWidth+gap), h-1, opts...) + return &Model{ + summary: bar, + data: dataSet, + } +} diff --git a/pkg/cmd/trace/chart/timeserieslinechart/options.go b/pkg/cmd/trace/chart/timeserieslinechart/options.go new file mode 100644 index 000000000..c68b1cc62 --- /dev/null +++ b/pkg/cmd/trace/chart/timeserieslinechart/options.go @@ -0,0 +1,139 @@ +// ntcharts - Copyright (c) 2024 Neomantra Corp. + +package timeserieslinechart + +// File contains options used by the timeserieslinechart during initialization with New(). + +import ( + "time" + + "github.com/NimbleMarkets/ntcharts/canvas/runes" + "github.com/NimbleMarkets/ntcharts/linechart" + + "github.com/charmbracelet/lipgloss" + zone "github.com/lrstanley/bubblezone" +) + +// Option is used to set options when initializing a timeserieslinechart. Example: +// +// tslc := New(width, height, minX, maxX, minY, maxY, WithStyles(someLineStyle, someLipglossStyle)) +type Option func(*Model) + +// WithLineChart sets internal linechart to given linechart. +func WithLineChart(lc *linechart.Model) Option { + return func(m *Model) { + m.Model = *lc + } +} + +// WithUpdateHandler sets the UpdateHandler used +// when processing bubbletea Msg events in Update(). +func WithUpdateHandler(h linechart.UpdateHandler) Option { + return func(m *Model) { + m.UpdateHandler = h + } +} + +// WithZoneManager sets the bubblezone Manager used +// when processing bubbletea Msg mouse events in Update(). +func WithZoneManager(zm *zone.Manager) Option { + return func(m *Model) { + m.SetZoneManager(zm) + } +} + +// WithXLabelFormatter sets the default X label formatter for displaying X values as strings. +func WithXLabelFormatter(fmter linechart.LabelFormatter) Option { + return func(m *Model) { + m.XLabelFormatter = fmter + } +} + +// WithYLabelFormatter sets the default Y label formatter for displaying Y values as strings. +func WithYLabelFormatter(fmter linechart.LabelFormatter) Option { + return func(m *Model) { + m.YLabelFormatter = fmter + } +} + +// WithAxesStyles sets the axes line and line label styles. +func WithAxesStyles(as lipgloss.Style, ls lipgloss.Style) Option { + return func(m *Model) { + m.AxisStyle = as + m.LabelStyle = ls + } +} + +// WithXYSteps sets the number of steps when drawing X and Y axes values. +// If X steps 0, then X axis will be hidden. +// If Y steps 0, then Y axis will be hidden. +func WithXYSteps(x, y int) Option { + return func(m *Model) { + m.SetXStep(x) + m.SetYStep(y) + } +} + +// WithYRange sets expected and displayed +// minimum and maximum Y value range. +func WithYRange(min, max float64) Option { + return func(m *Model) { + m.SetYRange(min, max) + m.SetViewYRange(min, max) + } +} + +// WithTimeRange sets expected and displayed minimun and maximum +// time values range on the X axis. +func WithTimeRange(min, max time.Time) Option { + return func(m *Model) { + m.SetTimeRange(min, max) + m.SetViewTimeRange(min, max) + } +} + +// WithLineStyle sets the default line style of data sets. +func WithLineStyle(ls runes.LineStyle) Option { + return func(m *Model) { + m.SetLineStyle(ls) + } +} + +// WithDataSetLineStyle sets the line style of the data set given by name. +func WithDataSetLineStyle(n string, ls runes.LineStyle) Option { + return func(m *Model) { + m.SetDataSetLineStyle(n, ls) + } +} + +// WithStyle sets the default lipgloss style of data sets. +func WithStyle(s lipgloss.Style) Option { + return func(m *Model) { + m.SetStyle(s) + } +} + +// WithDataSetStyle sets the lipgloss style of the data set given by name. +func WithDataSetStyle(n string, s lipgloss.Style) Option { + return func(m *Model) { + m.SetDataSetStyle(n, s) + } +} + +// WithTimeSeries adds []TimePoint values to the default data set. +func WithTimeSeries(p []TimePoint) Option { + return func(m *Model) { + for _, v := range p { + m.Push(v) + } + } +} + +// WithDataSetTimeSeries adds []TimePoint data points to the data set given by name. +func WithDataSetTimeSeries(n string, p []TimePoint) Option { + return func(m *Model) { + for _, v := range p { + m.PushDataSet(n, v) + } + } +} diff --git a/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go b/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go new file mode 100644 index 000000000..b551812a2 --- /dev/null +++ b/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go @@ -0,0 +1,586 @@ +// ntcharts - Copyright (c) 2024 Neomantra Corp. +/* +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 timeserieslinechart implements a linechart that draws lines +// for time series data points +package timeserieslinechart + +// https://en.wikipedia.org/wiki/Moving_average + +import ( + "fmt" + "math" + "sort" + "time" + + "github.com/NimbleMarkets/ntcharts/canvas" + "github.com/NimbleMarkets/ntcharts/canvas/buffer" + "github.com/NimbleMarkets/ntcharts/canvas/graph" + "github.com/NimbleMarkets/ntcharts/canvas/runes" + "github.com/NimbleMarkets/ntcharts/linechart" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +const DefaultDataSetName = "default" + +type StyleFunc func(*TimePoint) lipgloss.Style + +func DateTimeLabelFormatter() linechart.LabelFormatter { + var yearLabel string + return func(i int, v float64) string { + if i == 0 { // reset year labeling if redisplaying values + yearLabel = "" + } + t := time.Unix(int64(v), 0).UTC() + monthDay := t.Format("01/02") + year := t.Format("'06") + if yearLabel != year { // apply year label if first time seeing year + yearLabel = year + return fmt.Sprintf("%s %s", yearLabel, monthDay) + } else { + return monthDay + } + } +} + +func HourTimeLabelFormatter() linechart.LabelFormatter { + return func(i int, v float64) string { + t := time.Unix(int64(v), 0).UTC() + return t.Format("15:04:05") + } +} + +type TimePoint struct { + Time time.Time + Value float64 +} + +// cAverage tracks cumulative average +type cAverage struct { + Avg float64 + Count float64 +} + +// Add adds a float64 to current cumulative average +func (a *cAverage) Add(f float64) float64 { + a.Count += 1 + a.Avg += (f - a.Avg) / a.Count + return a.Avg +} + +type dataSet struct { + LineStyle runes.LineStyle // type of line runes to draw + Style lipgloss.Style + + lastTime time.Time // last seen time value + + // stores TimePoints as FloatPoint64{X:time.Time, Y: value} + // time.Time will be converted to seconds since epoch. + // both time and value will be scaled to fit the graphing area + tBuf *buffer.Float64PointScaleBuffer +} + +// Model contains state of a timeserieslinechart with an embedded linechart.Model +// The X axis contains time.Time values and the Y axis contains float64 values. +// A data set consists of a sequence TimePoints in chronological order. +// If multiple TimePoints map to the same column, then average value the time points +// will be used as the Y value of the column. +// The X axis contains a time range and the Y axis contains a numeric value range. +// Uses linechart Model UpdateHandler() for processing keyboard and mouse messages. +type Model struct { + linechart.Model + dLineStyle runes.LineStyle // default data set LineStyletype + dStyle lipgloss.Style // default data set Style + dStyleFunc StyleFunc + dSets map[string]*dataSet // maps names to data sets + + pointToDataMap map[canvas.Point]*TimePoint // canvas point to time point map +} + +// New returns a timeserieslinechart Model initialized from +// width, height, Y value range and various options. +// By default, the chart will set time.Now() as the minimum time, +// enable auto set X and Y value ranges, +// and only allow moving viewport on X axis. +func New(w, h int, opts ...Option) Model { + m := Model{ + Model: linechart.New(w, h, 0.0, float64(time.Now().Unix()), 0, 1, + linechart.WithXYSteps(4, 2), + linechart.WithXLabelFormatter(DateTimeLabelFormatter()), + linechart.WithAutoXYRange(), // automatically adjust value ranges + linechart.WithUpdateHandler(DateUpdateHandler(1))), // only scroll on X axis, increments by 1 day + dLineStyle: runes.ArcLineStyle, + dStyle: lipgloss.NewStyle(), + dSets: make(map[string]*dataSet), + } + for _, opt := range opts { + opt(&m) + } + m.UpdateGraphSizes() + m.rescaleData() + if _, ok := m.dSets[DefaultDataSetName]; !ok { + m.dSets[DefaultDataSetName] = m.newDataSet() + } + return m +} + +// newDataSet returns a new initialize *dataSet. +func (m *Model) newDataSet() *dataSet { + xs := float64(m.GraphWidth()) / (m.ViewMaxX() - m.ViewMinX()) // x scale factor + ys := float64(m.Origin().Y) / (m.ViewMaxY() - m.ViewMinY()) // y scale factor + offset := canvas.Float64Point{X: m.ViewMinX(), Y: m.ViewMinY()} + scale := canvas.Float64Point{X: xs, Y: ys} + return &dataSet{ + LineStyle: m.dLineStyle, + Style: m.dStyle, + tBuf: buffer.NewFloat64PointScaleBuffer(offset, scale), + } +} + +// rescaleData will reinitialize time chunks and +// map time points into graph columns for display +func (m *Model) rescaleData() { + // rescale time points buffer + xs := float64(m.GraphWidth()) / (m.ViewMaxX() - m.ViewMinX()) // x scale factor + ys := float64(m.Origin().Y) / (m.ViewMaxY() - m.ViewMinY()) // y scale factor + offset := canvas.Float64Point{X: m.ViewMinX(), Y: m.ViewMinY()} + scale := canvas.Float64Point{X: xs, Y: ys} + for _, ds := range m.dSets { + if ds.tBuf.Offset() != offset { + ds.tBuf.SetOffset(offset) + } + if ds.tBuf.Scale() != scale { + ds.tBuf.SetScale(scale) + } + } +} + +// ClearAllData will reset stored data values in all data sets. +func (m *Model) ClearAllData() { + for _, ds := range m.dSets { + ds.tBuf.Clear() + } + m.dSets[DefaultDataSetName] = m.newDataSet() +} + +// ClearDataSet will erase stored data set given by name string. +func (m *Model) ClearDataSet(n string) { + if ds, ok := m.dSets[n]; ok { + ds.tBuf.Clear() + } +} + +// SetTimeRange updates the minimum and maximum expected time values. +// Existing data will be rescaled. +func (m *Model) SetTimeRange(min, max time.Time) { + m.Model.SetXRange(float64(min.Unix()), float64(max.Unix())) + m.rescaleData() +} + +// SetYRange updates the minimum and maximum expected Y values. +// Existing data will be rescaled. +func (m *Model) SetYRange(min, max float64) { + m.Model.SetYRange(min, max) + m.rescaleData() +} + +// SetViewTimeRange updates the displayed minimum and maximum time values. +// Existing data will be rescaled. +func (m *Model) SetViewTimeRange(min, max time.Time) { + m.Model.SetViewXRange(float64(min.Unix()), float64(max.Unix())) + m.rescaleData() +} + +// SetViewYRange updates the displayed minimum and maximum Y values. +// Existing data will be rescaled. +func (m *Model) SetViewYRange(min, max float64) { + m.Model.SetViewYRange(min, max) + m.rescaleData() +} + +// SetViewTimeAndYRange updates the displayed minimum and maximum time and Y values. +// Existing data will be rescaled. +func (m *Model) SetViewTimeAndYRange(minX, maxX time.Time, minY, maxY float64) { + m.Model.SetViewXRange(float64(minX.Unix()), float64(maxX.Unix())) + m.Model.SetViewYRange(minY, maxY) + m.rescaleData() +} + +// Resize will change timeserieslinechart display width and height. +// Existing data will be rescaled. +func (m *Model) Resize(w, h int) { + m.Model.Resize(w, h) + m.rescaleData() +} + +// SetLineStyle will set the default line styles of data sets. +func (m *Model) SetLineStyle(ls runes.LineStyle) { + m.dLineStyle = ls + m.SetDataSetLineStyle(DefaultDataSetName, ls) +} + +// SetStyle will set the default lipgloss styles of data sets. +func (m *Model) SetStyle(s lipgloss.Style) { + m.dStyle = s + m.SetDataSetStyle(DefaultDataSetName, s) +} + +// SetDataSetLineStyle will set the line style of the given data set by name string. +func (m *Model) SetDataSetLineStyle(n string, ls runes.LineStyle) { + if _, ok := m.dSets[n]; !ok { + m.dSets[n] = m.newDataSet() + } + ds := m.dSets[n] + ds.LineStyle = ls +} + +// SetDataSetStyle will set the lipgloss style of the given data set by name string. +func (m *Model) SetDataSetStyle(n string, s lipgloss.Style) { + if _, ok := m.dSets[n]; !ok { + m.dSets[n] = m.newDataSet() + } + ds := m.dSets[n] + ds.Style = s +} + +func (m *Model) SetDataSetStyleFunc(f StyleFunc) { + m.dStyleFunc = f +} + +// Push will push a TimePoint data value to the default data set +// to be displayed with Draw. +func (m *Model) Push(t TimePoint) { + m.PushDataSet(DefaultDataSetName, t) +} + +// Push will push a TimePoint data value to a data set +// to be displayed with Draw. Using given data set by name string. +func (m *Model) PushDataSet(n string, t TimePoint) { + f := canvas.Float64Point{X: float64(t.Time.Unix()), Y: t.Value} + // auto adjust x and y ranges if enabled + if m.AutoAdjustRange(f) { + m.UpdateGraphSizes() + m.rescaleData() + } + if _, ok := m.dSets[n]; !ok { + m.dSets[n] = m.newDataSet() + } + ds := m.dSets[n] + ds.tBuf.Push(f) +} + +// Draw will draw lines runes displayed from right to left +// of the graphing area of the canvas. Uses default data set. +func (m *Model) Draw() { + m.DrawDataSets([]string{DefaultDataSetName}) +} + +// DrawAll will draw lines runes for all data sets +// from right to left of the graphing area of the canvas. +func (m *Model) DrawAll() { + names := make([]string, 0, len(m.dSets)) + for n, ds := range m.dSets { + if ds.tBuf.Length() > 0 { + names = append(names, n) + } + } + sort.Strings(names) + m.DrawDataSets(names) +} + +// DrawDataSets will draw lines runes from right to left +// of the graphing area of the canvas for each data set given +// by name strings. +func (m *Model) DrawDataSets(names []string) { + if len(names) == 0 { + return + } + m.Clear() + m.DrawXYAxisAndLabel() + for _, n := range names { + if ds, ok := m.dSets[n]; ok { + dataPoints := ds.tBuf.ReadAll() + dataLen := len(dataPoints) + if dataLen == 0 { + return + } + // get sequence of line values for graphing + seqY := m.getLineSequence(dataPoints) + // convert to canvas coordinates and avoid drawing below X axis + yCoords := canvas.CanvasYCoordinates(m.Origin().Y, seqY) + if m.XStep() > 0 { + for i, v := range yCoords { + if v > m.Origin().Y { + yCoords[i] = m.Origin().Y + } + } + } + startX := m.Canvas.Width() - len(yCoords) + graph.DrawLineSequence(&m.Canvas, + (startX == m.Origin().X), + startX, + yCoords, + ds.LineStyle, + ds.Style) + } + } +} + +// DrawRect will draw rect dots. Uses default data set. +func (m *Model) DrawRect() { + m.DrawRectDataSets([]string{DefaultDataSetName}) +} + +// DrawRectAll will draw rect dots for all data sets. +func (m *Model) DrawRectAll() { + names := make([]string, 0, len(m.dSets)) + for n, ds := range m.dSets { + if ds.tBuf.Length() > 0 { + names = append(names, n) + } + } + sort.Strings(names) + m.DrawRectDataSets(names) +} + +// DrawRectDataSets will draw each point in data set as a rect. +func (m *Model) DrawRectDataSets(names []string) { + if len(names) == 0 { + return + } + m.Clear() + m.DrawXYAxisAndLabel() + m.pointToDataMap = make(map[canvas.Point]*TimePoint) + for _, n := range names { + if ds, ok := m.dSets[n]; ok { + dataPoints := ds.tBuf.ReadAll() + dataLen := len(dataPoints) + if dataLen == 0 { + return + } + for i := 0; i < dataLen; i++ { + fp := dataPoints[i] + p := canvas.CanvasPointFromFloat64Point(m.Origin(), fp) + // get all rune patterns for braille grid + // and draw them on to the canvas + startX := 0 + if m.YStep() > 0 { + startX = m.Origin().X + 1 + } + p.X += startX + style := m.dStyle + raw := ds.tBuf.AtRaw(i) + tp := &TimePoint{Time: time.Unix(int64(raw.X), 0), Value: raw.Y} + if m.dStyleFunc != nil { + style = m.dStyleFunc(tp) + } + m.Canvas.SetCell(p, canvas.NewCellWithStyle(runes.FullBlock, style)) + + m.pointToDataMap[p] = tp + } + } + } +} + +// DrawBraille will draw braille runes displayed from right to left +// of the graphing area of the canvas. Uses default data set. +func (m *Model) DrawBraille() { + m.DrawBrailleDataSets([]string{DefaultDataSetName}) +} + +// DrawBrailleAll will draw braille runes for all data sets +// from right to left of the graphing area of the canvas. +func (m *Model) DrawBrailleAll() { + names := make([]string, 0, len(m.dSets)) + for n, ds := range m.dSets { + if ds.tBuf.Length() > 0 { + names = append(names, n) + } + } + sort.Strings(names) + m.DrawBrailleDataSets(names) +} + +// DrawBrailleDataSets will draw braille runes from right to left +// of the graphing area of the canvas for each data set given +// by name strings. +func (m *Model) DrawBrailleDataSets(names []string) { + if len(names) == 0 { + return + } + m.Clear() + m.DrawXYAxisAndLabel() + m.pointToDataMap = make(map[canvas.Point]*TimePoint) + for _, n := range names { + if ds, ok := m.dSets[n]; ok { + dataPoints := ds.tBuf.ReadAll() + dataLen := len(dataPoints) + if dataLen == 0 { + return + } + // draw lines from each point to the next point + bGrid := graph.NewBrailleGrid(m.GraphWidth(), m.GraphHeight(), + 0, float64(m.GraphWidth()), // X values already scaled to graph + 0, float64(m.GraphHeight())) // Y values already scaled to graph + for i := 0; i < dataLen; i++ { + p := dataPoints[i] + // ignore points that will not be displayed + bothBeforeMin := p.X < 0 + bothAfterMax := p.X > float64(m.GraphWidth()) + if bothBeforeMin || bothAfterMax { + continue + } + // get braille grid points from two Float64Point data points + gp := bGrid.GridPoint(p) + bGrid.Set(gp) + + // cache the mapping relationship + pos := canvas.Point{X: gp.X / m.XStep(), Y: gp.Y / m.YStep()} + raw := ds.tBuf.AtRaw(i) + m.pointToDataMap[pos] = &TimePoint{Time: time.Unix(int64(raw.X), 0), Value: raw.Y} + } + + // get all rune patterns for braille grid + // and draw them on to the canvas + startX := 0 + if m.YStep() > 0 { + startX = m.Origin().X + 1 + } + patterns := bGrid.BraillePatterns() + for i, xb := range patterns { + for j, r := range xb { + if r != runes.BrailleBlockOffset { + patterns[i][j] = runes.FullBlock + } + } + } + DrawBraillePatterns(&m.Canvas, + canvas.Point{X: startX, Y: 0}, patterns, ds.Style) + } + } +} + +// DrawColumnRune draws a braille rune on to the canvas at given (X,Y) coordinates with given style. +// The function checks for existing braille runes already on the canvas and +// will draw a new braille pattern with the dot patterns of both the existing and given runes. +// Does nothing if given rune is Null or is not a braille rune. +func DrawBrailleRune(m *canvas.Model, p canvas.Point, r rune, s lipgloss.Style) { + if r == runes.Null { + return + } + cr := m.Cell(p).Rune + if cr == 0 { // set rune if nothing exists on canvas + m.SetCell(p, canvas.NewCellWithStyle(r, s)) + return + } + m.SetCell(p, canvas.NewCellWithStyle(r, s)) +} + +// DrawBraillePatterns draws braille runes from a [][]rune representing a 2D grid of +// Braille Pattern runes. The runes will be drawn onto the canvas from starting from top +// left of the grid to the bottom right of the grid starting at the given canvas Point. +// Given style will be applied to all runes drawn. +// This function can be used with the output [][]rune from PatternDotsGrid.BraillePatterns(). +func DrawBraillePatterns(m *canvas.Model, p canvas.Point, b [][]rune, s lipgloss.Style) { + for y, row := range b { + for x, r := range row { + if r != runes.BrailleBlockOffset { + DrawBrailleRune(m, p.Add(canvas.Point{X: x, Y: y}), r, s) + } + } + } +} + +// getLineSequence returns a sequence of Y values +// to draw line runes from a given set of scaled []FloatPoint64. +func (m *Model) getLineSequence(points []canvas.Float64Point) []int { + width := m.Width() - m.Origin().X // line runes can draw on axes + if width <= 0 { + return []int{} + } + dataLen := len(points) + // each index of the bucket corresponds to a graph column. + // each index value is the average of data point values + // that is mapped to that graph column. + buckets := make([]cAverage, width, width) + for i := 0; i < dataLen; i++ { + j := i + 1 + if j >= dataLen { + j = i + } + p1 := canvas.NewPointFromFloat64Point(points[i]) + p2 := canvas.NewPointFromFloat64Point(points[j]) + // ignore points that will not be displayed on the graph + bothBeforeMin := (p1.X < 0 && p2.X < 0) + bothAfterMax := (p1.X > m.GraphWidth() && p2.X > m.GraphWidth()) + if bothBeforeMin || bothAfterMax { + continue + } + // place all points between two points + // that approximates a line into buckets + points := graph.GetLinePoints(p1, p2) + for _, p := range points { + if (p.X >= 0) && (p.X) < width { + buckets[p.X].Add(float64(p.Y)) + } + } + } + // populate sequence of Y values to for drawing + r := make([]int, width, width) + for i, v := range buckets { + r[i] = int(math.Round(v.Avg)) + } + return r +} + +// Update processes bubbletea Msg to by invoking +// UpdateHandlerFunc callback if linechart is focused. +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + if !m.Focused() { + return m, nil + } + m.UpdateHandler(&m.Model, msg) + m.rescaleData() + return m, nil +} + +func (m *Model) TimePointFromPoint(point canvas.Point) *TimePoint { + if m.pointToDataMap == nil { + return nil + } + return m.pointToDataMap[point] +} + +func (m *Model) HighlightLine(y int, color lipgloss.Color) { + if y < 0 || y >= m.Origin().Y { + return + } + startX := 0 + if m.YStep() > 0 { + startX = m.Origin().X + 1 + } + for i := 0; i < m.GraphWidth(); i++ { + p := canvas.Point{X: startX + i, Y: y} + cell := m.Canvas.Cell(p) + m.Canvas.SetCell(p, canvas.NewCellWithStyle(cell.Rune, cell.Style.Background(color))) + } +} diff --git a/pkg/cmd/trace/chart/timeserieslinechart/updatehandler.go b/pkg/cmd/trace/chart/timeserieslinechart/updatehandler.go new file mode 100644 index 000000000..0192293f4 --- /dev/null +++ b/pkg/cmd/trace/chart/timeserieslinechart/updatehandler.go @@ -0,0 +1,77 @@ +// ntcharts - Copyright (c) 2024 Neomantra Corp. + +package timeserieslinechart + +// File contains methods and objects used during +// timeserieslinechart Model Update() to modify internal state. +// timeserieslinechart is able to zoom in and out of the graph, +// and increase and decrease the X values to simulating moving +// the viewport of the linechart + +import ( + "github.com/NimbleMarkets/ntcharts/linechart" +) + +// DateUpdateHandler is used by timeserieslinechart to enable +// zooming in and out with the mouse wheel or page up and page down, +// moving the viewing window by holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by day increments. +// Uses linechart Canvas Keymap for keyboard messages. +func DateUpdateHandler(i int) linechart.UpdateHandler { + daySeconds := 86400 * i // number of seconds in a day + return linechart.XAxisUpdateHandler(float64(daySeconds)) +} + +// DateNoZoomUpdateHandler is used by timeserieslinechart to enable +// moving the viewing window by using the mouse scroll wheel, +// holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by day increments. +// Uses linechart Canvas Keymap for keyboard messages. +func DateNoZoomUpdateHandler(i int) linechart.UpdateHandler { + daySeconds := 86400 * i // number of seconds in a day + return linechart.XAxisNoZoomUpdateHandler(float64(daySeconds)) +} + +// HourUpdateHandler is used by timeserieslinechart to enable +// zooming in and out with the mouse wheel or page up and page down, +// moving the viewing window by holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by hour increments. +// Uses linechart Canvas Keymap for keyboard messages. +func HourUpdateHandler(i int) linechart.UpdateHandler { + hourSeconds := 3600 * i // number of seconds in a hour + return linechart.XAxisUpdateHandler(float64(hourSeconds)) +} + +// HourNoZoomUpdateHandler is used by timeserieslinechart to enable +// moving the viewing window by using the mouse scroll wheel, +// holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by hour increments. +// Uses linechart Canvas Keymap for keyboard messages. +func HourNoZoomUpdateHandler(i int) linechart.UpdateHandler { + hourSeconds := 3600 * i // number of seconds in a hour + return linechart.XAxisNoZoomUpdateHandler(float64(hourSeconds)) +} + +// SecondUpdateHandler is used by timeserieslinechart to enable +// zooming in and out with the mouse wheel or page up and page down, +// moving the viewing window by holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by second increments. +// Uses linechart Canvas Keymap for keyboard messages. +func SecondUpdateHandler(i int) linechart.UpdateHandler { + return linechart.XAxisUpdateHandler(float64(i)) +} + +// SecondNoZoomUpdateHandler is used by timeserieslinechart to enable +// moving the viewing window by using the mouse scroll wheel, +// holding down mouse button and moving, +// and moving the viewing window with the arrow keys. +// There is only movement along the X axis by second increments. +// Uses linechart Canvas Keymap for keyboard messages. +func SecondNoZoomUpdateHandler(i int) linechart.UpdateHandler { + return linechart.XAxisNoZoomUpdateHandler(float64(i)) +} diff --git a/pkg/cmd/trace/create.go b/pkg/cmd/trace/create.go new file mode 100644 index 000000000..6307b20c9 --- /dev/null +++ b/pkg/cmd/trace/create.go @@ -0,0 +1,94 @@ +/* +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 trace + +import ( + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kbcli/pkg/action" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" +) + +const ( + CueTemplateName = "trace_template.cue" +) + +var ( + createExamples = templates.Examples(` + # create a trace for cluster has the same name 'pg-cluster' + kbcli trace create pg-cluster + + # create a trace for cluster has the name of 'pg-cluster' + kbcli trace create pg-cluster-trace --cluster-name pg-cluster + + # create a trace with custom locale, stateEvaluationExpression + kbcli trace create pg-cluster-trace --locale zh_cn --cel-state-evaluation-expression "has(object.status.phase) && object.status.phase == \"Running\""`) +) + +type CreateOptions struct { + action.CreateOptions `json:"-"` + ClusterName string `json:"clusterName,omitempty"` + Locale string `json:"locale,omitempty"` + Depth int64 `json:"depth,omitempty"` + CelStateEvaluationExpression string `json:"celStateEvaluationExpression,omitempty"` +} + +func (o *CreateOptions) Complete() error { + o.CreateOptions.Options = o + return o.CreateOptions.Complete() +} + +func (o *CreateOptions) Run() error { + return o.CreateOptions.Run() +} + +func newCreateCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := &CreateOptions{ + CreateOptions: action.CreateOptions{ + Factory: f, + IOStreams: streams, + CueTemplateName: CueTemplateName, + GVR: types.TraceGVR(), + }, + } + cmd := &cobra.Command{ + Use: "create trace-name", + Short: "create a trace.", + Example: createExamples, + Aliases: []string{"c"}, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.TraceGVR()), + Run: func(cmd *cobra.Command, args []string) { + o.CreateOptions.Args = args + util.CheckErr(o.Complete()) + util.CheckErr(o.Run()) + }, + } + + cmd.Flags().StringVar(&o.ClusterName, "cluster-name", "", "Specify target cluster name.") + cmd.Flags().StringVar(&o.Locale, "locale", "", "Specify locale.") + cmd.Flags().Int64Var(&o.Depth, "depth", 0, "Specify object tree depth to display.") + cmd.Flags().StringVar(&o.CelStateEvaluationExpression, "cel-state-evaluation-expression", "", "Specify CEL state evaluation expression.") + + return cmd +} diff --git a/pkg/cmd/trace/delete.go b/pkg/cmd/trace/delete.go new file mode 100644 index 000000000..90024c635 --- /dev/null +++ b/pkg/cmd/trace/delete.go @@ -0,0 +1,65 @@ +/* +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 trace + +import ( + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kbcli/pkg/action" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" +) + +var ( + deleteExamples = templates.Examples(` + # Delete a trace + kbcli trace delete pg-cluster`) +) + +func newDeleteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := action.NewDeleteOptions(f, streams, types.TraceGVR()) + cmd := &cobra.Command{ + Use: "delete trace-name", + Short: "Delete a trace.", + Example: deleteExamples, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.TraceGVR()), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(complete(o, args)) + cmdutil.CheckErr(o.Run()) + }, + } + return cmd +} + +func complete(o *action.DeleteOptions, args []string) error { + if len(args) == 0 { + return fmt.Errorf("missing trace name") + } + if len(args) > 1 { + return fmt.Errorf("can't delete multiple views at once") + } + o.Names = args + return nil +} diff --git a/pkg/cmd/trace/list.go b/pkg/cmd/trace/list.go new file mode 100644 index 000000000..c3ae64ee9 --- /dev/null +++ b/pkg/cmd/trace/list.go @@ -0,0 +1,54 @@ +/* +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 trace + +import ( + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kbcli/pkg/action" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" +) + +var ( + listExamples = templates.Examples(` + # list all traces + kbcli trace list`) +) + +func newListCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := action.NewListOptions(f, streams, types.TraceGVR()) + cmd := &cobra.Command{ + Use: "list", + Short: "list all traces.", + Example: listExamples, + Aliases: []string{"ls"}, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, o.GVR), + Run: func(cmd *cobra.Command, args []string) { + _, err := o.Run() + util.CheckErr(err) + }, + } + o.AddFlags(cmd) + return cmd +} diff --git a/pkg/cmd/trace/update.go b/pkg/cmd/trace/update.go new file mode 100644 index 000000000..b52c7ac48 --- /dev/null +++ b/pkg/cmd/trace/update.go @@ -0,0 +1,102 @@ +/* +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 trace + +import ( + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kbcli/pkg/action" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" +) + +var ( + updateExamples = templates.Examples(` + # update a trace with custom locale, stateEvaluationExpression + kbcli trace update pg-cluster-trace --locale zh_cn --cel-state-evaluation-expression "has(object.status.phase) && object.status.phase == \"Running\""`) +) + +type UpdateOptions struct { + *action.PatchOptions + Locale string `json:"locale,omitempty"` + Depth int64 `json:"depth,omitempty"` + CelStateEvaluationExpression string `json:"celStateEvaluationExpression,omitempty"` +} + +func (o *UpdateOptions) CmdComplete(cmd *cobra.Command) error { + if err := o.PatchOptions.CmdComplete(cmd); err != nil { + return err + } + return o.buildPatch() +} + +func (o *UpdateOptions) buildPatch() error { + spec := make(map[string]interface{}) + if o.Depth >= 0 { + spec["depth"] = o.Depth + } + if o.Locale != "" { + spec["locale"] = o.Locale + } + if o.CelStateEvaluationExpression != "" { + spec["celStateEvaluationExpression"] = o.CelStateEvaluationExpression + } + obj := unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": spec, + }, + } + bytes, err := obj.MarshalJSON() + if err != nil { + return err + } + o.Patch = string(bytes) + return nil +} + +func newUpdateCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := &UpdateOptions{ + PatchOptions: action.NewPatchOptions(f, streams, types.TraceGVR()), + } + cmd := &cobra.Command{ + Use: "update trace-name", + Short: "update a trace.", + Example: updateExamples, + Aliases: []string{"u"}, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.TraceGVR()), + Run: func(cmd *cobra.Command, args []string) { + o.Names = args + util.CheckErr(o.CmdComplete(cmd)) + util.CheckErr(o.Run()) + }, + } + + cmd.Flags().StringVar(&o.Locale, "locale", "", "Specify locale.") + cmd.Flags().Int64Var(&o.Depth, "depth", -1, "Specify object tree depth to display.") + cmd.Flags().StringVar(&o.CelStateEvaluationExpression, "cel-state-evaluation-expression", "", "Specify CEL state evaluation expression.") + + o.PatchOptions.AddFlags(cmd) + + return cmd +} diff --git a/pkg/cmd/trace/view.go b/pkg/cmd/trace/view.go new file mode 100644 index 000000000..54d54999c --- /dev/null +++ b/pkg/cmd/trace/view.go @@ -0,0 +1,42 @@ +/* +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 trace + +import ( + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" +) + +func NewTraceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "trace", + Short: "trace management command", + Aliases: []string{"v"}, + } + cmd.AddCommand( + newListCmd(f, streams), + newWatchCmd(f, streams), + newCreateCmd(f, streams), + newUpdateCmd(f, streams), + newDeleteCmd(f, streams), + ) + return cmd +} diff --git a/pkg/cmd/trace/watch.go b/pkg/cmd/trace/watch.go new file mode 100644 index 000000000..ce81ea578 --- /dev/null +++ b/pkg/cmd/trace/watch.go @@ -0,0 +1,133 @@ +/* +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 trace + +import ( + "context" + "fmt" + + tea "github.com/charmbracelet/bubbletea" + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericiooptions" + "k8s.io/client-go/dynamic" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kbcli/pkg/cmd/trace/chart" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" + tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" +) + +var ( + watchExamples = templates.Examples(` + # watch a trace + kbcli trace watch pg-cluster-trace`) + + program *tea.Program +) + +func newWatchCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + cmd := &cobra.Command{ + Use: "watch trace-name", + Short: "watch a trace.", + Example: watchExamples, + Aliases: []string{"w"}, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.TraceGVR()), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(watch(f, streams, args)) + }, + } + return cmd +} + +func watch(f cmdutil.Factory, streams genericiooptions.IOStreams, args []string) error { + go doWatch(f, streams, args) + return renderTrace() +} + +func doWatch(f cmdutil.Factory, streams genericiooptions.IOStreams, args []string) { + o := &watchOptions{factory: f, streams: streams, gvr: types.TraceGVR()} + if err := o.complete(args); err != nil { + klog.Fatal("failed to init clientset", err) + } + ctx := context.TODO() + watcher, err := o.dynamic.Resource(o.gvr).Namespace(o.namespace).Watch(ctx, metav1.ListOptions{}) + if err != nil { + klog.Fatal("failed to watch trace", err) + } + for event := range watcher.ResultChan() { + obj, ok := event.Object.(*unstructured.Unstructured) + if !ok { + continue + } + trace := &tracev1.ReconciliationTrace{} + if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, trace); err != nil { + klog.Fatal("failed to convert trace object", err) + } + if trace.Name == args[0] && program != nil { + program.Send(chart.TraceUpdateMsg{Trace: trace}) + } + } +} + +func renderTrace() error { + m := chart.NewReconciliationTraceChart() + program = tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion()) + _, err := program.Run() + return err +} + +type watchOptions struct { + factory cmdutil.Factory + client clientset.Interface + dynamic dynamic.Interface + namespace string + + gvr schema.GroupVersionResource + name string + + streams genericiooptions.IOStreams +} + +func (o *watchOptions) complete(args []string) error { + var err error + + if len(args) == 0 { + return fmt.Errorf("a trace name is required") + } + o.name = args[0] + if o.client, err = o.factory.KubernetesClientSet(); err != nil { + return err + } + if o.dynamic, err = o.factory.DynamicClient(); err != nil { + return err + } + if o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace(); err != nil { + return err + } + return nil +} diff --git a/pkg/types/types.go b/pkg/types/types.go index 8fad4a9be..51f49886c 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -216,6 +216,13 @@ const ( ResourceRSM = "replicatedstatemachines" ) +// trace API group +const ( + TraceAPIGroup = "trace.kubeblocks.io" + TraceAPIVersion = "v1" + ResourceTrace = "reconciliationtraces" +) + const ( None = "" @@ -474,3 +481,7 @@ func JobGVR() schema.GroupVersionResource { func CronJobGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: K8SBatchAPIGroup, Version: K8sBatchAPIVersion, Resource: ResourceCronJobs} } + +func TraceGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: TraceAPIGroup, Version: TraceAPIVersion, Resource: ResourceTrace} +} From 8157ef2e9acd6d99e94fd3c332a94bc617f2571e Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:32:23 +0800 Subject: [PATCH 11/34] chore: playground init support specifying k3s and k3d-proxy images. (#500) (cherry picked from commit 94e1160b905eda4ffe6e0166447a26f4127071ef) --- docs/user_docs/cli/kbcli_playground_init.md | 16 +++++++----- pkg/cloudprovider/k3d.go | 24 ++++++++--------- pkg/cloudprovider/k3d_test.go | 29 ++++++++++++++++----- pkg/cloudprovider/suite_test.go | 4 +-- pkg/cloudprovider/types.go | 7 +++++ pkg/cmd/cluster/operations.go | 3 +++ pkg/cmd/cluster/register_test.go | 2 +- pkg/cmd/playground/init.go | 12 +++++++++ pkg/cmd/playground/suite_test.go | 4 +-- 9 files changed, 71 insertions(+), 30 deletions(-) diff --git a/docs/user_docs/cli/kbcli_playground_init.md b/docs/user_docs/cli/kbcli_playground_init.md index 7b5be7d51..5acd4a857 100644 --- a/docs/user_docs/cli/kbcli_playground_init.md +++ b/docs/user_docs/cli/kbcli_playground_init.md @@ -43,13 +43,15 @@ kbcli playground init [flags] ### Options ``` - --auto-approve Skip interactive approval during the initialization of playground - --cloud-provider string Cloud provider type, one of [local aws] (default "local") - --cluster-type string Specify the cluster type to create, use 'kbcli cluster create --help' to get the available cluster type. (default "apecloud-mysql") - -h, --help help for init - --region string The region to create kubernetes cluster - --timeout duration Time to wait for init playground, such as --timeout=10m (default 10m0s) - --version string KubeBlocks version + --auto-approve Skip interactive approval during the initialization of playground + --cloud-provider string Cloud provider type, one of [local aws] (default "local") + --cluster-type string Specify the cluster type to create, use 'kbcli cluster create --help' to get the available cluster type. (default "apecloud-mysql") + -h, --help help for init + --k3d-proxy-image string Specify k3d proxy image if you want to init playground locally (default "docker.io/apecloud/k3d-proxy:5.4.4") + --k3s-image string Specify k3s image that you want to use for the nodes if you want to init playground locally (default "rancher/k3s:v1.23.8-k3s1") + --region string The region to create kubernetes cluster + --timeout duration Time to wait for init playground, such as --timeout=10m (default 10m0s) + --version string KubeBlocks version ``` ### Options inherited from parent commands diff --git a/pkg/cloudprovider/k3d.go b/pkg/cloudprovider/k3d.go index ac26d5e96..1439d074e 100644 --- a/pkg/cloudprovider/k3d.go +++ b/pkg/cloudprovider/k3d.go @@ -52,11 +52,11 @@ var ( // all cluster will be created in this network, so they can communicate with each other CliDockerNetwork = "k3d-kbcli-playground" - // K3sImage is k3s image repo - K3sImage = "rancher/k3s:" + version.K3sImageTag + // K3sImageDefault is k3s image repo + K3sImageDefault = "rancher/k3s:" + version.K3sImageTag - // K3dProxyImage is k3d proxy image repo - K3dProxyImage = "docker.io/apecloud/k3d-proxy:" + version.K3dVersion + // K3dProxyImageDefault is k3d proxy image repo + K3dProxyImageDefault = "docker.io/apecloud/k3d-proxy:" + version.K3dVersion // K3dFixEnv KBEnvFix fixes.K3DFixEnv = "KB_FIX_MOUNTS" @@ -97,7 +97,7 @@ func (p *localCloudProvider) Name() string { func (p *localCloudProvider) CreateK8sCluster(clusterInfo *K8sClusterInfo) error { var err error - if p.cfg, err = buildClusterRunConfig(clusterInfo.ClusterName); err != nil { + if p.cfg, err = buildClusterRunConfig(clusterInfo.ClusterName, clusterInfo.K3sImage, clusterInfo.K3dProxyImage); err != nil { return err } @@ -209,9 +209,9 @@ func (p *localCloudProvider) GetClusterInfo() (*K8sClusterInfo, error) { } // buildClusterRunConfig returns the run-config for the k3d cluster -func buildClusterRunConfig(clusterName string) (config.ClusterConfig, error) { +func buildClusterRunConfig(clusterName string, k3sImage string, k3dProxyImage string) (config.ClusterConfig, error) { createOpts := buildClusterCreateOpts() - cluster, err := buildClusterConfig(clusterName, createOpts) + cluster, err := buildClusterConfig(clusterName, createOpts, k3sImage, k3dProxyImage) if err != nil { return config.ClusterConfig{}, err } @@ -239,7 +239,7 @@ func buildClusterCreateOpts() k3d.ClusterCreateOpts { return clusterCreateOpts } -func buildClusterConfig(clusterName string, opts k3d.ClusterCreateOpts) (k3d.Cluster, error) { +func buildClusterConfig(clusterName string, opts k3d.ClusterCreateOpts, k3sImage string, k3dProxyImage string) (k3d.Cluster, error) { var network = k3d.ClusterNetwork{ Name: CliDockerNetwork, External: false, @@ -273,14 +273,14 @@ func buildClusterConfig(clusterName string, opts k3d.ClusterCreateOpts) (k3d.Clu var nodes []*k3d.Node // build load balancer node - clusterConfig.ServerLoadBalancer = buildLoadbalancer(clusterConfig, opts) + clusterConfig.ServerLoadBalancer = buildLoadbalancer(clusterConfig, opts, k3dProxyImage) nodes = append(nodes, clusterConfig.ServerLoadBalancer.Node) // build k3d node serverNode := k3d.Node{ Name: k3dClient.GenerateNodeName(clusterConfig.Name, k3d.ServerRole, 0), Role: k3d.ServerRole, - Image: K3sImage, + Image: k3sImage, ServerOpts: k3d.ServerOpts{}, Args: []string{"--disable=metrics-server", "--disable=traefik", "--disable=local-storage"}, } @@ -317,7 +317,7 @@ func findAvailablePort(start int) (string, error) { return "", errors.New("can not find any available port") } -func buildLoadbalancer(cluster k3d.Cluster, opts k3d.ClusterCreateOpts) *k3d.Loadbalancer { +func buildLoadbalancer(cluster k3d.Cluster, opts k3d.ClusterCreateOpts, k3dProxyImage string) *k3d.Loadbalancer { lb := k3d.NewLoadbalancer() labels := map[string]string{} @@ -326,7 +326,7 @@ func buildLoadbalancer(cluster k3d.Cluster, opts k3d.ClusterCreateOpts) *k3d.Loa } lb.Node.Name = fmt.Sprintf("%s-%s-serverlb", k3d.DefaultObjectNamePrefix, cluster.Name) - lb.Node.Image = K3dProxyImage + lb.Node.Image = k3dProxyImage lb.Node.Ports = nat.PortMap{ k3d.DefaultAPIPort: []nat.PortBinding{cluster.KubeAPI.Binding}, } diff --git a/pkg/cloudprovider/k3d_test.go b/pkg/cloudprovider/k3d_test.go index 423b2f67d..a5ea366e9 100644 --- a/pkg/cloudprovider/k3d_test.go +++ b/pkg/cloudprovider/k3d_test.go @@ -31,13 +31,30 @@ var _ = Describe("playground", func() { var ( provider = newLocalCloudProvider(os.Stdout, os.Stderr) clusterName = "k3d-kb-test" + image = "test.com/k3d-proxy:5.4.4" ) - It("k3d util function", func() { - config, err := buildClusterRunConfig("test") - Expect(err).ShouldNot(HaveOccurred()) - Expect(config.Name).Should(ContainSubstring("test")) - Expect(setUpK3d(context.Background(), nil)).Should(HaveOccurred()) - Expect(provider.DeleteK8sCluster(&K8sClusterInfo{ClusterName: clusterName})).Should(HaveOccurred()) + Context("k3d util function", func() { + It("with name", func() { + config, err := buildClusterRunConfig("test", "", "") + Expect(err).ShouldNot(HaveOccurred()) + Expect(config.Name).Should(ContainSubstring("test")) + Expect(setUpK3d(context.Background(), nil)).Should(HaveOccurred()) + Expect(provider.DeleteK8sCluster(&K8sClusterInfo{ClusterName: clusterName})).Should(HaveOccurred()) + }) + + It("with name and k3s image", func() { + config, err := buildClusterRunConfig("test", image, "") + Expect(err).ShouldNot(HaveOccurred()) + Expect(config.Cluster.Nodes[1].Image).Should(ContainSubstring("test.com")) + }) + + It("with name and k3d proxy image", func() { + config, err := buildClusterRunConfig("test", "", image) + Expect(err).ShouldNot(HaveOccurred()) + Expect(config.ServerLoadBalancer.Node.Image).Should(ContainSubstring("test.com")) + }) + }) + }) diff --git a/pkg/cloudprovider/suite_test.go b/pkg/cloudprovider/suite_test.go index c8f03d902..9de7a81fe 100644 --- a/pkg/cloudprovider/suite_test.go +++ b/pkg/cloudprovider/suite_test.go @@ -33,6 +33,6 @@ func TestPlayground(t *testing.T) { var _ = BeforeSuite(func() { // set fake image info - K3sImage = "fake-k3s-image" - K3dProxyImage = "fake-k3d-proxy-image" + K3sImageDefault = "fake-k3s-image" + K3dProxyImageDefault = "fake-k3d-proxy-image" }) diff --git a/pkg/cloudprovider/types.go b/pkg/cloudprovider/types.go index fac4e0c49..e75fa8e86 100644 --- a/pkg/cloudprovider/types.go +++ b/pkg/cloudprovider/types.go @@ -68,6 +68,13 @@ type K8sClusterInfo struct { Region string `json:"region,omitempty"` KubeConfig string `json:"kube_config,omitempty"` KbcliVersion string `json:"kbcli_version,omitempty"` + K3dClusterInfo +} + +// K3dClusterInfo is the k3d cluster information for playground +type K3dClusterInfo struct { + K3sImage string `json:"k3s_image,omitempty"` + K3dProxyImage string `json:"k3d_proxy_image,omitempty"` } // IsValid checks if kubernetes cluster info is valid diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index 309c639c2..d1dc7c20d 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -1095,6 +1095,9 @@ func getTempFactory(f cmdutil.Factory) cmdutil.Factory { func buildCustomOpsCmds(option *OperationsOptions) []*cobra.Command { dynamic, _ := getTempFactory(option.Factory).DynamicClient() + if dynamic == nil { + return nil + } opsDefs, _ := dynamic.Resource(types.OpsDefinitionGVR()).List(context.TODO(), metav1.ListOptions{}) if opsDefs == nil { return nil diff --git a/pkg/cmd/cluster/register_test.go b/pkg/cmd/cluster/register_test.go index d3fdd51eb..1600afef2 100644 --- a/pkg/cmd/cluster/register_test.go +++ b/pkg/cmd/cluster/register_test.go @@ -98,7 +98,7 @@ var _ = Describe("cluster register", func() { ) AfterEach(func() { - cluster.ClearCharts(cluster.ClusterType(engine)) + os.Remove(filepath.Join(cluster.CliChartsCacheDir, filepath.Base(source))) }) It("test register chart by source", func() { diff --git a/pkg/cmd/playground/init.go b/pkg/cmd/playground/init.go index 1cd782a71..c841968cc 100644 --- a/pkg/cmd/playground/init.go +++ b/pkg/cmd/playground/init.go @@ -97,9 +97,15 @@ type initOptions struct { autoApprove bool dockerVersion *gv.Version + k3dClusterOptions baseOptions } +type k3dClusterOptions struct { + k3sImage string + k3dProxyImage string +} + func newInitCmd(streams genericiooptions.IOStreams) *cobra.Command { o := &initOptions{ IOStreams: streams, @@ -123,6 +129,8 @@ func newInitCmd(streams genericiooptions.IOStreams) *cobra.Command { cmd.Flags().StringVar(&o.region, "region", "", "The region to create kubernetes cluster") cmd.Flags().DurationVar(&o.Timeout, "timeout", 600*time.Second, "Time to wait for init playground, such as --timeout=10m") cmd.Flags().BoolVar(&o.autoApprove, "auto-approve", false, "Skip interactive approval during the initialization of playground") + cmd.Flags().StringVar(&o.k3sImage, "k3s-image", cp.K3sImageDefault, "Specify k3s image that you want to use for the nodes if you want to init playground locally") + cmd.Flags().StringVar(&o.k3dProxyImage, "k3d-proxy-image", cp.K3dProxyImageDefault, "Specify k3d proxy image if you want to init playground locally") util.CheckErr(cmd.RegisterFlagCompletionFunc( "cloud-provider", @@ -192,6 +200,10 @@ func (o *initOptions) local() error { clusterInfo = &cp.K8sClusterInfo{ CloudProvider: provider.Name(), ClusterName: types.K3dClusterName, + K3dClusterInfo: cp.K3dClusterInfo{ + K3sImage: o.k3sImage, + K3dProxyImage: o.k3dProxyImage, + }, } } diff --git a/pkg/cmd/playground/suite_test.go b/pkg/cmd/playground/suite_test.go index 7e2dba31e..5d774a082 100644 --- a/pkg/cmd/playground/suite_test.go +++ b/pkg/cmd/playground/suite_test.go @@ -37,8 +37,8 @@ func TestPlayground(t *testing.T) { var _ = BeforeSuite(func() { // set fake image info - cp.K3sImage = "fake-k3s-image" - cp.K3dProxyImage = "fake-k3d-proxy-image" + cp.K3sImageDefault = "fake-k3s-image" + cp.K3dProxyImageDefault = "fake-k3d-proxy-image" // set default cluster name to test types.K3dClusterName = "kb-playground-test" From d44637559cc1464ff445dd9dbed0b14d37df0003 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 29 Nov 2024 14:39:22 +0800 Subject: [PATCH 12/34] chore: remove standard chart schema (#504) (cherry picked from commit 1b4475e51a0440665a3ab03ab06536bbb297aa15) --- pkg/cluster/external_charts.go | 55 +++++++--------------- pkg/cluster/register_test.go | 18 +++++++ pkg/cluster/testdata/mock-chart-0.1.0.tgz | Bin 0 -> 672 bytes 3 files changed, 36 insertions(+), 37 deletions(-) create mode 100644 pkg/cluster/testdata/mock-chart-0.1.0.tgz diff --git a/pkg/cluster/external_charts.go b/pkg/cluster/external_charts.go index fc83afc05..fdaacd1a6 100644 --- a/pkg/cluster/external_charts.go +++ b/pkg/cluster/external_charts.go @@ -21,7 +21,6 @@ package cluster import ( "compress/gzip" - "encoding/json" "fmt" "io" "io/fs" @@ -29,6 +28,7 @@ import ( "path/filepath" "github.com/spf13/cobra" + "github.com/xeipuuv/gojsonschema" "gopkg.in/yaml.v2" "helm.sh/helm/v3/pkg/chart/loader" "k8s.io/klog" @@ -246,15 +246,6 @@ func (h *TypeInstance) PatchNewClusterType() error { return GlobalClusterChartConfig.WriteConfigs(CliClusterChartConfig) } -var StandardSchema = map[string]interface{}{ - "properties": map[string]interface{}{ - "replicas": nil, - "cpu": nil, - "memory": nil, - "storage": nil, - }, -} - func (h *TypeInstance) ValidateChartSchema() (bool, error) { file, err := h.loadChart() if err != nil { @@ -266,41 +257,31 @@ func (h *TypeInstance) ValidateChartSchema() (bool, error) { if err != nil { return false, err } - - data := c.Schema - if len(data) == 0 { + if c.Schema == nil || len(c.Schema) == 0 { return false, fmt.Errorf("register cluster chart of %s failed, schema of the chart doesn't exist", h.Name) } - - var schema map[string]interface{} - if err := json.Unmarshal(data, &schema); err != nil { - return false, fmt.Errorf("register cluster chart of %s failed, error decoding JSON: %s", h.Name, err) + if c.Values == nil { + return false, fmt.Errorf("register cluster chart of %s failed, values of the chart doesn't exist", h.Name) } - if err := validateSchema(schema, StandardSchema); err != nil { - return false, err - } + schemaLoader := gojsonschema.NewStringLoader(string(c.Schema)) + valuesLoader := gojsonschema.NewGoLoader(c.Values) - return true, nil -} + result, err := gojsonschema.Validate(schemaLoader, valuesLoader) + if err != nil { + return false, fmt.Errorf("Validation failed: %s\n", err) + } -func validateSchema(schema, standard map[string]interface{}) error { - for key, val := range standard { - if subStandard, ok := val.(map[string]interface{}); ok { - subSchema, ok := schema[key].(map[string]interface{}) - if !ok { - return fmt.Errorf("register cluster chart failed, schema missing required map key '%s'", key) - } - if err := validateSchema(subSchema, subStandard); err != nil { - return err - } - } else { - if _, exists := schema[key]; !exists { - return fmt.Errorf("register cluster chart failed, schema missing required key '%s'", key) - } + if result.Valid() { + return true, nil + } else { + fmt.Println("Values do not match the schema:") + res := "" + for _, desc := range result.Errors() { + res += fmt.Sprintf("- %s\n", desc) } + return false, fmt.Errorf(res) } - return nil } var _ chartLoader = &TypeInstance{} diff --git a/pkg/cluster/register_test.go b/pkg/cluster/register_test.go index a30726cbd..e95be1e19 100644 --- a/pkg/cluster/register_test.go +++ b/pkg/cluster/register_test.go @@ -63,6 +63,24 @@ var _ = Describe("cluster register", func() { Expect(err).Should(HaveOccurred()) }) + It("test invalid chart", func() { + testPath := "./testdata" + chartName := "mock-chart-0.1.0.tgz" + fakeChart := &TypeInstance{ + Name: "mock-chart", + ChartName: chartName, + } + srcPath := filepath.Join(testPath, chartName) + destPath := filepath.Join(CliChartsCacheDir, chartName) + + srcFile, _ := os.Open(srcPath) + destFile, _ := os.Create(destPath) + _, _ = io.Copy(destFile, srcFile) + _, err := fakeChart.ValidateChartSchema() + Expect(err).Should(HaveOccurred()) + _ = os.Remove(destPath) + }) + Context("test Config reader", func() { var tempConfigPath string diff --git a/pkg/cluster/testdata/mock-chart-0.1.0.tgz b/pkg/cluster/testdata/mock-chart-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..67e2d3ae65e1dc47d23d34b7b9d175144b43899e GIT binary patch literal 672 zcmV;R0$=?fiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PIxHYZFft&w2lf$Mk#5kJ;U90t;V3iw8kUPhO;laWhS)?(VF! zv!O+L@E0mn@L)lzc(Zs=t5BpC@vq6Ix%n^1%%;s|jZ`o;DEm2NvTx>n-kW*v^8)VO zta)oh$i|}jt#6XRugn%0W87*s^@=eruGqAkirH*imSwjvwkz1QEwfbtJmWm3B8ept z75v+=ikJIKKq7XHikR`xg`Q<}XzU3VN&S5hRCj>x4p`*^B#k8tyGBR?>cY5njNSYnE(sWKYVih zb}xPU2x4!I1_XwCcLpEte*3bW9z7fGzaBi_hYSCkaJ+keuyZij`C!CU^q5Co13+#@ z)P>6%l8TV{OC+Ep%Jk)%6TbBQZvXRR<^F27fB3b3_|5=`xR5Ti)#E9T;im`b!y{-p zj_s&O!|i+Ni-UPT*FT3J^`F(Kz8>>%MlsINzlj~Yp#NsuvP=D+2W}ex=)bJy$OW_} zW#l#*s`|B2CveeibOc$IHQa7wwgpsGeoE`giQibKp3H2K;1Ly)(HOa)*a1*}$edbp z{w$QVONA!T8vzShkOasDQ=7K39WQWDX#12r^5@5v97ik#3%jQrdXI)WkLzgB zPhv^Mm1R@~w8T4fnG31@U*)88iTEV+s6Z>HzFT7&q-NXCf)+1de1J(i*%;aNU>pZC z3PF*ew-P33N9V3%RY!SbDJl Date: Mon, 2 Dec 2024 16:22:26 +0800 Subject: [PATCH 13/34] fix: addon enable only register the chart when addon type is engine (#508) (cherry picked from commit 0fd532ec7672282aa0c3bda4cd2a5faf3d2c18b7) --- pkg/cmd/addon/addon.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/addon/addon.go b/pkg/cmd/addon/addon.go index 435627236..2b5c3f9d8 100644 --- a/pkg/cmd/addon/addon.go +++ b/pkg/cmd/addon/addon.go @@ -225,7 +225,9 @@ func newEnableCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. util.CheckErr(o.complete(o, cmd, []string{name})) util.CheckErr(o.CmdComplete(cmd)) util.CheckErr(o.Run()) - util.CheckErr(clusterCmd.RegisterClusterChart(f, streams, "", name, getAddonVersion(&o.addon), types.ClusterChartsRepoURL)) + if isEngineAddon(&o.addon) { + util.CheckErr(clusterCmd.RegisterClusterChart(f, streams, "", name, getAddonVersion(&o.addon), types.ClusterChartsRepoURL)) + } } }, } From fb98a27300a25676c8cdac2406f807c22cff2e83 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:52:47 +0800 Subject: [PATCH 14/34] fix: improve typo of list cmd in dataprotection (#511) (cherry picked from commit 25939fdf514f28c57b2832dfd424a8a29aa49afb) --- docs/user_docs/cli/cli.md | 10 +++++----- docs/user_docs/cli/kbcli_dataprotection.md | 10 +++++----- docs/user_docs/cli/kbcli_dataprotection_backup.md | 2 +- .../cli/kbcli_dataprotection_list-backups.md | 13 +++++++------ pkg/cmd/dataprotection/actionset.go | 2 +- pkg/cmd/dataprotection/backup.go | 10 +++++----- pkg/cmd/dataprotection/backuppolicy.go | 4 ++-- pkg/cmd/dataprotection/backuppolicytemplate.go | 4 ++-- pkg/cmd/dataprotection/datatprotection_test.go | 14 +++++++------- pkg/cmd/dataprotection/restore.go | 4 ++-- 10 files changed, 37 insertions(+), 36 deletions(-) diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 58b64aaa4..6786501ae 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -123,11 +123,11 @@ Data protection command. * [kbcli dataprotection describe-backup-policy](kbcli_dataprotection_describe-backup-policy.md) - Describe a backup policy * [kbcli dataprotection describe-restore](kbcli_dataprotection_describe-restore.md) - Describe a restore * [kbcli dataprotection edit-backup-policy](kbcli_dataprotection_edit-backup-policy.md) - Edit backup policy -* [kbcli dataprotection list-action-set](kbcli_dataprotection_list-action-set.md) - List actionsets -* [kbcli dataprotection list-backup](kbcli_dataprotection_list-backup.md) - List backups. -* [kbcli dataprotection list-backup-policy](kbcli_dataprotection_list-backup-policy.md) - List backup policies -* [kbcli dataprotection list-backup-policy-template](kbcli_dataprotection_list-backup-policy-template.md) - List backup policy template -* [kbcli dataprotection list-restore](kbcli_dataprotection_list-restore.md) - List restores. +* [kbcli dataprotection list-action-sets](kbcli_dataprotection_list-action-sets.md) - List actionsets +* [kbcli dataprotection list-backup-policies](kbcli_dataprotection_list-backup-policies.md) - List backup policies +* [kbcli dataprotection list-backup-policy-templates](kbcli_dataprotection_list-backup-policy-templates.md) - List backup policy templates +* [kbcli dataprotection list-backups](kbcli_dataprotection_list-backups.md) - List backups. +* [kbcli dataprotection list-restores](kbcli_dataprotection_list-restores.md) - List restores. * [kbcli dataprotection restore](kbcli_dataprotection_restore.md) - Restore a new cluster from backup diff --git a/docs/user_docs/cli/kbcli_dataprotection.md b/docs/user_docs/cli/kbcli_dataprotection.md index 2a02a1dae..7d23e1827 100644 --- a/docs/user_docs/cli/kbcli_dataprotection.md +++ b/docs/user_docs/cli/kbcli_dataprotection.md @@ -43,11 +43,11 @@ Data protection command. * [kbcli dataprotection describe-backup-policy](kbcli_dataprotection_describe-backup-policy.md) - Describe a backup policy * [kbcli dataprotection describe-restore](kbcli_dataprotection_describe-restore.md) - Describe a restore * [kbcli dataprotection edit-backup-policy](kbcli_dataprotection_edit-backup-policy.md) - Edit backup policy -* [kbcli dataprotection list-action-set](kbcli_dataprotection_list-action-set.md) - List actionsets -* [kbcli dataprotection list-backup](kbcli_dataprotection_list-backup.md) - List backups. -* [kbcli dataprotection list-backup-policy](kbcli_dataprotection_list-backup-policy.md) - List backup policies -* [kbcli dataprotection list-backup-policy-template](kbcli_dataprotection_list-backup-policy-template.md) - List backup policy template -* [kbcli dataprotection list-restore](kbcli_dataprotection_list-restore.md) - List restores. +* [kbcli dataprotection list-action-sets](kbcli_dataprotection_list-action-sets.md) - List actionsets +* [kbcli dataprotection list-backup-policies](kbcli_dataprotection_list-backup-policies.md) - List backup policies +* [kbcli dataprotection list-backup-policy-templates](kbcli_dataprotection_list-backup-policy-templates.md) - List backup policy templates +* [kbcli dataprotection list-backups](kbcli_dataprotection_list-backups.md) - List backups. +* [kbcli dataprotection list-restores](kbcli_dataprotection_list-restores.md) - List restores. * [kbcli dataprotection restore](kbcli_dataprotection_restore.md) - Restore a new cluster from backup #### Go Back to [CLI Overview](cli.md) Homepage. diff --git a/docs/user_docs/cli/kbcli_dataprotection_backup.md b/docs/user_docs/cli/kbcli_dataprotection_backup.md index 2011c3dc0..5608cb7da 100644 --- a/docs/user_docs/cli/kbcli_dataprotection_backup.md +++ b/docs/user_docs/cli/kbcli_dataprotection_backup.md @@ -17,7 +17,7 @@ kbcli dataprotection backup NAME [flags] # create a backup with a specified method, run "kbcli cluster desc-backup-policy mycluster" to show supported backup methods kbcli dp backup mybackup --cluster mycluster --method mymethod - # create a backup with specified backup policy, run "kbcli cluster list-backup-policy mycluster" to show the cluster supported backup policies + # create a backup with specified backup policy, run "kbcli cluster list-backup-policies mycluster" to show the cluster supported backup policies kbcli dp backup mybackup --cluster mycluster --policy mypolicy # create a backup from a parent backup diff --git a/docs/user_docs/cli/kbcli_dataprotection_list-backups.md b/docs/user_docs/cli/kbcli_dataprotection_list-backups.md index 5a2d1db48..5081f50e4 100644 --- a/docs/user_docs/cli/kbcli_dataprotection_list-backups.md +++ b/docs/user_docs/cli/kbcli_dataprotection_list-backups.md @@ -21,11 +21,13 @@ kbcli dataprotection list-backups [flags] ### Options ``` - --cluster string List backups in the specified cluster - -h, --help help for list-backups - -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) - -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. - --show-labels When printing, show all labels as the last column (default hide labels column) + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + --cluster string List backups in the specified cluster + -h, --help help for list-backups + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) ``` ### Options inherited from parent commands @@ -43,7 +45,6 @@ kbcli dataprotection list-backups [flags] --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 diff --git a/pkg/cmd/dataprotection/actionset.go b/pkg/cmd/dataprotection/actionset.go index 2823c207b..daca6162f 100644 --- a/pkg/cmd/dataprotection/actionset.go +++ b/pkg/cmd/dataprotection/actionset.go @@ -43,7 +43,7 @@ func newListActionSetCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) o := action.NewListOptions(f, streams, types.ActionSetGVR()) headers := []any{"NAME", "BACKUP-TYPE", "STATUS", "CREATED-TIME"} cmd := &cobra.Command{ - Use: "list-action-set", + Use: "list-action-sets", Short: "List actionsets", Aliases: []string{"list-as"}, Example: listActionSetExample, diff --git a/pkg/cmd/dataprotection/backup.go b/pkg/cmd/dataprotection/backup.go index 487046455..94ca4d262 100644 --- a/pkg/cmd/dataprotection/backup.go +++ b/pkg/cmd/dataprotection/backup.go @@ -55,7 +55,7 @@ var ( # create a backup with a specified method, run "kbcli cluster desc-backup-policy mycluster" to show supported backup methods kbcli dp backup mybackup --cluster mycluster --method mymethod - # create a backup with specified backup policy, run "kbcli cluster list-backup-policy mycluster" to show the cluster supported backup policies + # create a backup with specified backup policy, run "kbcli cluster list-backup-policies mycluster" to show the cluster supported backup policies kbcli dp backup mybackup --cluster mycluster --policy mypolicy # create a backup from a parent backup @@ -74,10 +74,10 @@ var ( listBackupExample = templates.Examples(` # list all backups - kbcli dp list-backup + kbcli dp list-backups # list all backups of specified cluster - kbcli dp list-backup --cluster mycluster + kbcli dp list-backups --cluster mycluster `) ) @@ -247,7 +247,7 @@ func newBackupCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *co customOutPut := func(opt *action.CreateOptions) { output := fmt.Sprintf("Backup %s created successfully, you can view the progress:", opt.Name) printer.PrintLine(output) - nextLine := fmt.Sprintf("\tkbcli dp list-backup %s -n %s", opt.Name, opt.Namespace) + nextLine := fmt.Sprintf("\tkbcli dp list-backups %s -n %s", opt.Name, opt.Namespace) printer.PrintLine(nextLine) } @@ -375,7 +375,7 @@ func newListBackupCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) o := action.NewListOptions(f, streams, types.BackupGVR()) clusterName := "" cmd := &cobra.Command{ - Use: "list-backup", + Use: "list-backups", Short: "List backups.", Aliases: []string{"ls-backups"}, Example: listBackupExample, diff --git a/pkg/cmd/dataprotection/backuppolicy.go b/pkg/cmd/dataprotection/backuppolicy.go index a2276743b..46a7fb9e9 100644 --- a/pkg/cmd/dataprotection/backuppolicy.go +++ b/pkg/cmd/dataprotection/backuppolicy.go @@ -50,7 +50,7 @@ import ( var ( listBackupPolicyExample = templates.Examples(` # list all backup policies - kbcli dp list-backup-policy + kbcli dp list-backup-policies # using short cmd to list backup policy of the specified cluster kbcli dp list-bp mycluster @@ -143,7 +143,7 @@ func newListBackupPolicyCmd(f cmdutil.Factory, streams genericclioptions.IOStrea o := action.NewListOptions(f, streams, types.BackupPolicyGVR()) clusterName := "" cmd := &cobra.Command{ - Use: "list-backup-policy", + Use: "list-backup-policies", Short: "List backup policies", Aliases: []string{"list-bp"}, Example: listBackupPolicyExample, diff --git a/pkg/cmd/dataprotection/backuppolicytemplate.go b/pkg/cmd/dataprotection/backuppolicytemplate.go index c4bd59c53..73c133383 100644 --- a/pkg/cmd/dataprotection/backuppolicytemplate.go +++ b/pkg/cmd/dataprotection/backuppolicytemplate.go @@ -43,8 +43,8 @@ func newListBackupPolicyTemplateCmd(f cmdutil.Factory, streams genericclioptions o := action.NewListOptions(f, streams, types.BackupPolicyTemplateGVR()) headers := []any{"NAME", "SERVICE-KIND", "STATUS", "CREATED-TIME"} cmd := &cobra.Command{ - Use: "list-backup-policy-template", - Short: "List backup policy template", + Use: "list-backup-policy-templates", + Short: "List backup policy templates", Aliases: []string{"list-bpt"}, Example: listBPTExample, ValidArgsFunction: util.ResourceNameCompletionFunc(f, o.GVR), diff --git a/pkg/cmd/dataprotection/datatprotection_test.go b/pkg/cmd/dataprotection/datatprotection_test.go index 7a6079eb9..18ba852d7 100644 --- a/pkg/cmd/dataprotection/datatprotection_test.go +++ b/pkg/cmd/dataprotection/datatprotection_test.go @@ -100,7 +100,7 @@ var _ = Describe("DataProtection", func() { tf.FakeDynamicClient = testing.FakeDynamicClient(objects...) } - It("list-action-set", func() { + It("list-action-sets", func() { By("fake client") initClient() @@ -124,7 +124,7 @@ var _ = Describe("DataProtection", func() { Expect(len(strings.Split(strings.Trim(out.String(), "\n"), "\n"))).Should(Equal(2)) }) - It("list-backup-policy", func() { + It("list-backup-policies", func() { By("fake client") defaultBackupPolicy := testing.FakeBackupPolicy(policyName, testing.ClusterName) policy2 := testing.FakeBackupPolicy("policy1", testing.ClusterName) @@ -132,7 +132,7 @@ var _ = Describe("DataProtection", func() { policy3.Namespace = "policy" initClient(defaultBackupPolicy, policy2, policy3) - By("test list-backup-policy cmd") + By("test list-backup-policies cmd") cmd := newListBackupPolicyCmd(tf, streams) Expect(cmd).ShouldNot(BeNil()) cmd.Run(cmd, nil) @@ -261,16 +261,16 @@ var _ = Describe("DataProtection", func() { Expect(completeForDeleteBackup(o, "")).Should(HaveOccurred()) }) - It("list-backup", func() { + It("list-backups", func() { cmd := newListBackupCommand(tf, streams) Expect(cmd).ShouldNot(BeNil()) - By("test list-backup cmd with no backup") + By("test list-backups cmd with no backup") tf.FakeDynamicClient = testing.FakeDynamicClient() o := action.NewListOptions(tf, streams, types.BackupGVR()) Expect(PrintBackupList(o)).Should(Succeed()) Expect(o.ErrOut.(*bytes.Buffer).String()).Should(ContainSubstring("No backups found")) - By("test list-backup") + By("test list-backups") backup1 := testing.FakeBackup("test1") backup1.Labels = map[string]string{ constant.AppInstanceLabelKey: "apecloud-mysql", @@ -338,7 +338,7 @@ var _ = Describe("DataProtection", func() { Expect(capturedOutput).Should(ContainSubstring(testing.BackupName)) }) - It("list-restore", func() { + It("list-restores", func() { By("fake client") initClient(testing.FakeRestore(testing.BackupName)) diff --git a/pkg/cmd/dataprotection/restore.go b/pkg/cmd/dataprotection/restore.go index c46198d7b..e0c439f77 100644 --- a/pkg/cmd/dataprotection/restore.go +++ b/pkg/cmd/dataprotection/restore.go @@ -54,7 +54,7 @@ var ( listRestoreExample = templates.Examples(` # list all restores - kbcli dp list-restore`) + kbcli dp list-restores`) ) type CreateRestoreOptions struct { @@ -155,7 +155,7 @@ func newListRestoreCommand(f cmdutil.Factory, streams genericiooptions.IOStreams o := action.NewListOptions(f, streams, types.RestoreGVR()) clusterName := "" cmd := &cobra.Command{ - Use: "list-restore", + Use: "list-restores", Short: "List restores.", Aliases: []string{"ls-restores"}, Example: listRestoreExample, From 1bd9e2c45cf6592ab21fcf56b51642fc03f9c148 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Fri, 6 Dec 2024 09:44:18 +0800 Subject: [PATCH 15/34] chore: support to create cluster with object flags (#513) (cherry picked from commit 3cc548f20e71bf85ce0d6bf3d51d63514d727652) --- pkg/cmd/cluster/create_subcmds.go | 16 +++++++++++---- pkg/cmd/cluster/create_util.go | 34 ++++++++++++++++++++++++++++++- pkg/util/flags/flags.go | 3 +++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/cluster/create_subcmds.go b/pkg/cmd/cluster/create_subcmds.go index 50939300f..db6baf5ba 100644 --- a/pkg/cmd/cluster/create_subcmds.go +++ b/pkg/cmd/cluster/create_subcmds.go @@ -127,10 +127,18 @@ func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { util.CheckErr(addCreateFlags(cmd, o.Factory, o.ChartInfo, t.String())) // Schedule policy - cmd.Flags().StringVar(&o.PodAntiAffinity, "pod-anti-affinity", "Preferred", "Pod anti-affinity type, one of: (Preferred, Required)") - cmd.Flags().StringArrayVar(&o.TopologyKeys, "topology-keys", nil, "Topology keys for affinity") - cmd.Flags().StringToStringVar(&o.NodeLabels, "node-labels", nil, "Node label selector") - cmd.Flags().StringSliceVar(&o.TolerationsRaw, "tolerations", nil, `Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"'`) + if cmd.Flag("pod-anti-affinity") == nil { + cmd.Flags().StringVar(&o.PodAntiAffinity, "pod-anti-affinity", "Preferred", "Pod anti-affinity type, one of: (Preferred, Required)") + } + if cmd.Flag("topology-keys") == nil { + cmd.Flags().StringArrayVar(&o.TopologyKeys, "topology-keys", nil, "Topology keys for affinity") + } + if cmd.Flag("node-labels") == nil { + cmd.Flags().StringToStringVar(&o.NodeLabels, "node-labels", nil, "Node label selector") + } + if cmd.Flag("tolerations") == nil { + cmd.Flags().StringSliceVar(&o.TolerationsRaw, "tolerations", nil, `Tolerations for cluster, such as "key=value:effect,key:effect", for example '"engineType=mongo:NoSchedule", "diskType:NoSchedule"'`) + } if cmd.Flag("tenancy") == nil { cmd.Flags().StringVar(&o.Tenancy, "tenancy", "SharedNode", "Tenancy options, one of: (SharedNode, DedicatedNode)") } diff --git a/pkg/cmd/cluster/create_util.go b/pkg/cmd/cluster/create_util.go index e52a86a45..4bbbad641 100644 --- a/pkg/cmd/cluster/create_util.go +++ b/pkg/cmd/cluster/create_util.go @@ -94,7 +94,39 @@ func getValuesFromFlags(fs *flag.FlagSet) map[string]interface{} { } values[strcase.LowerCamelCase(f.Name)] = val }) - return values + // normalize the values + return flattenToNestedMap(values) +} + +// flattenToNestedMap takes a flat map with keys that can contain dots to represent nesting, +// and returns a nested map structure. +func flattenToNestedMap(flatMap map[string]interface{}) map[string]interface{} { + nestedMap := make(map[string]interface{}) + for key, value := range flatMap { + setNestedValue(nestedMap, strings.Split(key, "."), value) + } + return nestedMap +} + +// setNestedValue sets the value in the nested map for the given key path. +func setNestedValue(m map[string]interface{}, keys []string, value interface{}) { + if len(keys) == 0 { + return + } + + currentKey := keys[0] + if len(keys) == 1 { + m[currentKey] = value + return + } + + subMap, exists := m[currentKey].(map[string]interface{}) + if !exists || subMap == nil { + subMap = make(map[string]interface{}) + m[currentKey] = subMap + } + + setNestedValue(subMap, keys[1:], value) } func resetEngineDefaultFlagsValue(fs *flag.FlagSet, engine string) { diff --git a/pkg/util/flags/flags.go b/pkg/util/flags/flags.go index 78a687275..d4332a60e 100644 --- a/pkg/util/flags/flags.go +++ b/pkg/util/flags/flags.go @@ -104,6 +104,9 @@ func castOrZero[T any](v any) T { func buildOneFlag(cmd *cobra.Command, k string, s *spec.Schema, isArray bool) error { name := strcase.KebabCase(k) + if cmd.Flag(name) != nil { + return nil + } tpe := typeString if len(s.Type) > 0 { tpe = s.Type[0] From 357646dd928f2ebc953641548cf70c83ea6594b2 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:00:25 +0800 Subject: [PATCH 16/34] chore: cluster restore support valid backup name (#509) (cherry picked from commit 96adf78cd913ae893677acd0bfe07b34b33f1487) --- pkg/cmd/dataprotection/restore.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/dataprotection/restore.go b/pkg/cmd/dataprotection/restore.go index e0c439f77..5f48d23d1 100644 --- a/pkg/cmd/dataprotection/restore.go +++ b/pkg/cmd/dataprotection/restore.go @@ -23,19 +23,21 @@ import ( "fmt" "strconv" - dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" - opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/constant" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/duration" "k8s.io/cli-runtime/pkg/genericiooptions" + "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes/scheme" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" + dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" + opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" + "github.com/apecloud/kubeblocks/pkg/constant" + "github.com/apecloud/kbcli/pkg/action" "github.com/apecloud/kbcli/pkg/cluster" "github.com/apecloud/kbcli/pkg/printer" @@ -71,6 +73,10 @@ func (o *CreateRestoreOptions) Validate() error { if o.RestoreSpec.BackupName == "" { return fmt.Errorf("must be specified one of the --backup ") } + backup, err := GetBackupByName(o.Dynamic, o.RestoreSpec.BackupName, o.Namespace) + if backup == nil || err != nil { + return fmt.Errorf("failed to find the backup, please confirm the specified name and namespace of backup. %s", err) + } if o.Name == "" { name, err := cluster.GenerateClusterName(o.Dynamic, o.Namespace) @@ -283,3 +289,11 @@ func PrintRestoreDescribe(o *DescribeDPOptions, obj *dpv1alpha1.Restore) error { printer.PrintAllWarningEvents(events, o.Out) return nil } + +func GetBackupByName(dynamic dynamic.Interface, name string, namespace string) (*dpv1alpha1.Backup, error) { + backup := &dpv1alpha1.Backup{} + if err := util.GetK8SClientObject(dynamic, backup, types.BackupGVR(), namespace, name); err != nil { + return nil, err + } + return backup, nil +} From 33c5be982d086bb20b800d33f343a7100ae8275d Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:21:38 +0800 Subject: [PATCH 17/34] feat: improve cluster list (#516) (cherry picked from commit 788adb4a294e0581fd7754ce4b3fc2de48e81927) --- docs/user_docs/cli/kbcli_cluster_list.md | 1 + pkg/action/list.go | 1 + pkg/cluster/printer.go | 154 +++++++++++++--- pkg/cmd/cluster/list.go | 6 +- pkg/cmd/cluster/list_test.go | 223 +++++++++++++++++------ 5 files changed, 304 insertions(+), 81 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_list.md b/docs/user_docs/cli/kbcli_cluster_list.md index 84d93dc77..53f15f0e8 100644 --- a/docs/user_docs/cli/kbcli_cluster_list.md +++ b/docs/user_docs/cli/kbcli_cluster_list.md @@ -36,6 +36,7 @@ kbcli cluster list [NAME] [flags] -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. --show-labels When printing, show all labels as the last column (default hide labels column) + --status string Filter objects by given status. ``` ### Options inherited from parent commands diff --git a/pkg/action/list.go b/pkg/action/list.go index 4f21cf210..42688a616 100644 --- a/pkg/action/list.go +++ b/pkg/action/list.go @@ -67,6 +67,7 @@ type ListOptions struct { // only return the result to caller. Print bool SortBy string + Status string genericiooptions.IOStreams } diff --git a/pkg/cluster/printer.go b/pkg/cluster/printer.go index 74c41ab92..663fb7854 100644 --- a/pkg/cluster/printer.go +++ b/pkg/cluster/printer.go @@ -21,6 +21,7 @@ package cluster import ( "io" + "sort" "strings" corev1 "k8s.io/api/core/v1" @@ -41,38 +42,38 @@ const ( ) type PrinterOptions struct { - ShowLabels bool + ShowLabels bool + StatusFilter string } type tblInfo struct { header []interface{} - addRow func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) + addRow func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} getOptions GetOptions } var mapTblInfo = map[PrintType]tblInfo{ PrintClusters: { - header: []interface{}{"NAME", "NAMESPACE", "CLUSTER-DEFINITION", "VERSION", "TERMINATION-POLICY", "STATUS", "CREATED-TIME"}, - addRow: func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { + header: []interface{}{"NAME", "NAMESPACE", "CLUSTER-DEFINITION", "TERMINATION-POLICY", "STATUS", "CREATED-TIME"}, + addRow: func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { c := objs.GetClusterInfo() - info := []interface{}{c.Name, c.Namespace, c.ClusterDefinition, c.ClusterVersion, c.TerminationPolicy, c.Status, c.CreatedTime} + info := []interface{}{c.Name, c.Namespace, c.ClusterDefinition, c.TerminationPolicy, c.Status, c.CreatedTime} if opt.ShowLabels { info = append(info, c.Labels) } - - tbl.AddRow(info...) + return [][]interface{}{info} }, getOptions: GetOptions{}, }, PrintWide: { - header: []interface{}{"NAME", "NAMESPACE", "CLUSTER-DEFINITION", "VERSION", "TERMINATION-POLICY", "STATUS", "INTERNAL-ENDPOINTS", "EXTERNAL-ENDPOINTS", "CREATED-TIME"}, - addRow: func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { + header: []interface{}{"NAME", "NAMESPACE", "CLUSTER-DEFINITION", "TERMINATION-POLICY", "STATUS", "INTERNAL-ENDPOINTS", "EXTERNAL-ENDPOINTS", "CREATED-TIME"}, + addRow: func(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { c := objs.GetClusterInfo() - info := []interface{}{c.Name, c.Namespace, c.ClusterDefinition, c.ClusterVersion, c.TerminationPolicy, c.Status, c.InternalEP, c.ExternalEP, c.CreatedTime} + info := []interface{}{c.Name, c.Namespace, c.ClusterDefinition, c.TerminationPolicy, c.Status, c.InternalEP, c.ExternalEP, c.CreatedTime} if opt.ShowLabels { info = append(info, c.Labels) } - tbl.AddRow(info...) + return [][]interface{}{info} }, getOptions: GetOptions{WithClusterDef: Maybe, WithService: Need, WithPod: Need}, }, @@ -100,13 +101,15 @@ var mapTblInfo = map[PrintType]tblInfo{ // Printer prints cluster info type Printer struct { - tbl *printer.TablePrinter - opt *PrinterOptions - tblInfo + tbl *printer.TablePrinter + opt *PrinterOptions + tblInfo tblInfo + pt PrintType + rows [][]interface{} } func NewPrinter(out io.Writer, printType PrintType, opt *PrinterOptions) *Printer { - p := &Printer{tbl: printer.NewTablePrinter(out)} + p := &Printer{tbl: printer.NewTablePrinter(out), pt: printType} p.tblInfo = mapTblInfo[printType] if opt == nil { @@ -123,48 +126,147 @@ func NewPrinter(out io.Writer, printType PrintType, opt *PrinterOptions) *Printe } func (p *Printer) AddRow(objs *ClusterObjects) { - p.addRow(p.tbl, objs, p.opt) + lines := p.tblInfo.addRow(p.tbl, objs, p.opt) + p.rows = append(p.rows, lines...) } func (p *Printer) Print() { + if p.pt == PrintClusters || p.pt == PrintWide { + p.filterByStatus() + p.sortRows() + } + + for _, row := range p.rows { + p.tbl.AddRow(row...) + } + p.tbl.Print() } func (p *Printer) GetterOptions() GetOptions { - return p.getOptions + return p.tblInfo.getOptions +} + +func (p *Printer) filterByStatus() { + if p.opt.StatusFilter == "" { + return + } + + statusIndex := 4 + + var filtered [][]interface{} + for _, r := range p.rows { + statusVal, _ := r[statusIndex].(string) + if strings.EqualFold(statusVal, p.opt.StatusFilter) { + filtered = append(filtered, r) + } + } + p.rows = filtered +} + +// sortRows Sort By namespace(1), clusterDef(2), status(4), name(0) +func (p *Printer) sortRows() { + // for PrintClusters and PrintWide + // NAME(0), NAMESPACE(1), CLUSTER-DEFINITION(2), STATUS(4) + sort.Slice(p.rows, func(i, j int) bool { + ri, rj := p.rows[i], p.rows[j] + + nsI, _ := ri[1].(string) + nsJ, _ := rj[1].(string) + if nsI != nsJ { + return nsI < nsJ + } + + cdI, _ := ri[2].(string) + cdJ, _ := rj[2].(string) + if cdI != cdJ { + return cdI < cdJ + } + + statusI, _ := ri[4].(string) + statusJ, _ := rj[4].(string) + if statusI != statusJ { + return compareStatus(statusI, statusJ) + } + + nameI, _ := ri[0].(string) + nameJ, _ := rj[0].(string) + return nameI < nameJ + }) +} + +// compareStatus compares statuses based on the desired order +func compareStatus(status1, status2 string) bool { + statusOrder := map[string]int{ + "Creating": 1, + "Running": 2, + "Updating": 3, + "Stopping": 4, + "Stopped": 5, + "Deleting": 6, + "Failed": 7, + "Abnormal": 8, + } + + order1, ok1 := statusOrder[status1] + order2, ok2 := statusOrder[status2] + + // unknown is the last + if !ok1 && !ok2 { + return status1 < status2 + } + if !ok1 { + return false + } + if !ok2 { + return true + } + + return order1 < order2 } -func AddLabelRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { +func AddLabelRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { c := objs.GetClusterInfo() info := []interface{}{c.Name, c.Namespace} if opt.ShowLabels { labels := strings.ReplaceAll(c.Labels, ",", "\n") info = append(info, labels) } - tbl.AddRow(info...) + return [][]interface{}{info} } -func AddComponentRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { +func AddComponentRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { components := objs.GetComponentInfo() + var rows [][]interface{} for _, c := range components { - tbl.AddRow(c.Name, c.NameSpace, c.Cluster, c.ComponentDef, c.Image) + row := []interface{}{c.Name, c.NameSpace, c.Cluster, c.ComponentDef, c.Image} + rows = append(rows, row) } + return rows } -func AddInstanceRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { +func AddInstanceRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { instances := objs.GetInstanceInfo() + var rows [][]interface{} for _, instance := range instances { - tbl.AddRow(instance.Name, instance.Namespace, instance.Cluster, instance.Component, + row := []interface{}{ + instance.Name, instance.Namespace, instance.Cluster, instance.Component, instance.Status, instance.Role, instance.AccessMode, instance.AZ, instance.CPU, instance.Memory, - BuildStorageSize(instance.Storage), instance.Node, instance.CreatedTime) + BuildStorageSize(instance.Storage), instance.Node, instance.CreatedTime, + } + rows = append(rows, row) } + return rows } -func AddEventRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) { +func AddEventRow(tbl *printer.TablePrinter, objs *ClusterObjects, opt *PrinterOptions) [][]interface{} { events := util.SortEventsByLastTimestamp(objs.Events, "") + var rows [][]interface{} for _, event := range *events { e := event.(*corev1.Event) - tbl.AddRow(e.Namespace, util.GetEventTimeStr(e), e.Type, e.Reason, util.GetEventObject(e), e.Message) + row := []interface{}{e.Namespace, util.GetEventTimeStr(e), e.Type, e.Reason, util.GetEventObject(e), e.Message} + rows = append(rows, row) } + return rows } diff --git a/pkg/cmd/cluster/list.go b/pkg/cmd/cluster/list.go index b15216528..64f710bb4 100644 --- a/pkg/cmd/cluster/list.go +++ b/pkg/cmd/cluster/list.go @@ -93,6 +93,7 @@ func NewListCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Co }, } o.AddFlags(cmd) + cmd.Flags().StringVar(&o.Status, "status", "", "Filter objects by given status.") return cmd } @@ -151,7 +152,7 @@ func NewListEventsCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *co } func run(o *action.ListOptions, printType cluster.PrintType) error { - // if format is JSON or YAML, use default printer to output the result. + // if format is JSON or YAML, use default printer. if o.Format == printer.JSON || o.Format == printer.YAML { _, err := o.Run() return err @@ -185,7 +186,8 @@ func run(o *action.ListOptions, printType cluster.PrintType) error { } opt := &cluster.PrinterOptions{ - ShowLabels: o.ShowLabels, + ShowLabels: o.ShowLabels, + StatusFilter: o.Status, } p := cluster.NewPrinter(o.IOStreams.Out, printType, opt) diff --git a/pkg/cmd/cluster/list_test.go b/pkg/cmd/cluster/list_test.go index 340d9846a..14c94fc56 100644 --- a/pkg/cmd/cluster/list_test.go +++ b/pkg/cmd/cluster/list_test.go @@ -24,9 +24,9 @@ import ( "net/http" "strings" - kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -37,6 +37,7 @@ import ( clientfake "k8s.io/client-go/rest/fake" cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" "github.com/apecloud/kbcli/pkg/cluster" @@ -45,12 +46,6 @@ import ( ) var _ = Describe("list", func() { - var ( - streams genericiooptions.IOStreams - out *bytes.Buffer - tf *cmdtesting.TestFactory - ) - const ( namespace = "test" clusterName = "test" @@ -59,13 +54,32 @@ var _ = Describe("list", func() { verticalScalingReason = "VerticalScaling" ) + var ( + streams genericiooptions.IOStreams + out *bytes.Buffer + tf *cmdtesting.TestFactory + ) + + // httpResp returns a *http.Response for the given runtime.Object. + httpResp := func(codec runtime.Codec, obj runtime.Object) *http.Response { + return &http.Response{ + StatusCode: http.StatusOK, + Header: cmdtesting.DefaultHeader(), + Body: cmdtesting.ObjBody(codec, obj), + } + } + BeforeEach(func() { streams, _, out, _ = genericiooptions.NewTestIOStreams() tf = testing.NewTestFactory(namespace) - _ = kbappsv1.AddToScheme(scheme.Scheme) + // Prepare schemes + Expect(kbappsv1.AddToScheme(scheme.Scheme)).Should(Succeed()) codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - cluster := testing.FakeCluster(clusterName, namespace, metav1.Condition{ + + By("Creating test clusters and pods") + // Prepare test data + baseCluster := testing.FakeCluster(clusterName, namespace, metav1.Condition{ Type: appsv1alpha1.ConditionTypeApplyResources, Status: metav1.ConditionFalse, Reason: "HorizontalScaleFailed", @@ -76,91 +90,194 @@ var _ = Describe("list", func() { Reason: verticalScalingReason, }) clusterWithVerticalScaling.Status.Phase = kbappsv1.UpdatingClusterPhase + clusterWithAbnormalPhase := testing.FakeCluster(clusterName2, namespace) clusterWithAbnormalPhase.Status.Phase = kbappsv1.AbnormalClusterPhase + pods := testing.FakePods(3, namespace, clusterName) - httpResp := func(obj runtime.Object) *http.Response { - return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, obj)} - } + By("Configuring the fake REST client responses") tf.UnstructuredClient = &clientfake.RESTClient{ GroupVersion: schema.GroupVersion{Group: types.AppsAPIGroup, Version: types.AppsAPIVersion}, NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, Client: clientfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { urlPrefix := "/api/v1/namespaces/" + namespace - return map[string]*http.Response{ - "/namespaces/" + namespace + "/clusters": httpResp(&kbappsv1.ClusterList{Items: []kbappsv1.Cluster{*cluster}}), - "/namespaces/" + namespace + "/clusters/" + clusterName: httpResp(cluster), - "/namespaces/" + namespace + "/clusters/" + clusterName1: httpResp(clusterWithVerticalScaling), - "/namespaces/" + namespace + "/clusters/" + clusterName2: httpResp(clusterWithAbnormalPhase), - "/namespaces/" + namespace + "/secrets": httpResp(testing.FakeSecrets(namespace, clusterName)), - "/api/v1/nodes/" + testing.NodeName: httpResp(testing.FakeNode()), - urlPrefix + "/services": httpResp(&corev1.ServiceList{}), - urlPrefix + "/secrets": httpResp(testing.FakeSecrets(namespace, clusterName)), - urlPrefix + "/pods": httpResp(pods), - urlPrefix + "/events": httpResp(testing.FakeEvents()), - }[req.URL.Path], nil + pathToResponse := map[string]*http.Response{ + "/namespaces/" + namespace + "/clusters": httpResp(codec, &kbappsv1.ClusterList{Items: []kbappsv1.Cluster{*baseCluster}}), + "/namespaces/" + namespace + "/clusters/" + clusterName: httpResp(codec, baseCluster), + "/namespaces/" + namespace + "/clusters/" + clusterName1: httpResp(codec, clusterWithVerticalScaling), + "/namespaces/" + namespace + "/clusters/" + clusterName2: httpResp(codec, clusterWithAbnormalPhase), + "/namespaces/" + namespace + "/secrets": httpResp(codec, testing.FakeSecrets(namespace, clusterName)), + "/api/v1/nodes/" + testing.NodeName: httpResp(codec, testing.FakeNode()), + urlPrefix + "/services": httpResp(codec, &corev1.ServiceList{}), + urlPrefix + "/secrets": httpResp(codec, testing.FakeSecrets(namespace, clusterName)), + urlPrefix + "/pods": httpResp(codec, pods), + urlPrefix + "/events": httpResp(codec, testing.FakeEvents()), + } + return pathToResponse[req.URL.Path], nil }), } tf.Client = tf.UnstructuredClient - tf.FakeDynamicClient = testing.FakeDynamicClient(cluster, clusterWithVerticalScaling, clusterWithAbnormalPhase, testing.FakeClusterDef()) + tf.FakeDynamicClient = testing.FakeDynamicClient( + baseCluster, + clusterWithVerticalScaling, + clusterWithAbnormalPhase, + testing.FakeClusterDef(), + ) }) AfterEach(func() { + By("Cleaning up the test factory") tf.Cleanup() }) - It("list", func() { + // Helper to run command and return output + runCmd := func(cmd *cobra.Command, args ...string) string { + out.Reset() + cmd.Run(cmd, args) + return out.String() + } + + It("list clusters by name", func() { + By("Running list command with cluster names") cmd := NewListCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + output := runCmd(cmd, clusterName, clusterName1, clusterName2) - cmd.Run(cmd, []string{clusterName, clusterName1, clusterName2}) - Expect(out.String()).Should(ContainSubstring(clusterName)) - Expect(out.String()).Should(ContainSubstring(string(appsv1alpha1.UpdatingClusterPhase))) - Expect(out.String()).Should(ContainSubstring(cluster.ConditionsError)) - Expect(out.String()).Should(ContainSubstring(string(appsv1alpha1.AbnormalClusterPhase))) + By("Checking output for expected clusters and statuses") + Expect(output).Should(ContainSubstring(clusterName)) + Expect(output).Should(ContainSubstring(string(appsv1alpha1.UpdatingClusterPhase))) + Expect(output).Should(ContainSubstring(cluster.ConditionsError)) + Expect(output).Should(ContainSubstring(string(appsv1alpha1.AbnormalClusterPhase))) }) - It("list instances", func() { + It("list instances for a specific cluster", func() { + By("Running list-instances command for a given cluster") cmd := NewListInstancesCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + output := runCmd(cmd, clusterName) - cmd.Run(cmd, []string{clusterName}) - Expect(out.String()).Should(ContainSubstring(testing.NodeName)) + By("Checking output for expected node name in instances list") + Expect(output).Should(ContainSubstring(testing.NodeName)) }) - It("list components", func() { + It("list components for a specific cluster", func() { + By("Running list-components command for a given cluster") cmd := NewListComponentsCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + output := runCmd(cmd, clusterName) - cmd.Run(cmd, []string{clusterName}) - Expect(out.String()).Should(ContainSubstring(testing.ComponentName)) + By("Checking output for expected component name") + Expect(output).Should(ContainSubstring(testing.ComponentName)) }) - It("list events", func() { + It("list events for a specific cluster", func() { + By("Running list-events command for a given cluster") cmd := NewListEventsCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + output := runCmd(cmd, clusterName) - cmd.Run(cmd, []string{clusterName}) - Expect(len(strings.Split(out.String(), "\n")) > 1).Should(BeTrue()) + By("Verifying that multiple events are returned") + Expect(len(strings.Split(strings.TrimSpace(output), "\n")) > 1).Should(BeTrue()) }) - It("output wide", func() { + It("output wide with cluster name", func() { + By("Setting output format to wide") cmd := NewListCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + Expect(cmd.Flags().Set("output", "wide")).Should(Succeed()) + + By("Running the command with a specific cluster") + output := runCmd(cmd, clusterName) + Expect(output).Should(ContainSubstring(clusterName)) + }) + It("output wide without specifying cluster names", func() { + By("Setting output format to wide without arguments") + cmd := NewListCmd(tf, streams) Expect(cmd.Flags().Set("output", "wide")).Should(Succeed()) - cmd.Run(cmd, []string{clusterName}) - Expect(out.String()).Should(ContainSubstring(clusterName)) + + output := runCmd(cmd) + Expect(output).Should(ContainSubstring(clusterName)) }) - It("output wide without args", func() { + It("should list clusters sorted and filtered by status", func() { + By("Preparing multiple clusters for sorting and filtering test") + clusterA := testing.FakeCluster("clusterA", "ns1", metav1.Condition{ + Type: appsv1alpha1.ConditionTypeReady, + Status: metav1.ConditionTrue, + Reason: "Ready", + }) + clusterA.Status.Phase = kbappsv1.RunningClusterPhase + clusterA.Spec.ClusterDef = "cd-mysql" + + clusterB := testing.FakeCluster("clusterB", "ns1", metav1.Condition{ + Type: appsv1alpha1.ConditionTypeReady, + Status: metav1.ConditionTrue, + Reason: "Ready", + }) + clusterB.Status.Phase = kbappsv1.CreatingClusterPhase + clusterB.Spec.ClusterDef = "cd-mysql" + + clusterC := testing.FakeCluster("clusterC", "ns2", metav1.Condition{ + Type: appsv1alpha1.ConditionTypeReady, + Status: metav1.ConditionFalse, + Reason: "Scaling", + }) + clusterC.Status.Phase = kbappsv1.UpdatingClusterPhase + clusterC.Spec.ClusterDef = "cd-postgres" + + codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) + tf.UnstructuredClient = &clientfake.RESTClient{ + GroupVersion: schema.GroupVersion{Group: types.AppsAPIGroup, Version: types.AppsAPIVersion}, + NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer, + + Client: clientfake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + pathToResponse := map[string]*http.Response{ + "/namespaces/" + namespace + "/clusters": httpResp(codec, &kbappsv1.ClusterList{ + Items: []kbappsv1.Cluster{*clusterA, *clusterB, *clusterC}, + }), + } + return pathToResponse[req.URL.Path], nil + }), + } + + tf.Client = tf.UnstructuredClient + + tf.FakeDynamicClient = testing.FakeDynamicClient(clusterA, clusterB, clusterC, testing.FakeClusterDef()) + + By("Listing all clusters across all namespaces") cmd := NewListCmd(tf, streams) - Expect(cmd).ShouldNot(BeNil()) + output := runCmd(cmd) - Expect(cmd.Flags().Set("output", "wide")).Should(Succeed()) - cmd.Run(cmd, []string{}) - Expect(out.String()).Should(ContainSubstring(clusterName)) + lines := strings.Split(strings.TrimSpace(output), "\n") + Expect(lines).To(ContainElements( + ContainSubstring("clusterB"), // Creating + ContainSubstring("clusterA"), // Running + ContainSubstring("clusterC"), // Updating + )) + + // Extract indices for validation + var bIndex, aIndex, cIndex int + for i, line := range lines { + switch { + case strings.Contains(line, "clusterB"): + bIndex = i + case strings.Contains(line, "clusterA"): + aIndex = i + case strings.Contains(line, "clusterC"): + cIndex = i + } + } + + Expect(bIndex).To(BeNumerically("<", aIndex)) // Creating < Running + Expect(aIndex).To(BeNumerically("<", cIndex)) // Running < Updating + + By("Filtering clusters by status Running") + out.Reset() + cmd = NewListCmd(tf, streams) + Expect(cmd.Flags().Set("status", "Running")).Should(Succeed()) + output = runCmd(cmd) + + // Only clusterA should remain (Running) + Expect(output).To(ContainSubstring("clusterA")) + Expect(output).NotTo(ContainSubstring("clusterB")) + Expect(output).NotTo(ContainSubstring("clusterC")) }) }) From 84d749353512a5b4873ce9f68a49869e4db7cf8f Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:21:30 +0800 Subject: [PATCH 18/34] feat: addon support clean sub-resource of multi-versions (#512) Co-authored-by: yipeng1030 (cherry picked from commit e18cf6b2d534ed10ae7ee8a04461abe19944caa4) --- docs/user_docs/cli/cli.md | 1 + docs/user_docs/cli/kbcli_addon.md | 1 + pkg/cmd/addon/addon.go | 1 + pkg/cmd/addon/delete_resource.go | 287 ++++++++++++++++++++++++++ pkg/cmd/addon/delete_resource_test.go | 267 ++++++++++++++++++++++++ pkg/cmd/addon/uninstall.go | 29 ++- pkg/cmd/addon/uninstall_test.go | 58 ++++++ 7 files changed, 643 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/addon/delete_resource.go create mode 100644 pkg/cmd/addon/delete_resource_test.go create mode 100644 pkg/cmd/addon/uninstall_test.go diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 6786501ae..708989538 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -8,6 +8,7 @@ sidebar_position: 1 Addon command. +* [kbcli addon delete-resources-with-version](kbcli_addon_delete-resources-with-version.md) - Delete the sub-resources of specified addon and versions * [kbcli addon describe](kbcli_addon_describe.md) - Describe an addon specification. * [kbcli addon disable](kbcli_addon_disable.md) - Disable an addon. * [kbcli addon enable](kbcli_addon_enable.md) - Enable an addon. diff --git a/docs/user_docs/cli/kbcli_addon.md b/docs/user_docs/cli/kbcli_addon.md index 83a423e66..c5a94004d 100644 --- a/docs/user_docs/cli/kbcli_addon.md +++ b/docs/user_docs/cli/kbcli_addon.md @@ -37,6 +37,7 @@ Addon command. ### SEE ALSO +* [kbcli addon delete-resources-with-version](kbcli_addon_delete-resources-with-version.md) - Delete the sub-resources of specified addon and versions * [kbcli addon describe](kbcli_addon_describe.md) - Describe an addon specification. * [kbcli addon disable](kbcli_addon_disable.md) - Disable an addon. * [kbcli addon enable](kbcli_addon_enable.md) - Enable an addon. diff --git a/pkg/cmd/addon/addon.go b/pkg/cmd/addon/addon.go index 2b5c3f9d8..777fb6827 100644 --- a/pkg/cmd/addon/addon.go +++ b/pkg/cmd/addon/addon.go @@ -116,6 +116,7 @@ func NewAddonCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.C newInstallCmd(f, streams), newUninstallCmd(f, streams), newUpgradeCmd(f, streams), + newDeleteResourcesCmd(f, streams), ) return cmd } diff --git a/pkg/cmd/addon/delete_resource.go b/pkg/cmd/addon/delete_resource.go new file mode 100644 index 000000000..9f0ad0aa4 --- /dev/null +++ b/pkg/cmd/addon/delete_resource.go @@ -0,0 +1,287 @@ +/* +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 addon + +import ( + "context" + "fmt" + "regexp" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericiooptions" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + kbv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + v1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" + + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" +) + +const ( + versionPattern = `(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?(?:\+[a-zA-Z0-9.-]+)?)$` + helmReleaseNameKey = "meta.helm.sh/release-name" + helmReleaseNamePrefix = "kb-addon-" + helmResourcePolicyKey = "helm.sh/resource-policy" + helmResourcePolicyKeep = "keep" +) + +// Resource types to be processed for deletion +var resourceToDelete = []schema.GroupVersionResource{ + types.CompDefGVR(), + types.ConfigmapGVR(), + types.ConfigConstraintGVR(), + types.ConfigConstraintOldGVR(), +} + +var addonDeleteResourcesExample = templates.Examples(` + # Delete specific versions of redis addon resources + kbcli addon delete-resources-with-version redis --versions=0.9.1,0.9.2 + + # Delete all unused and outdated resources of redis addon + kbcli addon delete-resources-with-version redis --all-unused-versions=true +`) + +type deleteResourcesOption struct { + *baseOption + name string + versions []string + allUnusedVersions bool + + // if set to true, the newest resources will also be deleted, and this flag is not open to user, only used to delete all the resources while addon uninstalling. + deleteNewestVersion bool +} + +func newDeleteResourcesOption(f cmdutil.Factory, streams genericiooptions.IOStreams) *deleteResourcesOption { + return &deleteResourcesOption{ + baseOption: &baseOption{ + Factory: f, + IOStreams: streams, + GVR: types.AddonGVR(), + }, + allUnusedVersions: false, + deleteNewestVersion: false, + } +} + +func newDeleteResourcesCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := newDeleteResourcesOption(f, streams) + cmd := &cobra.Command{ + Use: "delete-resources-with-version", + Short: "Delete the sub-resources of specified addon and versions", + Example: addonDeleteResourcesExample, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, o.GVR), + Run: func(cmd *cobra.Command, args []string) { + util.CheckErr(o.baseOption.complete()) + util.CheckErr(o.Complete(args)) + util.CheckErr(o.Validate()) + util.CheckErr(o.Run()) + }, + } + cmd.Flags().StringSliceVar(&o.versions, "versions", nil, "Specify the versions of resources to delete.") + cmd.Flags().BoolVar(&o.allUnusedVersions, "all-unused-versions", false, "If set to true, all the resources "+ + "which are not currently used and not with the newest version will be deleted.") + return cmd +} + +func (o *deleteResourcesOption) Complete(args []string) error { + if args == nil { + return fmt.Errorf("no addon provided; please specify the name of addon") + } + o.name = args[0] + versions, err := o.getExistedVersions(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve versions for resource %s: %v", o.name, err) + } + newestVersion, err := o.getNewestVersion(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve version for resource %s: %v", o.name, err) + } + versionInUse, err := o.getInUseVersions(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve versions for resource %s: %v", o.name, err) + } + if o.allUnusedVersions { + for k := range versions { + if !versionInUse[k] && k != newestVersion { + o.versions = append(o.versions, k) + } + } + if o.deleteNewestVersion { + o.versions = append(o.versions, newestVersion) + } + } + return nil +} + +func (o *deleteResourcesOption) Validate() error { + if o.allUnusedVersions { + return nil + } + if o.versions == nil { + return fmt.Errorf("no versions specified and --all-versions flag is not set; please specify versions or set --all-unused-versions to true") + } + versions, err := o.getExistedVersions(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve versions for resource %s: %v", o.name, err) + } + newestVersion, err := o.getNewestVersion(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve version for resource %s: %v", o.name, err) + } + versionsInUse, err := o.getInUseVersions(o.name) + if err != nil { + return fmt.Errorf("failed to retrieve versions for resource %s: %v", o.name, err) + } + for _, v := range o.versions { + if !versions[v] { + return fmt.Errorf("specified version %s does not exist for resource %s", v, o.name) + } + if !o.deleteNewestVersion && v == newestVersion { + return fmt.Errorf("specified version %s cannot be deleted as it is the newest version", v) + } + if versionsInUse[v] { + return fmt.Errorf("specified version %s cannot be deleted as it is currently used", v) + } + } + return nil +} + +func (o *deleteResourcesOption) Run() error { + return o.cleanSubResources(o.name, o.versions) +} + +// extractVersion extracts the version from a resource name using the provided regex pattern. +func extractVersion(name string) string { + versionRegex := regexp.MustCompile(versionPattern) + return versionRegex.FindString(name) +} + +// getExistedVersions get all the existed versions of specified addon by listing the componentDef. +func (o *deleteResourcesOption) getExistedVersions(addonName string) (map[string]bool, error) { + resources, err := o.Dynamic.Resource(types.CompDefGVR()).Namespace(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to list resources for %s: %w", types.CompDefGVR(), err) + } + + totalVersions := make(map[string]bool) + for _, item := range resources.Items { + annotations := item.GetAnnotations() + if annotations[helmReleaseNameKey] != helmReleaseNamePrefix+addonName { + continue + } + + version := extractVersion(item.GetName()) + if version != "" { + totalVersions[version] = true + } + } + + return totalVersions, nil +} + +// getNewestVersion retrieves the newest version of the addon +func (o *deleteResourcesOption) getNewestVersion(addonName string) (string, error) { + addon := &v1alpha1.Addon{} + err := util.GetK8SClientObject(o.Dynamic, addon, types.AddonGVR(), "", addonName) + if err != nil { + return "", fmt.Errorf("failed to get addon: %w", err) + } + return getAddonVersion(addon), nil +} + +// getInUseVersions retrieves the versions of resources that are currently in use. +func (o *deleteResourcesOption) getInUseVersions(addonName string) (map[string]bool, error) { + InUseVersions := map[string]bool{} + labelSelector := util.BuildClusterLabel("", []string{addonName}) + clusterList, err := o.Dynamic.Resource(types.ClusterGVR()).Namespace(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelector}) + if err != nil { + return nil, fmt.Errorf("failed to list clusters: %w", err) + } + if clusterList != nil && len(clusterList.Items) > 0 { + for _, item := range clusterList.Items { + var cluster kbv1alpha1.Cluster + if err = runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &cluster); err != nil { + return nil, fmt.Errorf("failed to convert cluster to structured object: %w", err) + } + for _, spec := range cluster.Spec.ComponentSpecs { + version := extractVersion(spec.ComponentDef) + if version != "" { + InUseVersions[version] = true + } + } + } + } + + return InUseVersions, nil +} + +// cleanSubResources Cleans up specified addon resources. +func (o *deleteResourcesOption) cleanSubResources(addon string, versionsToDelete []string) error { + versions := make(map[string]bool) + for _, v := range versionsToDelete { + versions[v] = true + } + + // Iterate through each resource type + for _, gvr := range resourceToDelete { + // List all resources of the current type + resources, err := o.Dynamic.Resource(gvr).Namespace(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{}) + if err != nil { + return fmt.Errorf("failed to list resources for %s: %w", gvr.Resource, err) + } + + // Process each resource in the list + for _, item := range resources.Items { + // Skip resources not belong to specified addon + annotations := item.GetAnnotations() + if annotations[helmReleaseNameKey] != helmReleaseNamePrefix+addon { + continue + } + + // Skip resources of other versions. + name := item.GetName() + extractedVersion := extractVersion(name) + if extractedVersion == "" || !versions[extractedVersion] { + continue + } + + // Skip resources if the resource doesn't have the annotation helm.sh/resource-policy: keep + if annotations[helmResourcePolicyKey] != helmResourcePolicyKeep { + continue + } + + // Delete the resource if it passes all checks, and only print msg when user calling. + if !o.deleteNewestVersion { + err := o.Dynamic.Resource(gvr).Namespace(item.GetNamespace()).Delete(context.Background(), name, metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("failed to delete resource %s/%s: %w", gvr.Resource, name, err) + } + fmt.Fprintf(o.Out, "Deleted resource: %s/%s\n", gvr.Resource, name) + } + } + } + + return nil +} diff --git a/pkg/cmd/addon/delete_resource_test.go b/pkg/cmd/addon/delete_resource_test.go new file mode 100644 index 000000000..012bb3208 --- /dev/null +++ b/pkg/cmd/addon/delete_resource_test.go @@ -0,0 +1,267 @@ +/* +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 addon + +import ( + "bytes" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + 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" + clientfake "k8s.io/client-go/rest/fake" + clienttesting "k8s.io/client-go/testing" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + + kbv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + v1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" + "github.com/apecloud/kubeblocks/pkg/constant" + + "github.com/apecloud/kbcli/pkg/testing" + "github.com/apecloud/kbcli/pkg/types" +) + +var _ = Describe("delete_resources_with_version test", func() { + var ( + streams genericiooptions.IOStreams + tf *cmdtesting.TestFactory + bufOut, bufErr *bytes.Buffer + addonName = "redis" + newestVersion = "0.9.3" + inUseVersion = "0.9.2" + unusedVersion = "0.9.1" + testAddonGVR = types.AddonGVR() + testCompDefGVR = types.CompDefGVR() + testClusterGVR = types.ClusterGVR() + testUnusedConfigGVR = types.ConfigmapGVR() + testResourceAnnotKey = helmReleaseNameKey + ) + + // Helper functions to create fake resources + createAddon := func(name, version string) *unstructured.Unstructured { + addon := &v1alpha1.Addon{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + types.AddonVersionLabelKey: version, + }, + }, + Spec: v1alpha1.AddonSpec{ + Version: version, + }, + } + obj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(addon) + u := &unstructured.Unstructured{Object: obj} + u.SetGroupVersionKind(testAddonGVR.GroupVersion().WithKind("Addon")) + return u + } + + createComponentDef := func(name, addon, version string) *unstructured.Unstructured { + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": testCompDefGVR.GroupVersion().String(), + "kind": "ComponentDefinition", + "metadata": map[string]interface{}{ + "name": name + "-" + version, + "annotations": map[string]interface{}{ + testResourceAnnotKey: helmReleaseNamePrefix + addon, + helmResourcePolicyKey: helmResourcePolicyKeep, + }, + }, + }, + } + return u + } + + createCluster := func(name, addon, version string) *unstructured.Unstructured { + cluster := &kbv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + constant.ClusterDefLabelKey: addon, + }, + }, + Spec: kbv1alpha1.ClusterSpec{ + ComponentSpecs: []kbv1alpha1.ClusterComponentSpec{ + { + ComponentDef: fmt.Sprintf("redis-%s", version), + }, + }, + }, + } + obj, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(cluster) + u := &unstructured.Unstructured{Object: obj} + u.SetGroupVersionKind(testClusterGVR.GroupVersion().WithKind("Cluster")) + return u + } + + createUnusedConfig := func(addon, version string) *unstructured.Unstructured { + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": testUnusedConfigGVR.GroupVersion().String(), + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "config-" + unusedVersion, + "annotations": map[string]interface{}{ + testResourceAnnotKey: helmReleaseNamePrefix + addon, + helmResourcePolicyKey: helmResourcePolicyKeep, + }, + }, + }, + } + return u + } + + BeforeEach(func() { + bufOut = new(bytes.Buffer) + bufErr = new(bytes.Buffer) + streams = genericiooptions.IOStreams{Out: bufOut, ErrOut: bufErr} + tf = cmdtesting.NewTestFactory().WithNamespace(testNamespace) + tf.FakeDynamicClient = testing.FakeDynamicClient() + tf.Client = &clientfake.RESTClient{} + + // Populate dynamic client with test resources + // Addon + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testAddonGVR, "", createAddon(addonName, newestVersion)), nil) + + // ComponentDefs with different versions + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testCompDefGVR, types.DefaultNamespace, createComponentDef("redis", addonName, newestVersion)), nil) + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testCompDefGVR, types.DefaultNamespace, createComponentDef("redis", addonName, inUseVersion)), nil) + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testCompDefGVR, types.DefaultNamespace, createComponentDef("redis", addonName, unusedVersion)), nil) + + // Cluster using inUseVersion + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testClusterGVR, testNamespace, createCluster("test-cluster", addonName, inUseVersion)), nil) + + // Unused config resource for unusedVersion + _, _ = tf.FakeDynamicClient.Invokes(clienttesting.NewCreateAction(testUnusedConfigGVR, types.DefaultNamespace, createUnusedConfig(addonName, unusedVersion)), nil) + }) + + AfterEach(func() { + tf.Cleanup() + }) + + It("test delete_resources_with_versions cmd creation", func() { + Expect(newDeleteResourcesCmd(tf, streams)).ShouldNot(BeNil()) + }) + + It("test baseOption complete", func() { + option := newDeleteResourcesOption(tf, streams) + Expect(option).ShouldNot(BeNil()) + Expect(option.baseOption.complete()).Should(Succeed()) + }) + + It("test no addon name provided", func() { + option := newDeleteResourcesOption(tf, streams) + err := option.Complete(nil) + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).Should(ContainSubstring("no addon provided")) + }) + + It("test no versions and no --all-unused-versions", func() { + option := newDeleteResourcesOption(tf, streams) + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + // Validate should fail due to no versions and no all-unused-versions + err = option.Validate() + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("please specify versions or set --all-unused-versions to true")) + }) + + It("test specifying a non-existent version", func() { + option := newDeleteResourcesOption(tf, streams) + option.versions = []string{"1.0.0"} + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + err = option.Validate() + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("does not exist")) + }) + + It("test specifying newest version without deleteNewestVersion flag", func() { + option := newDeleteResourcesOption(tf, streams) + option.versions = []string{newestVersion} // newest version + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + err = option.Validate() + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("cannot be deleted as it is the newest version")) + }) + + It("test specifying an in-use version", func() { + option := newDeleteResourcesOption(tf, streams) + option.versions = []string{inUseVersion} + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + err = option.Validate() + Expect(err).Should(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("cannot be deleted as it is currently used")) + }) + + It("test specifying an unused old version directly", func() { + option := newDeleteResourcesOption(tf, streams) + option.versions = []string{unusedVersion} + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + // Validate should succeed + err = option.Validate() + Expect(err).ShouldNot(HaveOccurred()) + + // Run should delete resources associated with unusedVersion + err = option.Run() + Expect(err).ShouldNot(HaveOccurred()) + Expect(bufOut.String()).To(ContainSubstring("Deleted resource: configmaps/" + "config-" + unusedVersion)) + }) + + It("test using --all-unused-versions", func() { + option := newDeleteResourcesOption(tf, streams) + option.allUnusedVersions = true + option.Dynamic = tf.FakeDynamicClient + option.Factory = tf + err := option.Complete([]string{addonName}) + Expect(err).ShouldNot(HaveOccurred()) + + // Validate should succeed now that we have automatically set unused versions + err = option.Validate() + Expect(err).ShouldNot(HaveOccurred()) + + // Run should delete all unused and non-newest versions. In this case, unusedVersion = "0.9.1" + err = option.Run() + Expect(err).ShouldNot(HaveOccurred()) + Expect(bufOut.String()).To(ContainSubstring("Deleted resource: configmaps/" + "config-" + unusedVersion)) + }) +}) diff --git a/pkg/cmd/addon/uninstall.go b/pkg/cmd/addon/uninstall.go index 3c8dd4747..5c84ce6df 100644 --- a/pkg/cmd/addon/uninstall.go +++ b/pkg/cmd/addon/uninstall.go @@ -79,7 +79,12 @@ func newUninstallCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cob func (o *uninstallOption) Run() error { for _, name := range o.names { - err := o.Dynamic.Resource(o.GVR).Delete(context.Background(), name, metav1.DeleteOptions{}) + // delete the resources, because some resources won't be deleted by helm. + err := o.deleteAllMultiVersionsResources(name) + if err != nil { + return err + } + err = o.Dynamic.Resource(o.GVR).Delete(context.Background(), name, metav1.DeleteOptions{}) if err != nil { return err } @@ -96,3 +101,25 @@ func (o *uninstallOption) checkBeforeUninstall() error { } return CheckAddonUsedByCluster(o.Dynamic, o.names, o.In) } + +func (o *uninstallOption) deleteAllMultiVersionsResources(name string) error { + dro := &deleteResourcesOption{ + baseOption: &baseOption{ + Factory: o.Factory, + IOStreams: o.IOStreams, + GVR: types.AddonGVR(), + }, + allUnusedVersions: true, + deleteNewestVersion: true, + } + if err := dro.baseOption.complete(); err != nil { + return err + } + if err := dro.Complete([]string{name}); err != nil { + return err + } + if err := dro.Run(); err != nil { + return err + } + return nil +} diff --git a/pkg/cmd/addon/uninstall_test.go b/pkg/cmd/addon/uninstall_test.go new file mode 100644 index 000000000..cebe041c5 --- /dev/null +++ b/pkg/cmd/addon/uninstall_test.go @@ -0,0 +1,58 @@ +/* +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 addon + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/cli-runtime/pkg/genericiooptions" + clientfake "k8s.io/client-go/rest/fake" + cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + + "github.com/apecloud/kbcli/pkg/testing" +) + +var _ = Describe("uninstall test", func() { + var ( + streams genericiooptions.IOStreams + tf *cmdtesting.TestFactory + ) + + BeforeEach(func() { + streams, _, _, _ = genericiooptions.NewTestIOStreams() + tf = cmdtesting.NewTestFactory().WithNamespace(testNamespace) + tf.FakeDynamicClient = testing.FakeDynamicClient() + tf.Client = &clientfake.RESTClient{} + }) + + AfterEach(func() { + tf.Cleanup() + }) + + It("text uninstall cmd", func() { + Expect(newUninstallCmd(tf, streams)).ShouldNot(BeNil()) + }) + + It("test baseOption complete", func() { + option := newUninstallOption(tf, streams) + Expect(option).ShouldNot(BeNil()) + Expect(option.baseOption.complete()).Should(Succeed()) + }) +}) From 56bc9b666de9e95505f16cc90ed261242b0851c7 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Mon, 16 Dec 2024 10:09:03 +0800 Subject: [PATCH 19/34] fix: make static bug (#520) (cherry picked from commit 31dce3ef85d59351a2e6592c5045c7c5d38dd88a) --- pkg/cmd/trace/chart/reconciliation_trace_chart.go | 3 ++- .../chart/timeserieslinechart/timeserieslinechart.go | 10 ++++------ pkg/cmd/trace/watch.go | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/trace/chart/reconciliation_trace_chart.go b/pkg/cmd/trace/chart/reconciliation_trace_chart.go index da458d9ed..a478807db 100644 --- a/pkg/cmd/trace/chart/reconciliation_trace_chart.go +++ b/pkg/cmd/trace/chart/reconciliation_trace_chart.go @@ -32,11 +32,12 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" + tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" + "github.com/apecloud/kbcli/pkg/cmd/trace/chart/objecttree" "github.com/apecloud/kbcli/pkg/cmd/trace/chart/richviewport" "github.com/apecloud/kbcli/pkg/cmd/trace/chart/summary" "github.com/apecloud/kbcli/pkg/cmd/trace/chart/timeserieslinechart" - tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" ) var ( diff --git a/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go b/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go index b551812a2..d6925cc72 100644 --- a/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go +++ b/pkg/cmd/trace/chart/timeserieslinechart/timeserieslinechart.go @@ -91,8 +91,6 @@ type dataSet struct { LineStyle runes.LineStyle // type of line runes to draw Style lipgloss.Style - lastTime time.Time // last seen time value - // stores TimePoints as FloatPoint64{X:time.Time, Y: value} // time.Time will be converted to seconds since epoch. // both time and value will be scaled to fit the graphing area @@ -272,7 +270,7 @@ func (m *Model) Push(t TimePoint) { m.PushDataSet(DefaultDataSetName, t) } -// Push will push a TimePoint data value to a data set +// PushDataSet will push a TimePoint data value to a data set // to be displayed with Draw. Using given data set by name string. func (m *Model) PushDataSet(n string, t TimePoint) { f := canvas.Float64Point{X: float64(t.Time.Unix()), Y: t.Value} @@ -479,7 +477,7 @@ func (m *Model) DrawBrailleDataSets(names []string) { } } -// DrawColumnRune draws a braille rune on to the canvas at given (X,Y) coordinates with given style. +// DrawBrailleRune draws a braille rune on to the canvas at given (X,Y) coordinates with given style. // The function checks for existing braille runes already on the canvas and // will draw a new braille pattern with the dot patterns of both the existing and given runes. // Does nothing if given rune is Null or is not a braille rune. @@ -521,7 +519,7 @@ func (m *Model) getLineSequence(points []canvas.Float64Point) []int { // each index of the bucket corresponds to a graph column. // each index value is the average of data point values // that is mapped to that graph column. - buckets := make([]cAverage, width, width) + buckets := make([]cAverage, width) for i := 0; i < dataLen; i++ { j := i + 1 if j >= dataLen { @@ -545,7 +543,7 @@ func (m *Model) getLineSequence(points []canvas.Float64Point) []int { } } // populate sequence of Y values to for drawing - r := make([]int, width, width) + r := make([]int, width) for i, v := range buckets { r[i] = int(math.Round(v.Avg)) } diff --git a/pkg/cmd/trace/watch.go b/pkg/cmd/trace/watch.go index ce81ea578..a5098d430 100644 --- a/pkg/cmd/trace/watch.go +++ b/pkg/cmd/trace/watch.go @@ -36,10 +36,11 @@ import ( cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" + tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" + "github.com/apecloud/kbcli/pkg/cmd/trace/chart" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" - tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" ) var ( From bf4c7a3f4eef162248c43a8c914e0867da5b3e48 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:03:10 +0800 Subject: [PATCH 20/34] chore: addon install and search support auto complete (#518) (cherry picked from commit 34f83394f284b45573e022185e64f724f054f2c4) --- docs/user_docs/cli/kbcli_addon_search.md | 2 +- pkg/cmd/addon/install.go | 1 + pkg/cmd/addon/search.go | 115 ++++++++++++++--------- pkg/cmd/addon/util.go | 89 +++++++++++++++++- pkg/cmd/addon/util_test.go | 4 +- 5 files changed, 161 insertions(+), 50 deletions(-) diff --git a/docs/user_docs/cli/kbcli_addon_search.md b/docs/user_docs/cli/kbcli_addon_search.md index 1fa3c142a..10995adff 100644 --- a/docs/user_docs/cli/kbcli_addon_search.md +++ b/docs/user_docs/cli/kbcli_addon_search.md @@ -5,7 +5,7 @@ title: kbcli addon search Search the addon from index ``` -kbcli addon search [flags] +kbcli addon search [ADDON_NAME] [flags] ``` ### Examples diff --git a/pkg/cmd/addon/install.go b/pkg/cmd/addon/install.go index 5200e9c0e..9fe2eb930 100644 --- a/pkg/cmd/addon/install.go +++ b/pkg/cmd/addon/install.go @@ -131,6 +131,7 @@ func newInstallCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra PersistentPreRun: func(_ *cobra.Command, _ []string) { util.CheckErr(addDefaultIndex()) }, + ValidArgsFunction: addonNameCompletionFunc, Run: func(cmd *cobra.Command, args []string) { o.name = args[0] util.CheckErr(o.Complete()) diff --git a/pkg/cmd/addon/search.go b/pkg/cmd/addon/search.go index 9599aa8d8..68df5b2c0 100644 --- a/pkg/cmd/addon/search.go +++ b/pkg/cmd/addon/search.go @@ -70,79 +70,102 @@ type searchOpts struct { func newSearchCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := &searchOpts{} cmd := &cobra.Command{ - Use: "search", + Use: "search [ADDON_NAME]", Short: "Search the addon from index", Example: addonSearchExample, + Args: cobra.MaximumNArgs(1), PersistentPreRun: func(cmd *cobra.Command, _ []string) { util.CheckErr(util.EnableLogToFile(cmd.Flags())) util.CheckErr(addDefaultIndex()) }, + ValidArgsFunction: addonNameCompletionFunc, Run: func(_ *cobra.Command, args []string) { - if len(args) == 0 { - o.name = "" - } else { + if len(args) == 1 { o.name = args[0] + } else { + o.name = "" } - util.CheckErr(o.search(streams.Out, &addonListOpts{ - ListOptions: action.NewListOptions(f, streams, types.AddonGVR()), - })) + util.CheckErr(o.Run(streams.Out, f)) }, } cmd.Flags().StringVar(&o.path, "path", "", "the local directory contains addon CRs") return cmd } -func (o *searchOpts) search(out io.Writer, addonListOpts *addonListOpts) error { - var ( - err error - results []searchResult - ) - if o.path == "" { - dir, err := util.GetCliAddonDir() +func (o *searchOpts) Run(out io.Writer, f cmdutil.Factory) error { + listOpt := &addonListOpts{ + ListOptions: action.NewListOptions(f, genericiooptions.IOStreams{Out: out}, types.AddonGVR()), + } + + // Determine the directory to search in + dir := o.path + if dir == "" { + var err error + dir, err = util.GetCliAddonDir() if err != nil { return err } - if results, err = searchAddon(o.name, dir, ""); err != nil { - return err - } - } else { - if results, err = searchAddon(o.name, filepath.Dir(o.path), filepath.Base(o.path)); err != nil { - return err - } } - tbl := printer.NewTablePrinter(out) + // Search for addons based on the name or all addons if no name is specified + results, err := searchAddon(o.name, dir, "") + if err != nil { + return err + } + + // Display results based on whether a name is specified or not if o.name == "" { - tbl.AddRow("ADDON", "STATUS") - statusMap := map[bool]string{ - true: "installed", - false: "uninstalled", - } - results = uniqueByName(results) - err := checkAddonInstalled(&results, addonListOpts) - if err != nil { - return err - } - for _, res := range results { - tbl.AddRow(res.addon.Name, statusMap[res.isInstalled]) - } + return o.displayAllAddons(out, results, listOpt) } else { - tbl.AddRow("ADDON", "VERSION", "INDEX") - if len(results) == 0 { - fmt.Fprintf(out, "%s addon not found. Please update your index or check the addon name.\n"+ - "You can use the command 'kbcli addon index update --all=true' to update all indexes,\n"+ - "or specify a local path containing addons with the command 'kbcli addon search --path=/path/to/local/chart'", o.name) - return nil - } - for _, res := range results { - tbl.AddRow(res.addon.Name, getAddonVersion(res.addon), res.index.name) - } + return o.displaySingleAddon(out, results) + } +} + +// displayAllAddons lists all addons and indicates whether they are installed +func (o *searchOpts) displayAllAddons(out io.Writer, results []searchResult, listOpt *addonListOpts) error { + results = uniqueByName(results) + err := checkAddonInstalled(&results, listOpt) + if err != nil { + return err + } + + tbl := printer.NewTablePrinter(out) + tbl.AddRow("ADDON", "STATUS") + + statusMap := map[bool]string{ + true: "installed", + false: "uninstalled", + } + + for _, res := range results { + tbl.AddRow(res.addon.Name, statusMap[res.isInstalled]) } tbl.Print() + + return nil +} + +// displaySingleAddon shows detailed information for a specified addon +func (o *searchOpts) displaySingleAddon(out io.Writer, results []searchResult) error { + tbl := printer.NewTablePrinter(out) + tbl.AddRow("ADDON", "VERSION", "INDEX") + + if len(results) == 0 { + fmt.Fprintf(out, "%s addon not found. Please update your index or check the addon name.\n"+ + "You can use the command 'kbcli addon index update --all=true' to update all indexes,\n"+ + "or specify a local path containing addons with the command 'kbcli addon search --path=/path/to/local/addons'\n", o.name) + return nil + } + + for _, res := range results { + tbl.AddRow(res.addon.Name, getAddonVersion(res.addon), res.index.name) + } + tbl.Print() + return nil } -// searchAddon function will search for the addons with the specified name in the index of the specified directory and return them. +// searchAddon searches for addons that meet the specified criteria in a given directory func searchAddon(name string, indexDir string, theIndex string) ([]searchResult, error) { var res []searchResult searchInDir := func(i index) error { diff --git a/pkg/cmd/addon/util.go b/pkg/cmd/addon/util.go index df6ba2bf5..e5a155113 100644 --- a/pkg/cmd/addon/util.go +++ b/pkg/cmd/addon/util.go @@ -23,14 +23,20 @@ import ( "context" "fmt" "io" + "io/fs" + "os" + "path/filepath" "sort" "strings" + "github.com/spf13/cobra" "golang.org/x/exp/maps" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/dynamic" + "k8s.io/klog/v2" "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" @@ -130,7 +136,7 @@ func checkAddonInstalled(objects *[]searchResult, o *addonListOpts) error { func CheckAddonUsedByCluster(dynamic dynamic.Interface, addons []string, in io.Reader) error { labelSelecotor := util.BuildClusterLabel("", addons) - list, err := dynamic.Resource(types.ClusterGVR()).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelecotor}) + list, err := dynamic.Resource(types.ClusterGVR()).Namespace(metav1.NamespaceAll).List(context.Background(), metav1.ListOptions{LabelSelector: labelSelecotor}) if err != nil { return err } @@ -151,3 +157,84 @@ func CheckAddonUsedByCluster(dynamic dynamic.Interface, addons []string, in io.R } return nil } + +// addonNameCompletionFunc provides name auto-completion for addons +func addonNameCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + fmt.Printf("toComplete: %s\n", toComplete) + var addonDir string + var err error + addonDir, err = util.GetCliAddonDir() + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + // Retrieve all addon names + allAddons, err := getAllAddonNames(addonDir) + if err != nil { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + matches := fuzzyMatch(allAddons, toComplete) + return matches, cobra.ShellCompDirectiveNoFileComp +} + +// getAllAddonNames retrieves all addon names from the given directory +func getAllAddonNames(dir string) ([]string, error) { + var addonNames []string + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return nil + } + // Skip hidden directories + if strings.HasPrefix(d.Name(), ".") && d.IsDir() { + return filepath.SkipDir + } + if d.IsDir() { + return nil + } + if strings.HasSuffix(strings.ToLower(d.Name()), ".yaml") { + content, err := os.ReadFile(path) + if err != nil { + klog.V(2).Infof("read file %s error: %v", path, err) + return nil + } + addon := &extensionsv1alpha1.Addon{} + if yaml.Unmarshal(content, addon) != nil { + return nil + } + if addon.Kind == "Addon" && addon.Name != "" { + addonNames = append(addonNames, addon.Name) + } + } + return nil + }) + if err != nil { + return nil, err + } + + // Remove duplicates + nameSet := make(map[string]struct{}) + for _, n := range addonNames { + nameSet[n] = struct{}{} + } + var unique []string + for n := range nameSet { + unique = append(unique, n) + } + return unique, nil +} + +// fuzzyMatch performs simple substring fuzzy matching on names +func fuzzyMatch(names []string, prefix string) []string { + if prefix == "" { + return names + } + var matches []string + lp := strings.ToLower(prefix) + for _, n := range names { + if strings.Contains(strings.ToLower(n), lp) { + matches = append(matches, n) + } + } + return matches +} diff --git a/pkg/cmd/addon/util_test.go b/pkg/cmd/addon/util_test.go index 0ad53a0c2..11f6911aa 100644 --- a/pkg/cmd/addon/util_test.go +++ b/pkg/cmd/addon/util_test.go @@ -22,7 +22,6 @@ package addon import ( "net/http" - kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/runtime" @@ -33,6 +32,8 @@ import ( clientfake "k8s.io/client-go/rest/fake" cmdtesting "k8s.io/kubectl/pkg/cmd/testing" + kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + "github.com/apecloud/kbcli/pkg/testing" "github.com/apecloud/kbcli/pkg/types" ) @@ -70,5 +71,4 @@ var _ = Describe("addon util test", func() { It("text CheckAddonUsedByCluster", func() { Expect(CheckAddonUsedByCluster(tf.FakeDynamicClient, []string{fakeAddonName}, streams.In)).Should(HaveOccurred()) }) - }) From c921569b87ae51a7e9cb213b90d4fea3b47db6e0 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:41:39 +0800 Subject: [PATCH 21/34] chore: fix typo of cluster dp cmd (#522) (cherry picked from commit 80005d8308bef53bf8dc74b02c458cdc2d669dbf) --- docs/user_docs/cli/cli.md | 6 ++--- docs/user_docs/cli/kbcli_cluster.md | 6 ++--- docs/user_docs/cli/kbcli_cluster_backup.md | 2 +- .../cli/kbcli_cluster_list-backups.md | 20 ++++++++++------ pkg/cmd/cluster/dataprotection.go | 24 +++++++++---------- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 708989538..56b5b50b7 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -58,14 +58,14 @@ Cluster command. * [kbcli cluster expose](kbcli_cluster_expose.md) - Expose a cluster with a new endpoint, the new endpoint can be found by executing 'kbcli cluster describe NAME'. * [kbcli cluster label](kbcli_cluster_label.md) - Update the labels on cluster * [kbcli cluster list](kbcli_cluster_list.md) - List clusters. -* [kbcli cluster list-backup](kbcli_cluster_list-backup.md) - List backups. -* [kbcli cluster list-backup-policy](kbcli_cluster_list-backup-policy.md) - List backups policies. +* [kbcli cluster list-backup-policies](kbcli_cluster_list-backup-policies.md) - List backups policies. +* [kbcli cluster list-backups](kbcli_cluster_list-backups.md) - List backups. * [kbcli cluster list-components](kbcli_cluster_list-components.md) - List cluster components. * [kbcli cluster list-events](kbcli_cluster_list-events.md) - List cluster events. * [kbcli cluster list-instances](kbcli_cluster_list-instances.md) - List cluster instances. * [kbcli cluster list-logs](kbcli_cluster_list-logs.md) - List supported log files in cluster. * [kbcli cluster list-ops](kbcli_cluster_list-ops.md) - List all opsRequests. -* [kbcli cluster list-restore](kbcli_cluster_list-restore.md) - List restores. +* [kbcli cluster list-restores](kbcli_cluster_list-restores.md) - List restores. * [kbcli cluster logs](kbcli_cluster_logs.md) - Access cluster log file. * [kbcli cluster promote](kbcli_cluster_promote.md) - Promote a non-primary or non-leader instance as the new primary or leader of the cluster * [kbcli cluster rebuild-instance](kbcli_cluster_rebuild-instance.md) - Rebuild the specified instances in the cluster. diff --git a/docs/user_docs/cli/kbcli_cluster.md b/docs/user_docs/cli/kbcli_cluster.md index 95de599b6..075601e9f 100644 --- a/docs/user_docs/cli/kbcli_cluster.md +++ b/docs/user_docs/cli/kbcli_cluster.md @@ -59,14 +59,14 @@ Cluster command. * [kbcli cluster expose](kbcli_cluster_expose.md) - Expose a cluster with a new endpoint, the new endpoint can be found by executing 'kbcli cluster describe NAME'. * [kbcli cluster label](kbcli_cluster_label.md) - Update the labels on cluster * [kbcli cluster list](kbcli_cluster_list.md) - List clusters. -* [kbcli cluster list-backup](kbcli_cluster_list-backup.md) - List backups. -* [kbcli cluster list-backup-policy](kbcli_cluster_list-backup-policy.md) - List backups policies. +* [kbcli cluster list-backup-policies](kbcli_cluster_list-backup-policies.md) - List backups policies. +* [kbcli cluster list-backups](kbcli_cluster_list-backups.md) - List backups. * [kbcli cluster list-components](kbcli_cluster_list-components.md) - List cluster components. * [kbcli cluster list-events](kbcli_cluster_list-events.md) - List cluster events. * [kbcli cluster list-instances](kbcli_cluster_list-instances.md) - List cluster instances. * [kbcli cluster list-logs](kbcli_cluster_list-logs.md) - List supported log files in cluster. * [kbcli cluster list-ops](kbcli_cluster_list-ops.md) - List all opsRequests. -* [kbcli cluster list-restore](kbcli_cluster_list-restore.md) - List restores. +* [kbcli cluster list-restores](kbcli_cluster_list-restores.md) - List restores. * [kbcli cluster logs](kbcli_cluster_logs.md) - Access cluster log file. * [kbcli cluster promote](kbcli_cluster_promote.md) - Promote a non-primary or non-leader instance as the new primary or leader of the cluster * [kbcli cluster rebuild-instance](kbcli_cluster_rebuild-instance.md) - Rebuild the specified instances in the cluster. diff --git a/docs/user_docs/cli/kbcli_cluster_backup.md b/docs/user_docs/cli/kbcli_cluster_backup.md index 928c825e5..d17c45b10 100644 --- a/docs/user_docs/cli/kbcli_cluster_backup.md +++ b/docs/user_docs/cli/kbcli_cluster_backup.md @@ -17,7 +17,7 @@ kbcli cluster backup NAME [flags] # create a backup with a specified method, run "kbcli cluster desc-backup-policy mycluster" to show supported backup methods kbcli cluster backup mycluster --method volume-snapshot - # create a backup with specified backup policy, run "kbcli cluster list-backup-policy mycluster" to show the cluster supported backup policies + # create a backup with specified backup policy, run "kbcli cluster list-backup-policies mycluster" to show the cluster supported backup policies kbcli cluster backup mycluster --method volume-snapshot --policy # create a backup from a parent backup diff --git a/docs/user_docs/cli/kbcli_cluster_list-backups.md b/docs/user_docs/cli/kbcli_cluster_list-backups.md index 5a3b6baf7..6931bb567 100644 --- a/docs/user_docs/cli/kbcli_cluster_list-backups.md +++ b/docs/user_docs/cli/kbcli_cluster_list-backups.md @@ -13,17 +13,24 @@ kbcli cluster list-backups [flags] ``` # list all backups kbcli cluster list-backups + + # list all backups of the cluster + kbcli cluster list-backups + + # list the specified backups + kbcli cluster list-backups --names b1,b2 ``` ### Options ``` - -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. - -h, --help help for list-backups - --name string The backup name to get the details. - -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) - -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. - --show-labels When printing, show all labels as the last column (default hide labels column) + -A, --all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. + -h, --help help for list-backups + --names strings The backup name to get the details. + -n, --namespace string specified the namespace + -o, --output format prints the output in the specified format. Allowed values: table, json, yaml, wide (default table) + -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. + --show-labels When printing, show all labels as the last column (default hide labels column) ``` ### Options inherited from parent commands @@ -42,7 +49,6 @@ kbcli cluster list-backups [flags] --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 diff --git a/pkg/cmd/cluster/dataprotection.go b/pkg/cmd/cluster/dataprotection.go index 66e590ca9..45f63b869 100644 --- a/pkg/cmd/cluster/dataprotection.go +++ b/pkg/cmd/cluster/dataprotection.go @@ -42,7 +42,7 @@ import ( var ( listBackupPolicyExample = templates.Examples(` # list all backup policies - kbcli cluster list-backup-policy + kbcli cluster list-backup-policies # using short cmd to list backup policy of the specified cluster kbcli cluster list-bp mycluster @@ -58,7 +58,7 @@ var ( # create a backup with a specified method, run "kbcli cluster desc-backup-policy mycluster" to show supported backup methods kbcli cluster backup mycluster --method volume-snapshot - # create a backup with specified backup policy, run "kbcli cluster list-backup-policy mycluster" to show the cluster supported backup policies + # create a backup with specified backup policy, run "kbcli cluster list-backup-policies mycluster" to show the cluster supported backup policies kbcli cluster backup mycluster --method volume-snapshot --policy # create a backup from a parent backup @@ -66,13 +66,13 @@ var ( `) listBackupExample = templates.Examples(` # list all backups - kbcli cluster list-backup + kbcli cluster list-backups # list all backups of the cluster - kbcli cluster list-backup + kbcli cluster list-backups # list the specified backups - kbcli cluster list-backup --names b1,b2 + kbcli cluster list-backups --names b1,b2 `) deleteBackupExample = templates.Examples(` # delete a backup named backup-name @@ -84,13 +84,13 @@ var ( `) listRestoreExample = templates.Examples(` # list all restores - kbcli cluster list-restore + kbcli cluster list-restores # list all restores of the cluster - kbcli cluster list-restore + kbcli cluster list-restores # list the specified restores - kbcli cluster list-restore --names r1,r2 + kbcli cluster list-restores --names r1,r2 `) describeBackupExample = templates.Examples(` # describe a backup @@ -113,7 +113,7 @@ func NewCreateBackupCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) * customOutPut := func(opt *action.CreateOptions) { output := fmt.Sprintf("Backup %s created successfully, you can view the progress:", opt.Name) printer.PrintLine(output) - nextLine := fmt.Sprintf("\tkbcli cluster list-backup --name=%s -n %s", opt.Name, opt.Namespace) + nextLine := fmt.Sprintf("\tkbcli cluster list-backups --name=%s -n %s", opt.Name, opt.Namespace) printer.PrintLine(nextLine) } @@ -156,7 +156,7 @@ func NewCreateBackupCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) * func NewListBackupCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := action.NewListOptions(f, streams, types.BackupGVR()) cmd := &cobra.Command{ - Use: "list-backup", + Use: "list-backups", Short: "List backups.", Aliases: []string{"ls-backup"}, Example: listBackupExample, @@ -311,7 +311,7 @@ func NewCreateRestoreCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) func NewListBackupPolicyCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := action.NewListOptions(f, streams, types.BackupPolicyGVR()) cmd := &cobra.Command{ - Use: "list-backup-policy", + Use: "list-backup-policies", Short: "List backups policies.", Aliases: []string{"list-bp"}, Example: listBackupPolicyExample, @@ -390,7 +390,7 @@ func describeBackupPolicies(o *dp.DescribeDPOptions, args []string) error { func NewListRestoreCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := action.NewListOptions(f, streams, types.RestoreGVR()) cmd := &cobra.Command{ - Use: "list-restore", + Use: "list-restores", Short: "List restores.", Aliases: []string{"ls-restores"}, Example: listRestoreExample, From e7e641855423976a13eafa3b0073c0479b15a6bc Mon Sep 17 00:00:00 2001 From: wangyelei Date: Thu, 19 Dec 2024 11:16:40 +0800 Subject: [PATCH 22/34] chore: support to upgrade addon to 1.0 when existing 0.9 addon (#524) (cherry picked from commit bad36d9b9b78f5b15ef9285c7e76b8ab82161603) --- go.mod | 3 +- go.sum | 6 +- pkg/cluster/external_charts.go | 7 +- pkg/cmd/addon/addon.go | 148 ++++++++++++++++++++++++++++++ pkg/cmd/addon/upgrade.go | 42 +-------- pkg/cmd/cluster/operations.go | 2 +- pkg/cmd/kubeblocks/install_1.0.go | 31 ++++++- pkg/types/types.go | 9 ++ 8 files changed, 196 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index eb6dabc91..1a1243c1c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 github.com/NimbleMarkets/ntcharts v0.1.2 github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 - github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7 + github.com/apecloud/kubeblocks v1.0.0-beta.17 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/briandowns/spinner v1.23.0 github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad @@ -109,7 +109,6 @@ require ( github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/charmbracelet/keygen v0.5.1 // indirect github.com/charmbracelet/x/ansi v0.2.3 // indirect github.com/charmbracelet/x/term v0.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect diff --git a/go.sum b/go.sum index dfe89c400..67bd7a601 100644 --- a/go.sum +++ b/go.sum @@ -677,8 +677,8 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys= -github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7 h1:7IHywp4JkW0B2MZAfFOyXPclcCOuzLw1eb6KyzAxze0= -github.com/apecloud/kubeblocks v1.0.0-beta.1.0.20241126060033-67c88565bfc7/go.mod h1:WdpL00VvZuQMW35oP+I1EoWV6qdhILK54CnfFlfyEAI= +github.com/apecloud/kubeblocks v1.0.0-beta.17 h1:taNHtwUWyCUBSHbPAx5sY5ltY0Dcf62cr+1HjxlK60w= +github.com/apecloud/kubeblocks v1.0.0-beta.17/go.mod h1:bQ6uey/6S9gAuDkAJ7T89CdpmeXyxEFJpLw1hV2hANE= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -750,8 +750,6 @@ github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/ github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= -github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI= -github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= diff --git a/pkg/cluster/external_charts.go b/pkg/cluster/external_charts.go index fdaacd1a6..58eb7c074 100644 --- a/pkg/cluster/external_charts.go +++ b/pkg/cluster/external_charts.go @@ -21,6 +21,7 @@ package cluster import ( "compress/gzip" + "errors" "fmt" "io" "io/fs" @@ -257,7 +258,7 @@ func (h *TypeInstance) ValidateChartSchema() (bool, error) { if err != nil { return false, err } - if c.Schema == nil || len(c.Schema) == 0 { + if len(c.Schema) == 0 { return false, fmt.Errorf("register cluster chart of %s failed, schema of the chart doesn't exist", h.Name) } if c.Values == nil { @@ -269,7 +270,7 @@ func (h *TypeInstance) ValidateChartSchema() (bool, error) { result, err := gojsonschema.Validate(schemaLoader, valuesLoader) if err != nil { - return false, fmt.Errorf("Validation failed: %s\n", err) + return false, fmt.Errorf("validation failed: %v", err) } if result.Valid() { @@ -280,7 +281,7 @@ func (h *TypeInstance) ValidateChartSchema() (bool, error) { for _, desc := range result.Errors() { res += fmt.Sprintf("- %s\n", desc) } - return false, fmt.Errorf(res) + return false, errors.New(res) } } diff --git a/pkg/cmd/addon/addon.go b/pkg/cmd/addon/addon.go index 777fb6827..37114dae1 100644 --- a/pkg/cmd/addon/addon.go +++ b/pkg/cmd/addon/addon.go @@ -29,31 +29,41 @@ import ( "strconv" "strings" + kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" "github.com/spf13/pflag" + helmaction "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/releaseutil" corev1 "k8s.io/api/core/v1" + apierrors "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/apimachinery/pkg/runtime/schema" "k8s.io/cli-runtime/pkg/genericiooptions" discoverycli "k8s.io/client-go/discovery" "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" "k8s.io/utils/strings/slices" + "sigs.k8s.io/yaml" extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" "github.com/apecloud/kubeblocks/pkg/constant" viper "github.com/apecloud/kubeblocks/pkg/viperx" "github.com/apecloud/kbcli/pkg/action" + "github.com/apecloud/kbcli/pkg/cluster" clusterCmd "github.com/apecloud/kbcli/pkg/cmd/cluster" "github.com/apecloud/kbcli/pkg/cmd/plugin" "github.com/apecloud/kbcli/pkg/printer" "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" + "github.com/apecloud/kbcli/pkg/util/helm" ) type addonEnableFlags struct { @@ -81,6 +91,7 @@ type addonCmdOpts struct { Factory cmdutil.Factory dynamic dynamic.Interface + client kubernetes.Interface addon extensionsv1alpha1.Addon @@ -225,6 +236,7 @@ func newEnableCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. util.CheckErr(o.validate()) util.CheckErr(o.complete(o, cmd, []string{name})) util.CheckErr(o.CmdComplete(cmd)) + util.CheckErr(o.takeOver09AddonGlobalResources()) util.CheckErr(o.Run()) if isEngineAddon(&o.addon) { util.CheckErr(clusterCmd.RegisterClusterChart(f, streams, "", name, getAddonVersion(&o.addon), types.ClusterChartsRepoURL)) @@ -309,6 +321,10 @@ func (o *addonCmdOpts) init(args []string) error { return err } } + var err error + if o.client, err = o.Factory.KubernetesClientSet(); err != nil { + return err + } // setup _KUBE_SERVER_INFO if viper.Get(constant.CfgKeyServerInfo) == nil { @@ -1018,3 +1034,135 @@ func (o *addonCmdOpts) checkBeforeDisable() error { } return CheckAddonUsedByCluster(o.dynamic, o.Names, o.In) } + +func (o *addonCmdOpts) takeOver09AddonGlobalResources() error { + kbDeploys, err := util.GetKBDeploys(o.client, util.KubeblocksAppComponent, metav1.NamespaceAll) + if err != nil || len(kbDeploys) < 2 { + return err + } + if !strings.HasPrefix(o.addon.Spec.Version, "1.0") { + return nil + } + var newKBNamespace string + for _, v := range kbDeploys { + if strings.HasPrefix(v.Labels[constant.AppVersionLabelKey], "1.0") { + newKBNamespace = v.Namespace + break + } + } + selector := metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", constant.AppNameLabelKey, o.addon.Name), + } + takeOverRelease := func(gvr schema.GroupVersionResource) error { + cdList, err := o.dynamic.Resource(gvr).Namespace("").List(context.TODO(), selector) + if err != nil { + return err + } + for _, v := range cdList.Items { + if err = util.SetHelmOwner(o.dynamic, gvr, "kb-addon-"+o.addon.Name, newKBNamespace, []string{v.GetName()}); err != nil { + return err + } + } + return nil + } + if err = takeOverRelease(types.ClusterDefGVR()); err != nil { + return err + } + if err = takeOverRelease(types.ComponentVersionsGVR()); err != nil { + return err + } + return o.overwriteCDAndCMPVSpecWith10Version(newKBNamespace) +} + +func (o *addonCmdOpts) overwriteCDAndCMPVSpecWith10Version(namespace string) error { + // 1. get manifests from the helm repo + chartsDownloader, err := helm.NewDownloader(helm.NewConfig(namespace, "", "", false)) + if err != nil { + return err + } + // DownloadTo can't specify the saved name, so download it to TempDir and rename it when copy + chartPath, _, err := chartsDownloader.DownloadTo(o.addon.Spec.Helm.ChartLocationURL, "", cluster.CliChartsCacheDir) + if err != nil { + return err + } + // 2. overwrite the spec of ClusterDefinition and ComponentVersion with the new version + actionCfg, err := helm.NewActionConfig(helm.NewConfig(namespace, "", "", false)) + if err != nil { + return err + } + chart, err := loader.Load(chartPath) + if err != nil { + return err + } + renderer := helmaction.NewInstall(actionCfg) + renderer.ReleaseName = o.addon.Name + "for-upgrade" + renderer.Namespace = namespace + renderer.DryRun = true + renderer.Replace = true + renderer.ClientOnly = true + valuesMap := map[string]interface{}{} + if o.addon.Spec.Helm != nil { + for _, v := range o.addon.Spec.Helm.InstallValues.SetValues { + keyValues := strings.Split(v, "=") + if len(keyValues) != 2 { + return fmt.Errorf("invalid install value: %s", v) + } + valuesMap[keyValues[0]] = keyValues[1] + } + } + release, err := renderer.Run(chart, valuesMap) + if err != nil { + return err + } + + updateObject := func(obj runtime.Object, gvr schema.GroupVersionResource) error { + unstructuredObj := obj.(*unstructured.Unstructured) + targetObj, err := o.dynamic.Resource(gvr).Namespace("").Get(context.TODO(), unstructuredObj.GetName(), metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + if _, err = o.dynamic.Resource(gvr).Namespace("").Create(context.TODO(), unstructuredObj, metav1.CreateOptions{}); err != nil { + return err + } + return nil + } + return err + } + annotations := targetObj.GetAnnotations() + annotations[constant.CRDAPIVersionAnnotationKey] = kbappsv1.GroupVersion.String() + targetObj.SetAnnotations(annotations) + targetObj.Object["spec"] = unstructuredObj.Object["spec"] + if _, err = o.dynamic.Resource(gvr).Namespace("").Update(context.TODO(), targetObj, metav1.UpdateOptions{}); err != nil { + return err + } + return nil + } + manifests := releaseutil.SplitManifests(release.Manifest) + for _, manifest := range manifests { + + // convert yaml to json + jsonData, err := yaml.YAMLToJSON([]byte(manifest)) + if err != nil { + return err + } + // check if jsonData is empty or null + if len(jsonData) == 0 || string(jsonData) == "null" { + continue + } + // get resource gvk + obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(jsonData, nil, nil) + if err != nil { + return err + } + switch gvk.Kind { + case types.KindClusterDef: + if err = updateObject(obj, types.ClusterDefGVR()); err != nil { + return err + } + case types.KindComponentVersion: + if err = updateObject(obj, types.ComponentVersionsGVR()); err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/cmd/addon/upgrade.go b/pkg/cmd/addon/upgrade.go index 1c038e08b..eb90dfdb9 100644 --- a/pkg/cmd/addon/upgrade.go +++ b/pkg/cmd/addon/upgrade.go @@ -23,13 +23,11 @@ import ( "context" "encoding/json" "fmt" - "strings" "github.com/Masterminds/semver/v3" - "github.com/apecloud/dbctl/constant" + extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" ktypes "k8s.io/apimachinery/pkg/types" "k8s.io/cli-runtime/pkg/genericiooptions" cmdutil "k8s.io/kubectl/pkg/cmd/util" @@ -37,8 +35,6 @@ import ( "github.com/apecloud/kbcli/pkg/printer" - extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" - "github.com/apecloud/kbcli/pkg/types" "github.com/apecloud/kbcli/pkg/util" ) @@ -101,7 +97,6 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra o.name = args[0] util.CheckErr(o.Complete()) util.CheckErr(o.Validate()) - util.CheckErr(o.takeOver09AddonGlobalResources()) util.CheckErr(o.Run(f, streams)) }, } @@ -115,41 +110,6 @@ func newUpgradeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra cmd.Flags().StringVar(&o.path, "path", "", "specify the local path contains addon CRs and needs to be specified when operating offline") return cmd } -func (o *upgradeOption) takeOver09AddonGlobalResources() error { - kbDeploys, err := util.GetKBDeploys(o.Client, util.KubeblocksAppComponent, metav1.NamespaceAll) - if err != nil || len(kbDeploys) < 2 { - return err - } - if !strings.HasPrefix(o.version, "1.0") { - return nil - } - var newKBNamespace string - for _, v := range kbDeploys { - if strings.HasPrefix(v.Labels[constant.AppVersionLabelKey], "1.0") { - newKBNamespace = v.Namespace - break - } - } - selector := metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", constant.AppNameLabelKey, o.name), - } - takeOverRelease := func(gvr schema.GroupVersionResource) error { - cdList, err := o.Dynamic.Resource(gvr).Namespace("").List(context.TODO(), selector) - if err != nil { - return err - } - for _, v := range cdList.Items { - if err = util.SetHelmOwner(o.Dynamic, gvr, "kb-addon-"+o.name, newKBNamespace, []string{v.GetName()}); err != nil { - return err - } - } - return nil - } - if err := takeOverRelease(types.ClusterDefGVR()); err != nil { - return err - } - return takeOverRelease(types.ComponentVersionsGVR()) -} func (o *upgradeOption) Complete() error { if err := o.installOption.Complete(); err != nil { diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index d1dc7c20d..fce50953c 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -1229,7 +1229,7 @@ func (o *CustomOperations) completeCustomSpec(cmd *cobra.Command) error { } } // validate if flags values are legal. - data, err := common.CoverStringToInterfaceBySchemaType(o.SchemaProperties, paramMap) + data, err := common.ConvertStringToInterfaceBySchemaType(o.SchemaProperties, paramMap) if err != nil { return err } diff --git a/pkg/cmd/kubeblocks/install_1.0.go b/pkg/cmd/kubeblocks/install_1.0.go index 83e8666d2..46d943c07 100644 --- a/pkg/cmd/kubeblocks/install_1.0.go +++ b/pkg/cmd/kubeblocks/install_1.0.go @@ -30,9 +30,11 @@ import ( "helm.sh/helm/v3/pkg/cli/values" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8stypes "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/apecloud/kbcli/pkg/spinner" "github.com/apecloud/kbcli/pkg/types" @@ -103,7 +105,10 @@ func (o *InstallOptions) preInstallWhenUpgradeFrom09() error { return err } // 4. Set global resources helm owner to 1.0 KB - return o.setGlobalResourcesHelmOwner() + if err := o.setGlobalResourcesHelmOwner(); err != nil { + return err + } + return o.setCRDAPIVersion() } func (o *InstallOptions) configKB09() error { @@ -212,3 +217,27 @@ func (o *InstallOptions) setGlobalResourcesHelmOwner() error { // update BackupRepo return util.SetHelmOwner(o.Dynamic, types.BackupRepoGVR(), types.KubeBlocksChartName, o.HelmCfg.Namespace(), []string{fmt.Sprintf("%s-backuprepo", types.KubeBlocksChartName)}) } + +func (o *InstallOptions) setCRDAPIVersion() error { + setCRDAPIVersionAnnotation := func(list *unstructured.UnstructuredList, version string) error { + patchOP := fmt.Sprintf(`[{"op": "replace", "path": "/metadata/annotations/kubeblocks.io~1crd-api-version", "value": "apps.kubeblocks.io/%s"}]`, version) + for _, v := range list.Items { + if v.GetLabels()[constant.CRDAPIVersionAnnotationKey] != "" { + continue + } + if _, err := o.Dynamic.Resource(types.CompDefAlpha1GVR()).Namespace("").Patch(context.TODO(), v.GetName(), + k8stypes.JSONPatchType, []byte(patchOP), metav1.PatchOptions{}); client.IgnoreNotFound(err) != nil { + return err + } + } + return nil + } + compDefs, err := o.Dynamic.Resource(types.CompDefAlpha1GVR()).Namespace("").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return err + } + if err = setCRDAPIVersionAnnotation(compDefs, types.AppsAPIVersion); err != nil { + return err + } + return nil +} diff --git a/pkg/types/types.go b/pkg/types/types.go index 51f49886c..4183def0c 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -118,6 +118,7 @@ const ( ResourceConfigurationVersions = "configurations" KindCluster = "Cluster" KindClusterDef = "ClusterDefinition" + KindComponentVersion = "ComponentVersion" KindConfigConstraint = "ConfigConstraint" KindConfiguration = "Configuration" KindBackup = "Backup" @@ -300,10 +301,18 @@ func ClusterDefGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceClusterDefs} } +func ClusterDefV1alphaGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIVersion, Resource: ResourceClusterDefs} +} + func CompDefGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceComponentDefs} } +func CompDefAlpha1GVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIVersion, Resource: ResourceComponentDefs} +} + func ComponentGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceComponents} } From 32533f7aec49ef4126994191ca852a58c2702dfa Mon Sep 17 00:00:00 2001 From: a le <101848970+1aal@users.noreply.github.com> Date: Thu, 28 Dec 2023 13:54:57 +0800 Subject: [PATCH 23/34] chore: add _category_.yml for user docs --- docs/user_docs/cli/_category_.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/user_docs/cli/_category_.yml diff --git a/docs/user_docs/cli/_category_.yml b/docs/user_docs/cli/_category_.yml new file mode 100644 index 000000000..dff53aca8 --- /dev/null +++ b/docs/user_docs/cli/_category_.yml @@ -0,0 +1,5 @@ +position: 30 +label: Command Line +collapsible: true +collapsed: true +className: hide-children \ No newline at end of file From a03b5e35b314f13ee1b3bca1f2e045c9e0db350a Mon Sep 17 00:00:00 2001 From: wangyelei Date: Fri, 27 Dec 2024 18:08:52 +0800 Subject: [PATCH 24/34] feat: support to convert cluster to v1 api version (#526) Co-authored-by: wangyelei (cherry picked from commit 131c0cf0cd4ecdb9fbfe71a75eb604501a6a424a) --- docs/user_docs/cli/cli.md | 1 + docs/user_docs/cli/kbcli_cluster.md | 1 + go.mod | 10 +- go.sum | 20 +- pkg/cmd/addon/addon.go | 37 +- pkg/cmd/cluster/cluster.go | 6 + pkg/cmd/cluster/covert_to_1.0.go | 563 ++++++++++++++++++++++++++++ pkg/types/types.go | 8 +- 8 files changed, 597 insertions(+), 49 deletions(-) create mode 100644 pkg/cmd/cluster/covert_to_1.0.go diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 56b5b50b7..5407a55b4 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -40,6 +40,7 @@ Cluster command. * [kbcli cluster cancel-ops](kbcli_cluster_cancel-ops.md) - Cancel the pending/creating/running OpsRequest which type is vscale or hscale. * [kbcli cluster configure](kbcli_cluster_configure.md) - Configure parameters with the specified components in the cluster. * [kbcli cluster connect](kbcli_cluster_connect.md) - Connect to a cluster or instance. +* [kbcli cluster convert-to-v1](kbcli_cluster_convert-to-v1.md) - convert cluster api version. * [kbcli cluster create](kbcli_cluster_create.md) - Create a cluster. * [kbcli cluster custom-ops](kbcli_cluster_custom-ops.md) - * [kbcli cluster delete](kbcli_cluster_delete.md) - Delete clusters. diff --git a/docs/user_docs/cli/kbcli_cluster.md b/docs/user_docs/cli/kbcli_cluster.md index 075601e9f..a89b49262 100644 --- a/docs/user_docs/cli/kbcli_cluster.md +++ b/docs/user_docs/cli/kbcli_cluster.md @@ -41,6 +41,7 @@ Cluster command. * [kbcli cluster cancel-ops](kbcli_cluster_cancel-ops.md) - Cancel the pending/creating/running OpsRequest which type is vscale or hscale. * [kbcli cluster configure](kbcli_cluster_configure.md) - Configure parameters with the specified components in the cluster. * [kbcli cluster connect](kbcli_cluster_connect.md) - Connect to a cluster or instance. +* [kbcli cluster convert-to-v1](kbcli_cluster_convert-to-v1.md) - convert cluster api version. * [kbcli cluster create](kbcli_cluster_create.md) - Create a cluster. * [kbcli cluster custom-ops](kbcli_cluster_custom-ops.md) - * [kbcli cluster delete](kbcli_cluster_delete.md) - Delete clusters. diff --git a/go.mod b/go.mod index 1a1243c1c..b0021f349 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,9 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/briandowns/spinner v1.23.0 github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad - github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.1.1 - github.com/charmbracelet/lipgloss v0.13.0 + github.com/charmbracelet/lipgloss v1.0.0 github.com/containerd/stargz-snapshotter/estargz v0.15.1 github.com/containers/common v0.60.2 github.com/docker/go-connections v0.5.0 @@ -42,7 +42,8 @@ require ( github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851 github.com/replicatedhq/troubleshoot v0.57.0 github.com/robfig/cron/v3 v3.0.1 - github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f + github.com/sahilm/fuzzy v0.1.1 + github.com/sergi/go-diff v1.3.1 github.com/sethvargo/go-password v0.2.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cast v1.7.0 @@ -109,7 +110,7 @@ require ( github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/ansi v0.4.2 // indirect github.com/charmbracelet/x/term v0.2.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/clbanning/mxj/v2 v2.5.7 // indirect @@ -282,7 +283,6 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/segmentio/ksuid v1.0.4 // indirect - github.com/sergi/go-diff v1.3.1 // indirect github.com/shirou/gopsutil/v3 v3.24.5 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect diff --git a/go.sum b/go.sum index 67bd7a601..ec59c0000 100644 --- a/go.sum +++ b/go.sum @@ -746,16 +746,16 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad h1:DVxCvjXlmkm4idu4bAbI9P+D99BsVHTKOKbzRYTlFwU= github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad/go.mod h1:Yi/tSmvDrnFgyZN4bsXm3gfXrp3zo1uytHmnPEYfquM= -github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= -github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= -github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= -github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= -github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= -github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= -github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk= +github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= +github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= @@ -1588,8 +1588,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= -github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc= diff --git a/pkg/cmd/addon/addon.go b/pkg/cmd/addon/addon.go index 37114dae1..bdff17258 100644 --- a/pkg/cmd/addon/addon.go +++ b/pkg/cmd/addon/addon.go @@ -236,7 +236,7 @@ func newEnableCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. util.CheckErr(o.validate()) util.CheckErr(o.complete(o, cmd, []string{name})) util.CheckErr(o.CmdComplete(cmd)) - util.CheckErr(o.takeOver09AddonGlobalResources()) + util.CheckErr(o.process09ClusterDefAndComponentVersions()) util.CheckErr(o.Run()) if isEngineAddon(&o.addon) { util.CheckErr(clusterCmd.RegisterClusterChart(f, streams, "", name, getAddonVersion(&o.addon), types.ClusterChartsRepoURL)) @@ -1035,7 +1035,7 @@ func (o *addonCmdOpts) checkBeforeDisable() error { return CheckAddonUsedByCluster(o.dynamic, o.Names, o.In) } -func (o *addonCmdOpts) takeOver09AddonGlobalResources() error { +func (o *addonCmdOpts) process09ClusterDefAndComponentVersions() error { kbDeploys, err := util.GetKBDeploys(o.client, util.KubeblocksAppComponent, metav1.NamespaceAll) if err != nil || len(kbDeploys) < 2 { return err @@ -1050,33 +1050,8 @@ func (o *addonCmdOpts) takeOver09AddonGlobalResources() error { break } } - selector := metav1.ListOptions{ - LabelSelector: fmt.Sprintf("%s=%s", constant.AppNameLabelKey, o.addon.Name), - } - takeOverRelease := func(gvr schema.GroupVersionResource) error { - cdList, err := o.dynamic.Resource(gvr).Namespace("").List(context.TODO(), selector) - if err != nil { - return err - } - for _, v := range cdList.Items { - if err = util.SetHelmOwner(o.dynamic, gvr, "kb-addon-"+o.addon.Name, newKBNamespace, []string{v.GetName()}); err != nil { - return err - } - } - return nil - } - if err = takeOverRelease(types.ClusterDefGVR()); err != nil { - return err - } - if err = takeOverRelease(types.ComponentVersionsGVR()); err != nil { - return err - } - return o.overwriteCDAndCMPVSpecWith10Version(newKBNamespace) -} - -func (o *addonCmdOpts) overwriteCDAndCMPVSpecWith10Version(namespace string) error { // 1. get manifests from the helm repo - chartsDownloader, err := helm.NewDownloader(helm.NewConfig(namespace, "", "", false)) + chartsDownloader, err := helm.NewDownloader(helm.NewConfig(newKBNamespace, "", "", false)) if err != nil { return err } @@ -1086,7 +1061,7 @@ func (o *addonCmdOpts) overwriteCDAndCMPVSpecWith10Version(namespace string) err return err } // 2. overwrite the spec of ClusterDefinition and ComponentVersion with the new version - actionCfg, err := helm.NewActionConfig(helm.NewConfig(namespace, "", "", false)) + actionCfg, err := helm.NewActionConfig(helm.NewConfig(newKBNamespace, "", "", false)) if err != nil { return err } @@ -1096,7 +1071,7 @@ func (o *addonCmdOpts) overwriteCDAndCMPVSpecWith10Version(namespace string) err } renderer := helmaction.NewInstall(actionCfg) renderer.ReleaseName = o.addon.Name + "for-upgrade" - renderer.Namespace = namespace + renderer.Namespace = newKBNamespace renderer.DryRun = true renderer.Replace = true renderer.ClientOnly = true @@ -1129,6 +1104,8 @@ func (o *addonCmdOpts) overwriteCDAndCMPVSpecWith10Version(namespace string) err } annotations := targetObj.GetAnnotations() annotations[constant.CRDAPIVersionAnnotationKey] = kbappsv1.GroupVersion.String() + annotations["meta.helm.sh/release-name"] = "kb-addon-" + o.addon.Name + annotations["meta.helm.sh/release-namespace"] = newKBNamespace targetObj.SetAnnotations(annotations) targetObj.Object["spec"] = unstructuredObj.Object["spec"] if _, err = o.dynamic.Resource(gvr).Namespace("").Update(context.TODO(), targetObj, metav1.UpdateOptions{}); err != nil { diff --git a/pkg/cmd/cluster/cluster.go b/pkg/cmd/cluster/cluster.go index fd23585a4..cc0a81423 100644 --- a/pkg/cmd/cluster/cluster.go +++ b/pkg/cmd/cluster/cluster.go @@ -103,6 +103,12 @@ func NewClusterCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra NewListLogsCmd(f, streams), }, }, + { + Message: "Convert API version Commands:", + Commands: []*cobra.Command{ + NewConvertToV1Cmd(f, streams), + }, + }, } // add subcommands diff --git a/pkg/cmd/cluster/covert_to_1.0.go b/pkg/cmd/cluster/covert_to_1.0.go new file mode 100644 index 000000000..bd5c613eb --- /dev/null +++ b/pkg/cmd/cluster/covert_to_1.0.go @@ -0,0 +1,563 @@ +/* +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 cluster + +import ( + "context" + "fmt" + "slices" + "strings" + + kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + kbappsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + "github.com/apecloud/kubeblocks/pkg/constant" + "github.com/charmbracelet/lipgloss" + "github.com/fatih/color" + "github.com/sergi/go-diff/diffmatchpatch" + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + apiruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/cli-runtime/pkg/genericiooptions" + "k8s.io/client-go/dynamic" + clientset "k8s.io/client-go/kubernetes" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" + + "github.com/apecloud/kbcli/pkg/printer" + "github.com/apecloud/kbcli/pkg/types" + "github.com/apecloud/kbcli/pkg/util" + "github.com/apecloud/kbcli/pkg/util/prompt" +) + +const ( + kbIncrementConverterAK = "kb-increment-converter" + + escape = "\x1b" +) + +type componentDefRefConvert struct { + cmpdPrefix string + serviceVersion string +} + +var ( + clusterVersionConvert = map[string]map[string]componentDefRefConvert{} + // 0.9 cmpd name => 1.0 cmpd prefix + componentDefWithChartVersion = []string{ + "clickhouse-24", + "elasticsearch-8", + "kafka-combine", "kafka-controller", "kafka-exporter", "kafka-broker", + "loki-backend", "loki-gateway", "loki-write", "loki-read", + "milvus-datanode", "milvus-indexnode", "milvus-minio", + "milvus-mixcoord", "milvus-proxy", "milvus-querynode", "milvus-standalone", + "minio", + "mongodb", + "mysql-8.0", "mysql-8.4", "mysql-5.7", + "ob-ce", + "orchestrator-raft", "orchestrator-shareend", + "postgresql-12", "postgresql-14", "postgresql-15", "postgresql-16", + "qdrant", + "rabbitmq", + "redis-7", "redis-cluster-7", "redis-sentinel-7", "redis-twemproxy-0.5", + "pulsar-bookkeeper", "pulsar-broker-3", "pulsar-proxy-3", "pulsar-zookeeper-3", + "starrocks-ce-be", "starrocks-ce-fe", + "tidb-pd-7", "tidb-7", "tikv-7", "tikv-8", "tidb-8", "tidb-pd-8", + "vanilla-postgresql-12", "vanilla-postgresql-14", "vanilla-postgresql-15", "vanilla-postgresql-supabase15", + "vm-insert", "vm-select", "vm-storage", + "weaviate", + "zookeeper", + } + + // 0.9 cmpd prefix => 1.0 cmpd prefix + componentDefPrefixConvert = map[string]string{ + "etcd-": "etcd-3", + "ch-keeper-24": "clickhouse-keeper-24", + "pulsar-bkrecovery": "pulsar-bookies-recovery-3", + } +) + +func init() { + registerCVConvert := func(cvName string, cvConvert map[string]componentDefRefConvert) { + clusterVersionConvert[cvName] = cvConvert + } + // 1. apecloud-mysql + apeMysqlCompDefConvert := componentDefRefConvert{cmpdPrefix: "apecloud-mysql", serviceVersion: "8.0.30"} + apeMysqlCVConvert := map[string]componentDefRefConvert{ + "mysql": apeMysqlCompDefConvert, + "vtcontroller": apeMysqlCompDefConvert, + "vtgate": apeMysqlCompDefConvert, + } + registerCVConvert("ac-mysql-8.0.30", apeMysqlCVConvert) + registerCVConvert("ac-mysql-8.0.30-1", apeMysqlCVConvert) + + registerCVConvert("clickhouse-24.8.3", map[string]componentDefRefConvert{ + "clickhouse": {cmpdPrefix: "clickhouse-24", serviceVersion: "24.8.3"}, + "ch-keeper": {cmpdPrefix: "clickhouse-keeper-24", serviceVersion: "24.8.3"}, + // Warning: 3.8.0->3.8.4 + "zookeeper": {cmpdPrefix: "zookeeper", serviceVersion: "3.8.4"}, + }) + + registerCVConvert("elasticsearch-8.8.2", map[string]componentDefRefConvert{ + "elasticsearch": {cmpdPrefix: "elasticsearch-8", serviceVersion: "8.8.2"}, + }) + + registerCVConvert("etcd-v3.5.6", map[string]componentDefRefConvert{ + "etcd": {cmpdPrefix: "etcd-3", serviceVersion: "3.5.6"}, + }) + + registerCVConvert("greptimedb-0.3.2", map[string]componentDefRefConvert{ + "datanode": {cmpdPrefix: "greptimedb-datanode", serviceVersion: "0.3.2"}, + // Warning: 3.5.5->3.5.6 + "etcd": {cmpdPrefix: "etcd-3", serviceVersion: "3.5.6"}, + "meta": {cmpdPrefix: "greptimedb-meta", serviceVersion: "0.3.2"}, + "frontend": {cmpdPrefix: "greptimedb-frontend", serviceVersion: "0.3.2"}, + }) + registerCVConvert("influxdb-2.7.4", map[string]componentDefRefConvert{ + "influxdb": {cmpdPrefix: "influxdb", serviceVersion: "2.7.4"}, + }) + registerCVConvert("kafka-3.3.2", map[string]componentDefRefConvert{ + "kafka-server": {cmpdPrefix: "kafka-combine", serviceVersion: "3.3.2"}, + "kafka-broker": {cmpdPrefix: "kafka-broker", serviceVersion: "3.3.2"}, + "controller": {cmpdPrefix: "kafka-controller", serviceVersion: "3.3.2"}, + "kafka-exporter": {cmpdPrefix: "kafka-exporter", serviceVersion: "3.3.2"}, + }) + registerCVConvert("mariadb-10.6.15", map[string]componentDefRefConvert{ + "mariadb-compdef": {cmpdPrefix: "mariadb", serviceVersion: "10.6.15"}, + }) + registerCVConvert("mogdb-5.0.5", map[string]componentDefRefConvert{ + "mogdb": {cmpdPrefix: "mogdb", serviceVersion: "5.0.5"}, + }) + // mongodb + registerCVConvert("mongodb-5.0", map[string]componentDefRefConvert{ + "mongodb": {cmpdPrefix: "mongodb", serviceVersion: "5.0.28"}, + }) + registerCVConvert("mongodb-6.0", map[string]componentDefRefConvert{ + "mongodb": {cmpdPrefix: "mongodb", serviceVersion: "6.0.16"}, + }) + registerCVConvert("mongodb-4.4", map[string]componentDefRefConvert{ + "mongodb": {cmpdPrefix: "mongodb", serviceVersion: "4.4.29"}, + }) + registerCVConvert("mongodb-4.2", map[string]componentDefRefConvert{ + "mongodb": {cmpdPrefix: "mongodb", serviceVersion: "4.2.24"}, + }) + + registerCVConvert("mysql-8.0.33", map[string]componentDefRefConvert{ + "mysql": {cmpdPrefix: "mysql-8.0", serviceVersion: "8.0.33"}, + }) + registerCVConvert("mysql-5.7.44", map[string]componentDefRefConvert{ + "mysql": {cmpdPrefix: "mysql-5.7", serviceVersion: "5.7.44"}, + }) + registerCVConvert("mysql-8.4.2", map[string]componentDefRefConvert{ + "mysql": {cmpdPrefix: "mysql-8.4", serviceVersion: "8.4.2"}, + }) + registerCVConvert("nebula-v3.5.0", map[string]componentDefRefConvert{ + "nebula-console": {cmpdPrefix: "nebula-console", serviceVersion: "3.5.0"}, + "nebula-graphd": {cmpdPrefix: "nebula-graphd", serviceVersion: "3.5.0"}, + "nebula-metad": {cmpdPrefix: "nebula-metad", serviceVersion: "3.5.0"}, + "nebula-storaged": {cmpdPrefix: "nebula-storaged", serviceVersion: "3.5.0"}, + }) + registerCVConvert("neon-pg14-1.0.0", map[string]componentDefRefConvert{ + "neon-compute": {cmpdPrefix: "nneon-compute", serviceVersion: "1.0.0"}, + "neon-storagebroker": {cmpdPrefix: "neon-storagebroker", serviceVersion: "1.0.0"}, + "neon-safekeeper": {cmpdPrefix: "neon-safekeeper", serviceVersion: "1.0.0"}, + "neon-pageserver": {cmpdPrefix: "neon-pageserver", serviceVersion: "1.0.0"}, + }) + registerCVConvert("ob-ce-4.3.0.1-100000242024032211", map[string]componentDefRefConvert{ + "ob-ce": {cmpdPrefix: "oceanbase-ce", serviceVersion: "4.3.0.1"}, + }) + registerCVConvert("opensearch-2.7.0", map[string]componentDefRefConvert{ + "opensearch": {cmpdPrefix: "opensearch", serviceVersion: "2.7.0"}, + }) + registerCVConvert("orioledb-beta1", map[string]componentDefRefConvert{ + "orioledb": {cmpdPrefix: "orioledb", serviceVersion: "14.7.2"}, + }) + registerCVConvert("polardbx-v2.3", map[string]componentDefRefConvert{ + "gms": {cmpdPrefix: "polardbx-gms", serviceVersion: "2.3.0"}, + "dn": {cmpdPrefix: "polardbx-dn", serviceVersion: "2.3.0"}, + "cn": {cmpdPrefix: "polardbx-cn", serviceVersion: "2.3.0"}, + "cdc": {cmpdPrefix: "polardbx-cdc", serviceVersion: "2.3.0"}, + }) + // postgresql + registerCVConvert("postgresql-14.7.2", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-14", serviceVersion: "14.7.2"}, + }) + registerCVConvert("postgresql-12.14.1", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-12", serviceVersion: "12.14.1"}, + }) + registerCVConvert("postgresql-12.15.0", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-12", serviceVersion: "12.15.0"}, + }) + registerCVConvert("postgresql-14.8.0", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-14", serviceVersion: "14.8.0"}, + }) + registerCVConvert("postgresql-12.14.0", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-12", serviceVersion: "12.14.0"}, + }) + registerCVConvert("postgresql-15.7.0", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-15", serviceVersion: "15.7.0"}, + }) + registerCVConvert("postgresql-16.4.0", map[string]componentDefRefConvert{ + "postgresql": {cmpdPrefix: "postgresql-16", serviceVersion: "16.4.0"}, + }) + // pulsar + registerCVConvert("pulsar-2.11.2", map[string]componentDefRefConvert{ + "bookies": {cmpdPrefix: "pulsar-bookkeeper-2", serviceVersion: "2.11.2"}, + "bookies-recovery": {cmpdPrefix: "pulsar-bookies-recovery-2", serviceVersion: "2.11.2"}, + "pulsar-broker": {cmpdPrefix: "pulsar-broker-2", serviceVersion: "2.11.2"}, + "zookeeper": {cmpdPrefix: "pulsar-zookeeper-2", serviceVersion: "2.11.2"}, + "pulsar-proxy": {cmpdPrefix: "pulsar-proxy-2", serviceVersion: "2.11.2"}, + }) + registerCVConvert("pulsar-3.0.2", map[string]componentDefRefConvert{ + "bookies": {cmpdPrefix: "pulsar-bookkeeper-3", serviceVersion: "3.0.2"}, + "bookies-recovery": {cmpdPrefix: "pulsar-bookies-recovery-3", serviceVersion: "3.0.2"}, + "pulsar-broker": {cmpdPrefix: "pulsar-broker-3", serviceVersion: "3.0.2"}, + "zookeeper": {cmpdPrefix: "pulsar-zookeeper-3", serviceVersion: "3.0.2"}, + "pulsar-proxy": {cmpdPrefix: "pulsar-proxy-3", serviceVersion: "3.0.2"}, + }) + // qdrant + registerCVConvert("qdrant-1.5.0", map[string]componentDefRefConvert{ + "qdrant": {cmpdPrefix: "qdrant", serviceVersion: "1.5.0"}, + }) + registerCVConvert("qdrant-1.7.3", map[string]componentDefRefConvert{ + "qdrant": {cmpdPrefix: "qdrant", serviceVersion: "1.7.3"}, + }) + registerCVConvert("qdrant-1.8.1", map[string]componentDefRefConvert{ + "qdrant": {cmpdPrefix: "qdrant", serviceVersion: "1.8.1"}, + }) + registerCVConvert("qdrant-1.8.4", map[string]componentDefRefConvert{ + "qdrant": {cmpdPrefix: "qdrant", serviceVersion: "1.8.4"}, + }) + registerCVConvert("qdrant-1.10.0", map[string]componentDefRefConvert{ + "qdrant": {cmpdPrefix: "qdrant", serviceVersion: "1.10.0"}, + }) + // redis + registerCVConvert("redis-7.2.4", map[string]componentDefRefConvert{ + "redis": {cmpdPrefix: "redis-7", serviceVersion: "7.2.4"}, + }) + registerCVConvert("redis-7.0.6", map[string]componentDefRefConvert{ + "redis": {cmpdPrefix: "redis-7", serviceVersion: "7.0.6"}, + }) + registerCVConvert("risingwave-v1.0.0", map[string]componentDefRefConvert{ + "meta": {cmpdPrefix: "risingwave-meta", serviceVersion: "v1.0.0"}, + "frontend": {cmpdPrefix: "risingwave-frontend", serviceVersion: "v1.0.0"}, + "compute": {cmpdPrefix: "risingwave-compute", serviceVersion: "v1.0.0"}, + "compactor": {cmpdPrefix: "risingwave-compactor", serviceVersion: "v1.0.0"}, + "connector": {cmpdPrefix: "risingwave-connector", serviceVersion: "v1.0.0"}, + }) + registerCVConvert("tdengine-3.0.5.0", map[string]componentDefRefConvert{ + "tdengine": {cmpdPrefix: "tdengine", serviceVersion: "3.0.5"}, + }) + registerCVConvert("weaviate-1.18.0", map[string]componentDefRefConvert{ + "weaviate": {cmpdPrefix: "weaviate", serviceVersion: "1.19.6"}, + }) + registerCVConvert("yashandb-personal-23.1.1.100", map[string]componentDefRefConvert{ + "yashandb-compdef": {cmpdPrefix: "yashandb", serviceVersion: "23.1.1-100"}, + }) +} + +type ConvertToV1Options struct { + Cmd *cobra.Command `json:"-"` + + Client clientset.Interface + f cmdutil.Factory + Dynamic dynamic.Interface + DryRun bool + Name string + Namespace string + genericiooptions.IOStreams + compDefList *unstructured.UnstructuredList +} + +func NewConvertToV1Option(f cmdutil.Factory, streams genericiooptions.IOStreams) *ConvertToV1Options { + return &ConvertToV1Options{ + f: f, + IOStreams: streams, + } +} + +var convertExample = templates.Examples(` + # convert a v1alpha1 cluster + kbcli cluster convert-to-v1 mycluster + + # convert a v1alpha1 cluster with --dry-run + kbcli cluster convert-to-v1 mycluster --dry-run +`) + +func NewConvertToV1Cmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { + o := NewConvertToV1Option(f, streams) + cmd := &cobra.Command{ + Use: "convert-to-v1 [NAME]", + Short: "convert cluster api version.", + Example: convertExample, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.BehaviorOnFatal(printer.FatalWithRedColor) + cmdutil.CheckErr(o.complete(args)) + cmdutil.CheckErr(o.Run()) + }, + } + cmd.Flags().BoolVar(&o.DryRun, "dry-run", false, "dry run mode") + return cmd +} + +func (o *ConvertToV1Options) complete(args []string) error { + + if len(args) == 0 { + return fmt.Errorf("must specify cluster name") + } + o.Name = args[0] + o.Namespace, _, _ = o.f.ToRawKubeConfigLoader().Namespace() + o.Dynamic, _ = o.f.DynamicClient() + o.Client, _ = o.f.KubernetesClientSet() + return nil +} + +func (o *ConvertToV1Options) GetConvertedCluster() (*kbappsv1.Cluster, *kbappsv1alpha1.Cluster, bool, error) { + cluster := &kbappsv1.Cluster{} + err := util.GetK8SClientObject(o.Dynamic, cluster, types.ClusterGVR(), o.Namespace, o.Name) + if err != nil { + return nil, nil, false, err + } + if cluster.Annotations[constant.CRDAPIVersionAnnotationKey] == kbappsv1.GroupVersion.String() { + return nil, nil, false, fmt.Errorf("cluster %s is already v1", o.Name) + } + // 1. get cluster v1alpha1 spec + clusterV1alpha1 := &kbappsv1alpha1.Cluster{} + err = util.GetK8SClientObject(o.Dynamic, clusterV1alpha1, types.ClusterV1alphaGVR(), o.Namespace, o.Name) + if err != nil { + return nil, nil, false, err + } + o.compDefList, err = o.Dynamic.Resource(types.CompDefGVR()).Namespace("").List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, nil, false, err + } + + var existUnsupportedSpec bool + if len(clusterV1alpha1.Spec.ClusterVersionRef) > 0 { + if err := o.ConvertForClusterVersion(cluster, clusterV1alpha1.Spec, &existUnsupportedSpec); err != nil { + return nil, nil, false, err + } + } else { + if err := o.Convert09ComponentDef(cluster, clusterV1alpha1.Spec, &existUnsupportedSpec); err != nil { + return nil, nil, false, err + } + } + delete(cluster.Annotations, kbIncrementConverterAK) + cluster.Annotations[constant.CRDAPIVersionAnnotationKey] = kbappsv1.GroupVersion.String() + return cluster, clusterV1alpha1, existUnsupportedSpec, nil +} + +func (o *ConvertToV1Options) Run() error { + cluster, clusterV1alpha1, existUnsupportedSpec, err := o.GetConvertedCluster() + if err != nil { + return err + } + o.printDiff(clusterV1alpha1.DeepCopy(), cluster.DeepCopy()) + if existUnsupportedSpec { + return fmt.Errorf(`cluster "%s" has unknown clusterVersion or componentDefinition, you can replace with accorrding ComponentDefinition with 1.0 api`, o.Name) + } + fmt.Println(printer.BoldYellow(fmt.Sprintf("Cluster %s will be converted to v1 with output as yaml.", o.Name))) + if o.DryRun { + return nil + } + if err = prompt.Confirm(nil, o.In, "", "Please type 'Yes/yes' to confirm your operation:"); err != nil { + return err + } + if len(clusterV1alpha1.Spec.ClusterVersionRef) > 0 { + if err = o.convertCredential(clusterV1alpha1.Spec.ClusterDefRef); err != nil { + return err + } + } + // convert to v1 + clusterObj, err := apiruntime.DefaultUnstructuredConverter.ToUnstructured(cluster) + if err != nil { + return err + } + _, err = o.Dynamic.Resource(types.ClusterGVR()).Namespace(o.Namespace).Update(context.TODO(), &unstructured.Unstructured{Object: clusterObj}, metav1.UpdateOptions{}) + if err != nil { + return err + } + output := fmt.Sprintf("Cluster %s has converted successfully, you can view the spec:", o.Name) + printer.PrintLine(output) + nextLine := fmt.Sprintf("\tkubectl get clusters.apps.kubeblocks.io %s -n %s -oyaml", o.Name, o.Namespace) + printer.PrintLine(nextLine) + return nil +} + +func (o *ConvertToV1Options) convertCredential(cdName string) error { + oldSecret := &corev1.Secret{} + err := util.GetK8SClientObject(o.Dynamic, oldSecret, types.SecretGVR(), o.Namespace, fmt.Sprintf("%s-conn-credential", o.Name)) + if err != nil { + return err + } + var ( + compName string + accountName string + ) + // TODO: support all cluster definition + switch cdName { + case "postgresql": + compName = "postgresql" + accountName = "postgres" + default: + return fmt.Errorf("unknown cluster definition %s", cdName) + } + newSecret := &corev1.Secret{} + newSecret.Name = constant.GenerateAccountSecretName(o.Name, compName, accountName) + newSecret.Namespace = oldSecret.Namespace + newSecret.Labels = constant.GetCompLabels(o.Name, compName) + newSecret.Data = map[string][]byte{ + "username": oldSecret.Data["username"], + "password": oldSecret.Data["password"], + } + if _, err := o.Client.CoreV1().Secrets(oldSecret.Namespace).Create(context.TODO(), newSecret, metav1.CreateOptions{}); err != nil { + return client.IgnoreAlreadyExists(err) + } + return nil +} + +func (o *ConvertToV1Options) getLatestComponentDef(componentDefPrefix string) (string, error) { + var matchedCompDefs []string + for _, v := range o.compDefList.Items { + if v.GetAnnotations()[constant.CRDAPIVersionAnnotationKey] != kbappsv1.GroupVersion.String() { + continue + } + if strings.HasPrefix(v.GetName(), componentDefPrefix) { + matchedCompDefs = append(matchedCompDefs, v.GetName()) + } + } + if len(matchedCompDefs) == 0 { + return "", fmt.Errorf("no matched componentDefinition for componentDef %s", componentDefPrefix) + } + // TODO: sort with semantic version? + slices.Sort(matchedCompDefs) + return matchedCompDefs[len(matchedCompDefs)-1], nil +} + +func (o *ConvertToV1Options) ConvertForClusterVersion(cluster *kbappsv1.Cluster, + clusterV1alpha1Spec kbappsv1alpha1.ClusterSpec, + existUnsupportedSpec *bool) error { + convert, ok := clusterVersionConvert[clusterV1alpha1Spec.ClusterVersionRef] + if !ok { + *existUnsupportedSpec = true + return nil + } + for i := range clusterV1alpha1Spec.ComponentSpecs { + compDefRef := clusterV1alpha1Spec.ComponentSpecs[i].ComponentDefRef + compDefConVert, ok := convert[compDefRef] + if !ok { + *existUnsupportedSpec = true + cluster.Spec.ComponentSpecs[i].ComponentDef = "" + cluster.Spec.ComponentSpecs[i].ServiceVersion = "" + continue + } + compDef, err := o.getLatestComponentDef(compDefConVert.cmpdPrefix) + if err != nil { + return err + } + cluster.Spec.ComponentSpecs[i].ComponentDef = compDef + cluster.Spec.ComponentSpecs[i].ServiceVersion = compDefConVert.serviceVersion + } + // remove deprecated v1alpha1 + cluster.Spec.ClusterDef = "" + return nil +} + +func (o *ConvertToV1Options) Convert09ComponentDef(cluster *kbappsv1.Cluster, + clusterV1alpha1Spec kbappsv1alpha1.ClusterSpec, + existUnsupportedSpec *bool) error { + componentDefWithChartVersionSet := sets.New(componentDefWithChartVersion...) + convertCompDef := func(compDef string) (string, error) { + if componentDefWithChartVersionSet.Has(compDef) { + return o.getLatestComponentDef(compDef) + } + for k, cmpdDefPrefix := range componentDefPrefixConvert { + if strings.HasPrefix(compDef, k) { + return o.getLatestComponentDef(cmpdDefPrefix) + } + } + *existUnsupportedSpec = true + return "", nil + } + for i := range clusterV1alpha1Spec.ComponentSpecs { + compDef, err := convertCompDef(clusterV1alpha1Spec.ComponentSpecs[i].ComponentDef) + if err != nil { + return err + } + cluster.Spec.ComponentSpecs[i].ComponentDef = compDef + } + for i := range clusterV1alpha1Spec.ShardingSpecs { + compDef, err := convertCompDef(clusterV1alpha1Spec.ShardingSpecs[i].Template.ComponentDef) + if err != nil { + return err + } + cluster.Spec.Shardings[i].Template.ComponentDef = compDef + } + delete(cluster.Annotations, kbIncrementConverterAK) + return nil +} + +func (o *ConvertToV1Options) printDiff(clusterV1Alpha1 *kbappsv1alpha1.Cluster, clusterV1 *kbappsv1.Cluster) { + clusterV1Alpha1.Status = kbappsv1alpha1.ClusterStatus{} + clusterV1Alpha1.ObjectMeta.ManagedFields = nil + clusterV1.Status = kbappsv1.ClusterStatus{} + clusterV1.ObjectMeta.ManagedFields = nil + clusterV1Alpha1Srr, _ := yaml.Marshal(clusterV1Alpha1) + clusterV1Str, _ := yaml.Marshal(clusterV1) + + getDiffContext := func(isV1Cluster bool) string { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(string(clusterV1Alpha1Srr), string(clusterV1Str), false) + var diffStr string + for _, d := range diffs { + switch d.Type { + case diffmatchpatch.DiffInsert: + if isV1Cluster { + diffStr += o.colorGreen(d.Text) + } + case diffmatchpatch.DiffDelete: + if !isV1Cluster { + diffStr += o.colorRed(d.Text) + } + case diffmatchpatch.DiffEqual: + diffStr += d.Text + } + } + return diffStr + } + // Add a purple, rectangular border + var style = lipgloss.NewStyle(). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("63")) + fmt.Println(lipgloss.JoinHorizontal(lipgloss.Left, style.Render(getDiffContext(false)), " ", style.Render(getDiffContext(true)))) +} + +func (o *ConvertToV1Options) colorRed(str string) string { + return strings.ReplaceAll(color.RedString(str), "\n", fmt.Sprintf("%s[0m\n%s[31m", escape, escape)) +} + +func (o *ConvertToV1Options) colorGreen(str string) string { + return strings.ReplaceAll(color.GreenString(str), "\n", fmt.Sprintf("%s[0m\n%s[32m", escape, escape)) +} diff --git a/pkg/types/types.go b/pkg/types/types.go index 4183def0c..23647e3ad 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -297,12 +297,12 @@ func ClusterGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceClusters} } -func ClusterDefGVR() schema.GroupVersionResource { - return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceClusterDefs} +func ClusterV1alphaGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIVersion, Resource: ResourceClusters} } -func ClusterDefV1alphaGVR() schema.GroupVersionResource { - return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsAPIVersion, Resource: ResourceClusterDefs} +func ClusterDefGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: AppsAPIGroup, Version: AppsV1APIVersion, Resource: ResourceClusterDefs} } func CompDefGVR() schema.GroupVersionResource { From f0c1321073e06c97296b639124588809453541df Mon Sep 17 00:00:00 2001 From: zhangtao <111836083+sophon-zt@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:41:33 +0800 Subject: [PATCH 25/34] fix: edit-config subcommand does not take effect (#530) (cherry picked from commit 801709ebb54ea7127af892f1243e99709217fe34) --- pkg/cmd/cluster/config_edit.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/cluster/config_edit.go b/pkg/cmd/cluster/config_edit.go index 89ffc1464..152ba6612 100644 --- a/pkg/cmd/cluster/config_edit.go +++ b/pkg/cmd/cluster/config_edit.go @@ -93,7 +93,7 @@ func (o *editConfigOptions) Run(fn func() error) error { util.DisplayDiffWithColor(o.IOStreams.Out, diff) configSpec := wrapper.ConfigTemplateSpec() - if configSpec.ConfigConstraintRef != "" { + if hasSchemaForFile(configSpec, wrapper.ConfigFile()) { return o.runWithConfigConstraints(cfgEditContext, configSpec, fn) } @@ -110,6 +110,16 @@ func (o *editConfigOptions) Run(fn func() error) error { return fn() } +func hasSchemaForFile(configSpec *appsv1alpha1.ComponentConfigSpec, configFile string) bool { + if configSpec.ConfigConstraintRef == "" { + return false + } + if len(configSpec.Keys) == 0 || configFile == "" { + return true + } + return slices.Contains(configSpec.Keys, configFile) +} + func (o *editConfigOptions) runWithConfigConstraints(cfgEditContext *configEditContext, configSpec *appsv1alpha1.ComponentConfigSpec, fn func() error) error { oldVersion := map[string]string{ o.CfgFile: cfgEditContext.getOriginal(), From a2dadcf5593c23d2db78bba0cfb2e2dc7e498093 Mon Sep 17 00:00:00 2001 From: Shanshan Date: Mon, 6 Jan 2025 17:59:16 +0800 Subject: [PATCH 26/34] chore: update go.mod w.r.t trivy Severity (#536) --- go.mod | 82 ++++---- go.sum | 208 ++++++++++--------- pkg/cmd/trace/chart/summary/summary_chart.go | 2 +- 3 files changed, 156 insertions(+), 136 deletions(-) diff --git a/go.mod b/go.mod index b0021f349..e546499d7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/apecloud/kbcli -go 1.22.4 +go 1.22.7 require ( cuelang.org/go v0.8.0 @@ -16,20 +16,20 @@ require ( github.com/charmbracelet/bubbletea v1.1.1 github.com/charmbracelet/lipgloss v1.0.0 github.com/containerd/stargz-snapshotter/estargz v0.15.1 - github.com/containers/common v0.60.2 + github.com/containers/common v0.60.4 github.com/docker/go-connections v0.5.0 github.com/dustin/go-humanize v1.0.1 github.com/evanphx/json-patch v5.9.0+incompatible github.com/fatih/color v1.16.0 github.com/ghodss/yaml v1.0.0 - github.com/go-git/go-git/v5 v5.6.1 + github.com/go-git/go-git/v5 v5.11.0 github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/hc-install v0.5.2 github.com/hashicorp/terraform-exec v0.18.0 github.com/jedib0t/go-pretty/v6 v6.4.6 - github.com/k3d-io/k3d/v5 v5.6.0 + github.com/k3d-io/k3d/v5 v5.7.5 github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0 github.com/kubernetes-csi/external-snapshotter/client/v6 v6.2.0 github.com/leaanthony/debme v1.2.1 @@ -51,12 +51,13 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stoewer/go-strcase v1.2.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/xeipuuv/gojsonschema v1.2.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/sync v0.8.0 + golang.org/x/mod v0.20.0 + golang.org/x/sync v0.10.0 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.1 @@ -78,7 +79,7 @@ require ( require ( cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.38.0 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect @@ -95,7 +96,6 @@ require ( github.com/Microsoft/hcsshim v0.12.5 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/StudioSol/set v1.0.0 // indirect - github.com/acomagu/bufpipe v1.0.4 // indirect github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 // indirect github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/andybalholm/brotli v1.0.5 // indirect @@ -108,6 +108,7 @@ require ( github.com/bhmj/xpression v0.9.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/charmbracelet/x/ansi v0.4.2 // indirect @@ -117,9 +118,10 @@ require ( github.com/cloudflare/circl v1.3.7 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect - github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/containerd v1.7.19 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/containers/image/v5 v5.32.2 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.0 // indirect @@ -155,8 +157,8 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-git/gcfg v1.5.0 // indirect - github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect @@ -175,7 +177,7 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/goodhosts/hostsfile v0.1.1 // indirect + github.com/goodhosts/hostsfile v0.1.6 // indirect github.com/google/btree v1.1.2 // indirect github.com/google/cel-go v0.17.8 // indirect github.com/google/certificate-transparency-go v1.1.5 // indirect @@ -194,9 +196,10 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.0 // indirect + github.com/hashicorp/go-getter v1.7.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect @@ -226,6 +229,7 @@ require ( github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magefile/mage v1.15.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -240,6 +244,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.4.0 // indirect @@ -251,7 +256,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/montanaflynn/stats v0.6.6 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect @@ -260,7 +264,6 @@ require ( github.com/nsf/termbox-go v1.1.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/opencontainers/runc v1.1.13 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect @@ -276,7 +279,7 @@ require ( github.com/rancher/wharfie v0.6.2 // indirect github.com/redis/go-redis/v9 v9.5.3 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/rubenv/sql-migrate v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -286,7 +289,7 @@ require ( github.com/shirou/gopsutil/v3 v3.24.5 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/skeema/knownhosts v1.1.0 // indirect + github.com/skeema/knownhosts v1.2.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect @@ -317,29 +320,35 @@ require ( go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.15.1 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect - go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.28.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect + go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/api v0.171.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.65.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -355,13 +364,6 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) -replace ( - github.com/docker/cli => github.com/docker/cli v24.0.7+incompatible - github.com/docker/distribution => github.com/docker/distribution v2.8.2+incompatible - github.com/docker/docker => github.com/moby/moby v24.0.7+incompatible - github.com/spf13/afero => github.com/spf13/afero v1.2.2 -) - replace ( // golang.org/x/exp => golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb k8s.io/api => k8s.io/api v0.29.2 @@ -373,6 +375,7 @@ replace ( k8s.io/cloud-provider => k8s.io/cloud-provider v0.29.2 k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.29.2 k8s.io/code-generator => k8s.io/code-generator v0.29.2 + k8s.io/component-base => k8s.io/component-base v0.29.2 k8s.io/component-helpers => k8s.io/component-helpers v0.29.2 k8s.io/controller-manager => k8s.io/controller-manager v0.29.2 k8s.io/cri-api => k8s.io/cri-api v0.29.2 @@ -393,5 +396,4 @@ replace ( k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.2 k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.29.2 k8s.io/sample-controller => k8s.io/sample-controller v0.29.2 - k8s.io/component-base => k8s.io/component-base v0.29.2 ) diff --git a/go.sum b/go.sum index ec59c0000..2c63dd2a6 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -15,6 +16,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -178,8 +180,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -527,6 +529,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= @@ -644,7 +647,6 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NimbleMarkets/ntcharts v0.1.2 h1:iW1aiOif/Dm74sQd18opi10RMED5589cVhy9SGp98Tw= github.com/NimbleMarkets/ntcharts v0.1.2/go.mod h1:WcHS7kc8oQctN1543DeV9a+gOrS4DDVfKp1N9RZFUqc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= @@ -652,8 +654,6 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/O github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/StudioSol/set v1.0.0 h1:G27J71la+Da08WidabBkoRrvPLTa4cdCn0RjvyJ5WKQ= github.com/StudioSol/set v1.0.0/go.mod h1:hIUNZPo6rEGF43RlPXHq7Fjmf+HkVJBqAjtK7Z9LoIU= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412 h1:vOVO0ypMfTt6tZacyI0kp+iCZb1XSNiYDqnzBWYgfe4= github.com/ahmetalpbalkan/go-cursor v0.0.0-20131010032410-8136607ea412/go.mod h1:AI9hp1tkp10pAlK5TCwL+7yWbRgtDm9jhToq6qij2xs= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= @@ -726,14 +726,13 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0Bsq github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/bugsnag/panicwrap v1.3.4 h1:A6sXFtDGsgU/4BLf5JT0o5uYg3EeKgGx3Sfs+/uk3pU= github.com/bugsnag/panicwrap v1.3.4/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM= github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f h1:tRk+aBit+q3oqnj/1mF5HHhP2yxJM2lSa0afOJxQ3nE= github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= @@ -773,7 +772,6 @@ github.com/clbanning/mxj/v2 v2.5.7/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ= github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= @@ -794,18 +792,20 @@ github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEa github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= -github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= -github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= +github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE= +github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= -github.com/containers/common v0.60.2 h1:utcwp2YkO8c0mNlwRxsxfOiqfj157FRrBjxgjR6f+7o= -github.com/containers/common v0.60.2/go.mod h1:I0upBi1qJX3QmzGbUOBN1LVP6RvkKhd3qQpZbQT+Q54= +github.com/containers/common v0.60.4 h1:H5+LAMHPZEqX6vVNOQ+IguVsaFl8kbO/SZ/VPXjxhy0= +github.com/containers/common v0.60.4/go.mod h1:I0upBi1qJX3QmzGbUOBN1LVP6RvkKhd3qQpZbQT+Q54= github.com/containers/image/v5 v5.32.2 h1:SzNE2Y6sf9b1GJoC8qjCuMBXwQrACFp4p0RK15+4gmQ= github.com/containers/image/v5 v5.32.2/go.mod h1:v1l73VeMugfj/QtKI+jhYbwnwFCFnNGckvbST3rQ5Hk= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= @@ -818,8 +818,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U= -github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= +github.com/corpix/uarand v0.0.0-20170723150923-031be390f409 h1:9A+mfQmwzZ6KwUXPc8nHxFtKgn9VIvO3gXAOspIcE3s= +github.com/corpix/uarand v0.0.0-20170723150923-031be390f409/go.mod h1:JSm890tOkDN+M1jqN8pUGDKnzJrsVbJwSMHBY4zwz7M= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -846,10 +846,13 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= -github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= @@ -871,6 +874,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= @@ -936,15 +941,14 @@ github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3 github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= -github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= -github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= -github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk= -github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -1062,8 +1066,8 @@ github.com/golangplus/testing v1.0.0 h1:+ZeeiKZENNOMkTTELoSySazi+XaEhVO0mb+eanrS github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA= github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/goodhosts/hostsfile v0.1.1 h1:SqRUTFOshOCon0ZSXDrW1bkKZvs4+5pRgYFWySdaLno= -github.com/goodhosts/hostsfile v0.1.1/go.mod h1:lXcUP8xO4WR5vvuQ3F/N0bMQoclOtYKEEUnyY2jTusY= +github.com/goodhosts/hostsfile v0.1.6 h1:aK6DxpNV6pZ1NbdvNE2vYBMTnvIJF5O2J/8ZOlp2eMY= +github.com/goodhosts/hostsfile v0.1.6/go.mod h1:bkCocEIf3Ca0hcBustUZoWYhOgKUaIK+47m8fBjoBx8= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -1118,6 +1122,7 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -1163,6 +1168,7 @@ github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5i github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -1183,8 +1189,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1192,8 +1198,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.0 h1:bzrYP+qu/gMrL1au7/aDvkoOVGUJpeKBgbqRHACAFDY= -github.com/hashicorp/go-getter v1.7.0/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.5 h1:dT58k9hQ/vbxNMwoI5+xFYAJuv6152UNvdHokfI5wE4= +github.com/hashicorp/go-getter v1.7.5/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= @@ -1218,10 +1224,9 @@ github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc= -github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= +github.com/icrowley/fake v0.0.0-20221112152111-d7b7e2276db2 h1:qU3v73XG4QAqCPHA4HOpfC1EfUvtLIDvQK4mNQ0LvgI= +github.com/icrowley/fake v0.0.0-20221112152111-d7b7e2276db2/go.mod h1:dQ6TM/OGAe+cMws81eTe4Btv1dKxfPZ2CX+YaAFAPN4= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -1239,7 +1244,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw= github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= @@ -1267,8 +1271,8 @@ github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVE github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/k3d-io/k3d/v5 v5.6.0 h1:XMRSQXyPErOcDCdOJVi6HUPjJZuWd/N6Dss7QeCDRhk= -github.com/k3d-io/k3d/v5 v5.6.0/go.mod h1:t/hRD2heCSkO9TJJdzFT72jXGCY8PjsCsClgjcmMoAA= +github.com/k3d-io/k3d/v5 v5.7.5 h1:rLzUV1XmCLfFkHZEU1YocnGGo2ccHAvCj79tC0yncdI= +github.com/k3d-io/k3d/v5 v5.7.5/go.mod h1:bFlhRV/R1cPT42ZZzQAHPHUF33CbCT8VSbjtjTr3J1Y= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= @@ -1286,6 +1290,7 @@ github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -1333,6 +1338,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2 github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -1340,7 +1347,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= -github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -1388,11 +1394,10 @@ github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmL github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby v24.0.7+incompatible h1:RrVT5IXBn85mRtFKP+gFwVLCcnNPZIgN3NVRJG9Le+4= -github.com/moby/moby v24.0.7+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -1490,8 +1495,6 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs= -github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= @@ -1521,6 +1524,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1576,8 +1581,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -1598,7 +1603,6 @@ github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624 github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= @@ -1618,15 +1622,19 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0= -github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -1660,8 +1668,9 @@ github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/sylabs/sif/v2 v2.18.0 h1:eXugsS1qx7St2Wu/AJ21KnsQiVCpouPlTigABh+6KYI= @@ -1764,27 +1773,35 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0 h1:7F29RDmnlqk6B5d+sUqemt8TBfDqxryYW5gX6L74RFA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.33.0/go.mod h1:ZiGDq7xwDMKmWDrN1XsXAj0iC7hns+2DhxBFSncNHSE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU= +go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= -go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -1793,26 +1810,25 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28 h1:zLxFnORHDFTSkJPawMU7LzsuGQJ4MUFS653jJHpORow= -go4.org/netipx v0.0.0-20230728184502-ec4c8b891b28/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= -golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= @@ -1820,8 +1836,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1924,6 +1940,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -1942,7 +1959,6 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -1961,8 +1977,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1995,8 +2011,8 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2016,8 +2032,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2066,12 +2082,14 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2106,7 +2124,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2122,13 +2139,12 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= @@ -2142,8 +2158,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2164,8 +2180,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2228,6 +2244,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -2374,7 +2391,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2479,15 +2498,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go. google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2530,8 +2549,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2551,8 +2570,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= @@ -2592,7 +2611,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= diff --git a/pkg/cmd/trace/chart/summary/summary_chart.go b/pkg/cmd/trace/chart/summary/summary_chart.go index 48b984eeb..eec44372a 100644 --- a/pkg/cmd/trace/chart/summary/summary_chart.go +++ b/pkg/cmd/trace/chart/summary/summary_chart.go @@ -68,7 +68,7 @@ func (m *Model) View() string { func (m *Model) setBarData(b *barchart.Model, msg tea.MouseMsg) { x, y := m.summary.ZoneManager().Get(b.ZoneID()).Pos(msg) - selectedBarData = b.BarDataFromPoint(canvas.Point{x, y}) + selectedBarData = b.BarDataFromPoint(canvas.Point{X: x, Y: y}) } func legend(bd barchart.BarData) (r string) { From 267dc72ec74332017ac3a032890655973f85da6d Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:27:14 +0800 Subject: [PATCH 27/34] fix: add enum check of flags of create cmd (#534) (cherry picked from commit 77d59e5376ea6c62b0262c9d020acb8b46c57fc7) --- pkg/cmd/cluster/cluster_test.go | 139 +++++++++++++++++-------- pkg/cmd/cluster/create_subcmds.go | 29 ++++++ pkg/cmd/cluster/create_subcmds_test.go | 4 + 3 files changed, 128 insertions(+), 44 deletions(-) diff --git a/pkg/cmd/cluster/cluster_test.go b/pkg/cmd/cluster/cluster_test.go index 5188d51d5..8955feee3 100644 --- a/pkg/cmd/cluster/cluster_test.go +++ b/pkg/cmd/cluster/cluster_test.go @@ -93,28 +93,8 @@ var _ = Describe("Cluster", func() { Expect(o.ChartInfo).ShouldNot(BeNil()) o.Format = printer.YAML - Expect(o.CreateOptions.Complete()).To(Succeed()) - o.Client = testing.FakeClientSet() - fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) - fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} - Expect(o.Complete(nil)).To(Succeed()) - Expect(o.Validate()).To(Succeed()) - Expect(o.Name).ShouldNot(BeEmpty()) - Expect(o.Run()).Should(Succeed()) - }) - It("should apply SharedNode tenancy and Preferred PodAntiAffinity", func() { - o, err := NewSubCmdsOptions(createOptions, clusterType) - Expect(err).Should(Succeed()) - o.Tenancy = "SharedNode" - o.TopologyKeys = []string{"test-topology1", "test-topology2"} - o.NodeLabels = map[string]string{"environment": "test-env", "region": "test-region"} - o.TolerationsRaw = []string{"testKey1=testValue1:NoSchedule", "testKey2=testValue2:NoExecute"} o.PodAntiAffinity = "Preferred" - - Expect(o).ShouldNot(BeNil()) - Expect(o.ChartInfo).ShouldNot(BeNil()) - o.Format = printer.YAML - + o.Tenancy = "SharedNode" Expect(o.CreateOptions.Complete()).To(Succeed()) o.Client = testing.FakeClientSet() fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) @@ -124,29 +104,6 @@ var _ = Describe("Cluster", func() { Expect(o.Name).ShouldNot(BeEmpty()) Expect(o.Run()).Should(Succeed()) }) - - It("should apply DedicatedNode tenancy and Required PodAntiAffinity", func() { - o, err := NewSubCmdsOptions(createOptions, clusterType) - Expect(err).Should(Succeed()) - o.Tenancy = "DedicatedNode" - o.TopologyKeys = []string{"test-region", "test-zone"} - o.NodeLabels = map[string]string{"cluster": "test-cluster", "env": "test-production"} - o.TolerationsRaw = []string{"testKey3=testValue3:NoSchedule", "testKey4=testValue4:NoExecute"} - o.PodAntiAffinity = "Required" - - Expect(o).ShouldNot(BeNil()) - Expect(o.ChartInfo).ShouldNot(BeNil()) - o.Format = printer.YAML - - Expect(o.CreateOptions.Complete()).To(Succeed()) - o.Client = testing.FakeClientSet() - fakeDiscovery2, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) - fakeDiscovery2.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} - Expect(o.Complete(nil)).To(Succeed()) - Expect(o.Validate()).To(Succeed()) - Expect(o.Name).ShouldNot(BeEmpty()) - Expect(o.Run()).Should(Succeed()) - }) }) Context("create validate", func() { @@ -162,6 +119,8 @@ var _ = Describe("Cluster", func() { } o.Name = "mycluster" o.ChartInfo, _ = cluster.BuildChartInfo(clusterType) + o.PodAntiAffinity = "Preferred" + o.Tenancy = "SharedNode" }) It("can validate the cluster name must begin with a letter and can only contain lowercase letters, numbers, and '-'.", func() { @@ -212,6 +171,98 @@ var _ = Describe("Cluster", func() { Expect(o.Validate()).Should(HaveOccurred()) }) + // Test case: invalid tenancy value + It("should fail when tenancy is invalid", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.Tenancy = "InvalidTenancy" // Set invalid tenancy value + + Expect(o.Validate()).ShouldNot(Succeed()) // Validation should fail + }) + + // Test case: invalid podAntiAffinity value + It("should fail when podAntiAffinity is invalid", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.PodAntiAffinity = "None" // Set invalid podAntiAffinity value + + Expect(o.Validate()).ShouldNot(Succeed()) // Validation should fail + }) + + // Test case: topologyKeys contains empty values + It("should fail when topologyKeys contains empty values", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.TopologyKeys = []string{"", "valid-topology"} // One of the keys is empty + + Expect(o.Validate()).ShouldNot(Succeed()) // Validation should fail + }) + + // Test case: nodeLabels contains empty key or value + It("should fail when nodeLabels contains empty key or value", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.NodeLabels = map[string]string{"environment": "", "": "test-region"} // Key or value is empty + + Expect(o.Validate()).ShouldNot(Succeed()) // Validation should fail + }) + + // Test case: tolerations contains empty key or operator + It("should fail when tolerations contains empty key or operator", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.TolerationsRaw = []string{"testKey=:NoSchedule", "=testValue:NoExecute"} // Key or operator is empty + + Expect(o.Validate()).ShouldNot(Succeed()) // Validation should fail + }) + + // Test case: all inputs are valid + It("should apply SharedNode tenancy and Preferred PodAntiAffinity", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.Tenancy = "SharedNode" + o.TopologyKeys = []string{"test-topology1", "test-topology2"} + o.NodeLabels = map[string]string{"environment": "test-env", "region": "test-region"} + o.TolerationsRaw = []string{"testKey1=testValue1:NoSchedule", "testKey2=testValue2:NoExecute"} + o.PodAntiAffinity = "Preferred" + + Expect(o).ShouldNot(BeNil()) + Expect(o.ChartInfo).ShouldNot(BeNil()) + o.Format = printer.YAML + + Expect(o.CreateOptions.Complete()).To(Succeed()) + o.Client = testing.FakeClientSet() + fakeDiscovery1, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) + fakeDiscovery1.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} + Expect(o.Complete(nil)).To(Succeed()) + Expect(o.Validate()).To(Succeed()) + Expect(o.Name).ShouldNot(BeEmpty()) + Expect(o.Run()).Should(Succeed()) + }) + + It("should apply DedicatedNode tenancy and Required PodAntiAffinity", func() { + o, err := NewSubCmdsOptions(createOptions, clusterType) + Expect(err).Should(Succeed()) + o.Tenancy = "DedicatedNode" + o.TopologyKeys = []string{"test-region", "test-zone"} + o.NodeLabels = map[string]string{"cluster": "test-cluster", "env": "test-production"} + o.TolerationsRaw = []string{"testKey3=testValue3:NoSchedule", "testKey4=testValue4:NoExecute"} + o.PodAntiAffinity = "Required" + + Expect(o).ShouldNot(BeNil()) + Expect(o.ChartInfo).ShouldNot(BeNil()) + o.Format = printer.YAML + + Expect(o.CreateOptions.Complete()).To(Succeed()) + o.Client = testing.FakeClientSet() + fakeDiscovery2, _ := o.Client.Discovery().(*fakediscovery.FakeDiscovery) + fakeDiscovery2.FakedServerVersion = &version.Info{Major: "1", Minor: "27", GitVersion: "v1.27.0"} + Expect(o.Complete(nil)).To(Succeed()) + Expect(o.Validate()).To(Succeed()) + Expect(o.Name).ShouldNot(BeEmpty()) + Expect(o.Run()).Should(Succeed()) + }) + }) Context("delete cluster", func() { diff --git a/pkg/cmd/cluster/create_subcmds.go b/pkg/cmd/cluster/create_subcmds.go index db6baf5ba..eeb5587e4 100644 --- a/pkg/cmd/cluster/create_subcmds.go +++ b/pkg/cmd/cluster/create_subcmds.go @@ -129,6 +129,7 @@ func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { // Schedule policy if cmd.Flag("pod-anti-affinity") == nil { cmd.Flags().StringVar(&o.PodAntiAffinity, "pod-anti-affinity", "Preferred", "Pod anti-affinity type, one of: (Preferred, Required)") + _ = cmd.Flags().SetAnnotation("pod-anti-affinity", cobra.BashCompCustom, []string{"Preferred", "Required"}) } if cmd.Flag("topology-keys") == nil { cmd.Flags().StringArrayVar(&o.TopologyKeys, "topology-keys", nil, "Topology keys for affinity") @@ -141,6 +142,7 @@ func buildCreateSubCmds(createOptions *action.CreateOptions) []*cobra.Command { } if cmd.Flag("tenancy") == nil { cmd.Flags().StringVar(&o.Tenancy, "tenancy", "SharedNode", "Tenancy options, one of: (SharedNode, DedicatedNode)") + _ = cmd.Flags().SetAnnotation("tenancy", cobra.BashCompCustom, []string{"SharedNode", "DedicatedNode"}) } cmds = append(cmds, cmd) } @@ -234,6 +236,33 @@ func (o *CreateSubCmdsOptions) Validate() error { if len(o.Name) > 16 { return fmt.Errorf("cluster name should be less than 16 characters") } + if o.Tenancy != "SharedNode" && o.Tenancy != "DedicatedNode" { + return fmt.Errorf("tenancy must be one of: (SharedNode, DedicatedNode)") + } + if o.PodAntiAffinity != "Preferred" && o.PodAntiAffinity != "Required" { + return fmt.Errorf("podAntiAffinity must be one of: (Preferred, Required)") + } + if o.TopologyKeys != nil { + for _, key := range o.TopologyKeys { + if key == "" { + return fmt.Errorf("topologyKeys cannot be empty") + } + } + } + if o.NodeLabels != nil { + for k, v := range o.NodeLabels { + if k == "" || v == "" { + return fmt.Errorf("nodeLabels cannot be empty") + } + } + } + if o.Tolerations != nil { + for _, t := range o.Tolerations { + if t.Key == "" || t.Operator == "" { + return fmt.Errorf("tolerations cannot be empty") + } + } + } return cluster.ValidateValues(o.ChartInfo, o.Values) } diff --git a/pkg/cmd/cluster/create_subcmds_test.go b/pkg/cmd/cluster/create_subcmds_test.go index 5eef97a87..8837abbe6 100644 --- a/pkg/cmd/cluster/create_subcmds_test.go +++ b/pkg/cmd/cluster/create_subcmds_test.go @@ -96,6 +96,8 @@ var _ = Describe("create cluster by cluster type", func() { Expect(err).Should(Succeed()) Expect(o).ShouldNot(BeNil()) Expect(o.ChartInfo).ShouldNot(BeNil()) + o.PodAntiAffinity = "Preferred" + o.Tenancy = "SharedNode" By("complete") var mysqlCmd *cobra.Command @@ -139,6 +141,8 @@ var _ = Describe("create cluster by cluster type", func() { Expect(err).Should(Succeed()) Expect(o).ShouldNot(BeNil()) Expect(o.ChartInfo).ShouldNot(BeNil()) + o.PodAntiAffinity = "Preferred" + o.Tenancy = "SharedNode" By("complete") var shardCmd *cobra.Command From 8361d79550fbc87d4c485bc35eb0617dda12d5e1 Mon Sep 17 00:00:00 2001 From: yipeng1030 <58055769+yipeng1030@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:27:38 +0800 Subject: [PATCH 28/34] fix: fix explain-config cmd (#532) (cherry picked from commit 7897da4be4ca6d6be9831fc5734707f156fa46cf) --- pkg/cmd/cluster/config_observer.go | 5 +++++ pkg/cmd/cluster/errors.go | 1 + 2 files changed, 6 insertions(+) diff --git a/pkg/cmd/cluster/config_observer.go b/pkg/cmd/cluster/config_observer.go index 4a3c71502..145214152 100644 --- a/pkg/cmd/cluster/config_observer.go +++ b/pkg/cmd/cluster/config_observer.go @@ -168,6 +168,11 @@ func (r *configObserverOptions) printExplainConfigure(configSpecs configSpecsTyp return nil } + if tpl.ConfigConstraint == nil { + fmt.Printf("\n%s\n", fmt.Sprintf(noConfigConstraintPrompt, printer.BoldYellow(tplName))) + return nil + } + confSpec := tpl.ConfigConstraint.Spec if confSpec.ParametersSchema == nil { fmt.Printf("\n%s\n", fmt.Sprintf(notConfigSchemaPrompt, printer.BoldYellow(tplName))) diff --git a/pkg/cmd/cluster/errors.go b/pkg/cmd/cluster/errors.go index 4459d652b..2688b4949 100644 --- a/pkg/cmd/cluster/errors.go +++ b/pkg/cmd/cluster/errors.go @@ -41,6 +41,7 @@ var ( notFoundConfigFileErrorMessage = "cannot find config file[name=%s] in the configspec[name=%s], all configfiles: %v" notSupportFileUpdateErrorMessage = "not supported file[%s] for updating, current supported files: %v" + noConfigConstraintPrompt = "cannot find configConstraint for template[%s]" notConfigSchemaPrompt = "The config template[%s] is not defined in schema and parameter explanation info cannot be generated." cue2openAPISchemaFailedPrompt = "The cue schema may not satisfy the conversion constraints of openAPISchema and parameter explanation info cannot be generated." restartConfirmPrompt = "The parameter change incurs a cluster restart, which brings the cluster down for a while. Enter to continue...\n, " From 4a3dbeb8a36592ecc58d6efa17d82818ad6b0540 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Wed, 15 Jan 2025 14:17:17 +0800 Subject: [PATCH 29/34] chore: tidy up promote and rebuild instance cmd with new api (#539) Co-authored-by: wangyelei (cherry picked from commit 00acda05f9544469b7f6b1c7a4469643f118a4d0) --- docs/user_docs/cli/kbcli_cluster_promote.md | 13 +-- .../cli/kbcli_cluster_rebuild-instance.md | 1 + go.mod | 2 +- go.sum | 4 +- .../template/cluster_operations_template.cue | 12 ++- pkg/cmd/cluster/operations.go | 79 +++++++++---------- pkg/cmd/cluster/operations_test.go | 8 +- 7 files changed, 54 insertions(+), 65 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_promote.md b/docs/user_docs/cli/kbcli_cluster_promote.md index d32b072be..a76bb1548 100644 --- a/docs/user_docs/cli/kbcli_cluster_promote.md +++ b/docs/user_docs/cli/kbcli_cluster_promote.md @@ -5,32 +5,25 @@ title: kbcli cluster promote Promote a non-primary or non-leader instance as the new primary or leader of the cluster ``` -kbcli cluster promote NAME [--component=] [--instance ] [flags] +kbcli cluster promote NAME [--instance ] [flags] ``` ### Examples ``` # Promote the instance mycluster-mysql-1 as the new primary or leader. - kbcli cluster promote mycluster --instance mycluster-mysql-1 - - # Promote a non-primary or non-leader instance as the new primary or leader, the new primary or leader is determined by the system. - kbcli cluster promote mycluster - - # If the cluster has multiple components, you need to specify a component, otherwise an error will be reported. - kbcli cluster promote mycluster --component=mysql --instance mycluster-mysql-1 + kbcli cluster promote mycluster --candidate mycluster-mysql-1 ``` ### Options ``` --auto-approve Skip interactive approval before promote the instance - --component string Specify the component name of the cluster, if the cluster has multiple components, you need to specify a component + --candidate string Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running "kbcli cluster list-instances" --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") --edit Edit the API resource before creating --force skip the pre-checks of the opsRequest to run the opsRequest forcibly -h, --help help for promote - --instance string Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running "kbcli cluster list-instances" --name string OpsRequest name. if not specified, it will be randomly generated -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed diff --git a/docs/user_docs/cli/kbcli_cluster_rebuild-instance.md b/docs/user_docs/cli/kbcli_cluster_rebuild-instance.md index 2d8598784..fd1a83b6a 100644 --- a/docs/user_docs/cli/kbcli_cluster_rebuild-instance.md +++ b/docs/user_docs/cli/kbcli_cluster_rebuild-instance.md @@ -39,6 +39,7 @@ kbcli cluster rebuild-instance NAME [flags] --node strings specified the target node which rebuilds the instance on the node otherwise will rebuild on a random node. format: insName1=nodeName,insName2=nodeName -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --restore-env stringArray provide the necessary env for the 'Restore' operation from the backup. format: key1=value, key2=value + --source-backup-target string To rebuild a sharding component instance from a backup, you can specify the name of the source backup target --ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed ``` diff --git a/go.mod b/go.mod index e546499d7..5bcbaceb2 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 github.com/NimbleMarkets/ntcharts v0.1.2 github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 - github.com/apecloud/kubeblocks v1.0.0-beta.17 + github.com/apecloud/kubeblocks v1.0.0-beta.23 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/briandowns/spinner v1.23.0 github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad diff --git a/go.sum b/go.sum index 2c63dd2a6..59ecb474a 100644 --- a/go.sum +++ b/go.sum @@ -677,8 +677,8 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys= -github.com/apecloud/kubeblocks v1.0.0-beta.17 h1:taNHtwUWyCUBSHbPAx5sY5ltY0Dcf62cr+1HjxlK60w= -github.com/apecloud/kubeblocks v1.0.0-beta.17/go.mod h1:bQ6uey/6S9gAuDkAJ7T89CdpmeXyxEFJpLw1hV2hANE= +github.com/apecloud/kubeblocks v1.0.0-beta.23 h1:JrQBB9gJ/jtMD9wHv5js26rdfNQgeoU5+GcUEiaAmFU= +github.com/apecloud/kubeblocks v1.0.0-beta.23/go.mod h1:b656nTyvHhwRwOuwNpOPG87Q0Lba3ygGRWoSOacPt5o= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= diff --git a/pkg/action/template/cluster_operations_template.cue b/pkg/action/template/cluster_operations_template.cue index e35c26acb..129187a41 100644 --- a/pkg/action/template/cluster_operations_template.cue +++ b/pkg/action/template/cluster_operations_template.cue @@ -30,6 +30,7 @@ options: { componentDefinitionName: string serviceVersion: string component: string + componentObjectName: string instance: string componentNames: [...string] instanceTPLNames: [...string] @@ -38,6 +39,7 @@ options: { componentName: string backupName?: string inPlace?: bool + sourceBackupTargetName?: string instances: [ ...{ name: string @@ -283,13 +285,9 @@ content: { } if options.type == "Switchover" { switchover: [{ - componentName: options.component - if options.instance == "" { - instanceName: "*" - } - if options.instance != "" { - instanceName: options.instance - } + componentObjectName: options.componentObjectName + instanceName: options.instance + candidateName: options.instance }] } if options.type == "RebuildInstance" { diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index fce50953c..c5b9b305d 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -108,14 +108,16 @@ type OperationsOptions struct { Services []opsv1alpha1.OpsService `json:"services,omitempty"` // Switchover options - Component string `json:"component"` - Instance string `json:"instance"` - BackupName string `json:"-"` - Inplace bool `json:"-"` - InstanceNames []string `json:"-"` - Nodes []string `json:"-"` - RebuildInstanceFrom []opsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"` - Env []string `json:"-"` + Component string `json:"component"` + ComponentObjectName string `json:"componentObjectName,omitempty"` + Instance string `json:"instance"` + BackupName string `json:"-"` + Inplace bool `json:"-"` + InstanceNames []string `json:"-"` + Nodes []string `json:"-"` + RebuildInstanceFrom []opsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"` + Env []string `json:"-"` + SourceBackupTargetName string `json:"-"` // Stop and Start options isComponentsFlagOptional bool @@ -212,10 +214,11 @@ func (o *OperationsOptions) CompleteSwitchoverOps() error { } if o.Component == "" { - if len(clusterObj.Spec.ComponentSpecs) > 1 { + if len(clusterObj.Spec.ComponentSpecs) == 1 { + o.Component = clusterObj.Spec.ComponentSpecs[0].Name + } else if len(clusterObj.Spec.ComponentSpecs) > 1 { return fmt.Errorf("there are multiple components in cluster, please use --component to specify the component for promote") } - o.Component = clusterObj.Spec.ComponentSpecs[0].Name } return nil } @@ -435,23 +438,19 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error { podObj = &corev1.Pod{} ) - if o.Component == "" && o.Instance == "" { - return fmt.Errorf("at least one of --component or --instance is required") + if o.Instance == "" { + return fmt.Errorf("--candidate is required") } // if the instance is not specified, do not need to check the validity of the instance - if o.Instance != "" { - // checks the validity of the instance whether it belongs to the current component and ensure it is not the primary or leader instance currently. - podKey := client.ObjectKey{ - Namespace: clusterObj.Namespace, - Name: o.Instance, - } - if err := util.GetResourceObjectFromGVR(types.PodGVR(), podKey, o.Dynamic, podObj); err != nil || podObj == nil { - return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) - } - if o.Component == "" { - o.Component = cluster.GetPodComponentName(podObj) - } + // checks the validity of the instance whether it belongs to the current component and ensure it is not the primary or leader instance currently. + podKey := client.ObjectKey{ + Namespace: clusterObj.Namespace, + Name: o.Instance, } + if err := util.GetResourceObjectFromGVR(types.PodGVR(), podKey, o.Dynamic, podObj); err != nil || podObj == nil { + return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) + } + o.ComponentObjectName = constant.GenerateClusterComponentName(clusterObj.Name, podObj.Labels[constant.KBAppComponentLabelKey]) getAndValidatePod := func(targetRoles ...string) error { // if the instance is not specified, do not need to check the validity of the instance @@ -467,8 +466,8 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error { return fmt.Errorf("instanceName %s cannot be promoted because it is already the targetRole %s instance", o.Instance, targetRole) } } - if cluster.GetPodComponentName(podObj) != o.Component || podObj.Labels[constant.AppInstanceLabelKey] != clusterObj.Name { - return fmt.Errorf("instanceName %s does not belong to the current component, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) + if podObj.Labels[constant.AppInstanceLabelKey] != clusterObj.Name { + return fmt.Errorf("instanceName %s does not belong to the current cluster, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) } return nil } @@ -492,13 +491,13 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error { // check componentDefinition exist compDefKey := client.ObjectKey{ Namespace: "", - Name: cluster.GetComponentSpec(clusterObj, o.Component).ComponentDef, + Name: cluster.GetComponentSpec(clusterObj, cluster.GetPodComponentName(podObj)).ComponentDef, } if err := util.GetResourceObjectFromGVR(types.CompDefGVR(), compDefKey, o.Dynamic, &compDefObj); err != nil { return err } if compDefObj.Spec.LifecycleActions == nil || compDefObj.Spec.LifecycleActions.Switchover == nil { - return fmt.Errorf(`this component "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Component, compDefKey.Name) + return fmt.Errorf(`this instance "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Instance, compDefKey.Name) } targetRole, err := getTargetRole(compDefObj.Spec.Roles) if err != nil { @@ -988,20 +987,14 @@ func NewCancelCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. var promoteExample = templates.Examples(` # Promote the instance mycluster-mysql-1 as the new primary or leader. - kbcli cluster promote mycluster --instance mycluster-mysql-1 - - # Promote a non-primary or non-leader instance as the new primary or leader, the new primary or leader is determined by the system. - kbcli cluster promote mycluster - - # If the cluster has multiple components, you need to specify a component, otherwise an error will be reported. - kbcli cluster promote mycluster --component=mysql --instance mycluster-mysql-1 + kbcli cluster promote mycluster --candidate mycluster-mysql-1 `) // NewPromoteCmd creates a promote command func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { o := newBaseOperationsOptions(f, streams, opsv1alpha1.SwitchoverType, false) cmd := &cobra.Command{ - Use: "promote NAME [--component=] [--instance ]", + Use: "promote NAME [--instance ]", Short: "Promote a non-primary or non-leader instance as the new primary or leader of the cluster", Example: promoteExample, ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()), @@ -1015,9 +1008,9 @@ func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra cmdutil.CheckErr(o.Run()) }, } - flags.AddComponentFlag(f, cmd, &o.Component, "Specify the component name of the cluster, if the cluster has multiple components, you need to specify a component") - cmd.Flags().StringVar(&o.Instance, "instance", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"") + cmd.Flags().StringVar(&o.Instance, "candidate", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"") cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before promote the instance") + _ = cmd.MarkFlagRequired("candidate") o.addCommonFlags(cmd, f) return cmd } @@ -1327,10 +1320,11 @@ func NewRebuildInstanceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams ComponentOps: opsv1alpha1.ComponentOps{ ComponentName: compName, }, - Instances: instances, - InPlace: o.Inplace, - BackupName: o.BackupName, - RestoreEnv: envVars, + Instances: instances, + InPlace: o.Inplace, + BackupName: o.BackupName, + RestoreEnv: envVars, + SourceBackupTargetName: o.SourceBackupTargetName, }, } return nil @@ -1355,6 +1349,7 @@ func NewRebuildInstanceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams cmd.Flags().StringVar(&o.BackupName, "backup", "", "instances will be rebuild by the specified backup.") cmd.Flags().StringSliceVar(&o.InstanceNames, "instances", nil, "instances which need to rebuild.") util.CheckErr(flags.CompletedInstanceFlag(cmd, f, "instances")) + cmd.Flags().StringVar(&o.SourceBackupTargetName, "source-backup-target", "", "To rebuild a sharding component instance from a backup, you can specify the name of the source backup target") cmd.Flags().StringSliceVar(&o.Nodes, "node", nil, `specified the target node which rebuilds the instance on the node otherwise will rebuild on a random node. format: insName1=nodeName,insName2=nodeName`) cmd.Flags().StringArrayVar(&o.Env, "restore-env", []string{}, "provide the necessary env for the 'Restore' operation from the backup. format: key1=value, key2=value") return cmd diff --git a/pkg/cmd/cluster/operations_test.go b/pkg/cmd/cluster/operations_test.go index 9f420c993..bb4f351c5 100644 --- a/pkg/cmd/cluster/operations_test.go +++ b/pkg/cmd/cluster/operations_test.go @@ -370,8 +370,9 @@ var _ = Describe("operations", func() { By("validate failed because o.Instance does not belong to the current component") o.Instance = fmt.Sprintf("%s-%s-%d", clusterName, testing.ComponentName, 1) + o.Name = clusterName1 Expect(o.Validate()).ShouldNot(Succeed()) - Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current component")).Should(BeTrue()) + Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current cluster")).Should(BeTrue()) }) It("Switchover ops base on component definition", func() { @@ -402,11 +403,12 @@ var _ = Describe("operations", func() { Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "cannot be promoted because it is already the targetRole")).Should(BeTrue()) - By("validate failed because o.Instance does not belong to the current component") + By("validate failed because o.Instance does not belong to the current cluster") o.Instance = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 1) o.Component = testing.ComponentName + o.Name = clusterName Expect(o.Validate()).ShouldNot(Succeed()) - Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current component")).Should(BeTrue()) + Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current cluster")).Should(BeTrue()) }) From b2a8e0bb3c9b7fe3575037c72862c88f4477ba43 Mon Sep 17 00:00:00 2001 From: cjc7373 Date: Thu, 23 Jan 2025 17:50:57 +0800 Subject: [PATCH 30/34] chore: remove rbac args (#506) (cherry picked from commit b64be7d84d241fb66ce5ffb2fecae79ad56fc75b) --- .../cli/kbcli_cluster_create_elasticsearch.md | 2 +- docs/user_docs/cli/kbcli_cluster_delete.md | 1 - pkg/cmd/cluster/create_util.go | 8 +- pkg/cmd/cluster/delete.go | 81 +------------------ 4 files changed, 4 insertions(+), 88 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md b/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md index cdb523001..791bdbe8b 100644 --- a/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md +++ b/docs/user_docs/cli/kbcli_cluster_create_elasticsearch.md @@ -31,7 +31,7 @@ kbcli cluster create elasticsearch NAME [flags] --node-labels stringToString Node label selector (default []) -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --pod-anti-affinity string Pod anti-affinity type, one of: (Preferred, Required) (default "Preferred") - --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. (default true) + --rbac-enabled Specify whether rbac resources will be created by client, otherwise KubeBlocks server will try to create rbac resources. --replicas int The number of replicas, for single-node mode, the replicas is 1, for multi-node mode, the default replicas is 3. Value range [1, 5]. (default 1) --storage float Storage size, the unit is Gi. Value range [1, 10000]. (default 20) --tenancy string Tenancy options, one of: (SharedNode, DedicatedNode) (default "SharedNode") diff --git a/docs/user_docs/cli/kbcli_cluster_delete.md b/docs/user_docs/cli/kbcli_cluster_delete.md index 2f68418b8..09dcdcf17 100644 --- a/docs/user_docs/cli/kbcli_cluster_delete.md +++ b/docs/user_docs/cli/kbcli_cluster_delete.md @@ -26,7 +26,6 @@ kbcli cluster delete NAME [flags] --grace-period int Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion). (default -1) -h, --help help for delete --now If true, resources are signaled for immediate shutdown (same as --grace-period=1). - --rbac-enabled Specify whether rbac resources will be deleted by kbcli -l, --selector string Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Matching objects must satisfy all of the specified label constraints. ``` diff --git a/pkg/cmd/cluster/create_util.go b/pkg/cmd/cluster/create_util.go index 4bbbad641..1e83410a1 100644 --- a/pkg/cmd/cluster/create_util.go +++ b/pkg/cmd/cluster/create_util.go @@ -38,11 +38,7 @@ import ( ) var ( - resetEngineFlagValues = map[string]map[string]string{ - "elasticsearch": { - "rbac-enabled": "true", - }, - } + resetEngineFlagValues = map[string]map[string]string{} ) // addCreateFlags adds the flags for creating a cluster, these flags are built by the cluster schema. @@ -61,7 +57,7 @@ func addCreateFlags(cmd *cobra.Command, f cmdutil.Factory, c *cluster.ChartInfo, return err } - // reset engine related flags default value, such as rbac-enabled for elasticsearch should be true by default + // reset engine related flags default value resetEngineDefaultFlagsValue(cmd.Flags(), engine) return nil } diff --git a/pkg/cmd/cluster/delete.go b/pkg/cmd/cluster/delete.go index f4194837c..e0688801c 100644 --- a/pkg/cmd/cluster/delete.go +++ b/pkg/cmd/cluster/delete.go @@ -20,18 +20,12 @@ along with this program. If not, see . package cluster import ( - "context" "fmt" "github.com/spf13/cobra" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/errors" "k8s.io/cli-runtime/pkg/genericiooptions" - "k8s.io/client-go/kubernetes" - "k8s.io/klog/v2" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/util/templates" @@ -49,8 +43,6 @@ var ( # delete a cluster by label selector kbcli cluster delete --selector clusterdefinition.kubeblocks.io/name=apecloud-mysql `) - - rbacEnabled = false ) func NewDeleteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { @@ -68,7 +60,6 @@ func NewDeleteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra. }, } o.AddFlags(cmd) - cmd.Flags().BoolVar(&rbacEnabled, "rbac-enabled", false, "Specify whether rbac resources will be deleted by kbcli") return cmd } @@ -100,81 +91,11 @@ func clusterPostDeleteHook(o *action.DeleteOptions, object runtime.Object) error return nil } - c, err := getClusterFromObject(object) - if err != nil { - return err - } - - client, err := o.Factory.KubernetesClientSet() - if err != nil { - return err - } + // currently no hook is defined - if err = deleteDependencies(client, c.Namespace, c.Name); err != nil { - return err - } return nil } -func deleteDependencies(client kubernetes.Interface, ns string, name string) error { - if !rbacEnabled { - return nil - } - - klog.V(1).Infof("delete dependencies for cluster %s", name) - var ( - saName = saNamePrefix + name - roleName = roleNamePrefix + name - roleBindingName = roleBindingNamePrefix + name - clusterRoleName = clusterRolePrefix + name - clusterRoleBindingName = clusterRoleBindingPrefix + name - allErr []error - ) - - // now, delete the dependencies, for postgresql, we delete sa, role and rolebinding - ctx := context.TODO() - gracePeriod := int64(0) - deleteOptions := metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod} - checkErr := func(err error) bool { - if err != nil && !apierrors.IsNotFound(err) { - return true - } - return false - } - - // delete cluster role binding - klog.V(1).Infof("delete cluster role binding %s", clusterRoleBindingName) - if err := client.RbacV1().ClusterRoleBindings().Delete(ctx, clusterRoleBindingName, deleteOptions); checkErr(err) { - allErr = append(allErr, err) - } - - // delete cluster role - klog.V(1).Infof("delete cluster role %s", clusterRoleName) - if err := client.RbacV1().ClusterRoles().Delete(ctx, clusterRoleName, deleteOptions); checkErr(err) { - allErr = append(allErr, err) - } - - // delete role binding - klog.V(1).Infof("delete role binding %s", roleBindingName) - if err := client.RbacV1().RoleBindings(ns).Delete(ctx, roleBindingName, deleteOptions); checkErr(err) { - allErr = append(allErr, err) - } - - // delete role - klog.V(1).Infof("delete role %s", roleName) - if err := client.RbacV1().Roles(ns).Delete(ctx, roleName, deleteOptions); checkErr(err) { - allErr = append(allErr, err) - } - - // delete service account - klog.V(1).Infof("delete service account %s", saName) - if err := client.CoreV1().ServiceAccounts(ns).Delete(ctx, saName, deleteOptions); checkErr(err) { - allErr = append(allErr, err) - } - - return errors.NewAggregate(allErr) -} - func getClusterFromObject(object runtime.Object) (*appsv1alpha1.Cluster, error) { if object.GetObjectKind().GroupVersionKind().Kind != appsv1alpha1.ClusterKind { return nil, fmt.Errorf("object %s is not of kind %s", object.GetObjectKind().GroupVersionKind().Kind, appsv1alpha1.ClusterKind) From 4d5471f20ab162739f0df47dcf35df3cd8a973f4 Mon Sep 17 00:00:00 2001 From: zhangtao <111836083+sophon-zt@users.noreply.github.com> Date: Fri, 24 Jan 2025 09:07:39 +0800 Subject: [PATCH 31/34] chore: deprecated reconfigure api field for kb-1.0 (#541) (cherry picked from commit 371782203018a021623652813a1ca110ec4edf98) --- .../template/cluster_operations_template.cue | 81 +++++++------------ 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/pkg/action/template/cluster_operations_template.cue b/pkg/action/template/cluster_operations_template.cue index 129187a41..abc685414 100644 --- a/pkg/action/template/cluster_operations_template.cue +++ b/pkg/action/template/cluster_operations_template.cue @@ -36,9 +36,9 @@ options: { instanceTPLNames: [...string] rebuildInstanceFrom: [ ...{ - componentName: string - backupName?: string - inPlace?: bool + componentName: string + backupName?: string + inPlace?: bool sourceBackupTargetName?: string instances: [ ...{ @@ -59,8 +59,8 @@ options: { replicas: string offlineInstancesToOnline: [...string] onlineInstancesToOffline: [...string] - scaleOut: bool - storage: string + scaleOut: bool + storage: string opsDefinitionName: string vctNames: [...string] keyValues: [string]: {string | null} @@ -155,7 +155,7 @@ content: { replicaChanges: strconv.Atoi(options.replicas) } if len(options.offlineInstancesToOnline) > 0 { - offlineInstancesToOnline: options.offlineInstancesToOnline + offlineInstancesToOnline: options.offlineInstancesToOnline } } } @@ -165,8 +165,8 @@ content: { replicaChanges: strconv.Atoi(options.replicas) } if len(options.onlineInstancesToOffline) > 0 { - onlineInstancesToOffline: options.onlineInstancesToOffline - } + onlineInstancesToOffline: options.onlineInstancesToOffline + } } } }] @@ -217,52 +217,27 @@ content: { }] } if options.type == "Reconfiguring" { - if len(options.componentNames) == 1 { - reconfigure: { - componentName: options.componentNames[0] - configurations: [ { - name: options.cfgTemplateName - if options.forceRestart { - policy: "simple" + reconfigures: [ for _, cName in options.componentNames { + componentName: cName + configurations: [ { + name: options.cfgTemplateName + if options.forceRestart { + policy: "simple" + } + keys: [{ + key: options.cfgFile + if options.fileContent != "" { + fileContent: options.fileContent } - keys: [{ - key: options.cfgFile - if options.fileContent != "" { - fileContent: options.fileContent - } - if options.hasPatch { - parameters: [ for k, v in options.keyValues { - key: k - value: v - }] - } - }] - }] - } - } - if len(options.componentNames) > 1 { - reconfigures: [ for _, cName in options.componentNames { - componentName: cName - configurations: [ { - name: options.cfgTemplateName - if options.forceRestart { - policy: "simple" + if options.hasPatch { + parameters: [ for k, v in options.keyValues { + key: k + value: v + }] } - keys: [{ - key: options.cfgFile - if options.fileContent != "" { - fileContent: options.fileContent - } - if options.hasPatch { - parameters: [ for k, v in options.keyValues { - key: k - value: v - }] - } - }] }] }] - } + }] } if options.type == "Expose" { expose: [ for _, cName in options.componentNames { @@ -286,8 +261,8 @@ content: { if options.type == "Switchover" { switchover: [{ componentObjectName: options.componentObjectName - instanceName: options.instance - candidateName: options.instance + instanceName: options.instance + candidateName: options.instance }] } if options.type == "RebuildInstance" { @@ -300,7 +275,7 @@ content: { { componentName: options.component if len(options.params) > 0 { - parameters: options.params + parameters: options.params } }, ] From f147a4098ddb4c54164b4b61f2d1fda89432b2dd Mon Sep 17 00:00:00 2001 From: wangyelei Date: Thu, 6 Feb 2025 11:05:10 +0800 Subject: [PATCH 32/34] fix: switchover failed (#546) Co-authored-by: wangyelei (cherry picked from commit 99ff4bfe5f06aee3eb8968dff932f34544b34682) --- docs/user_docs/cli/kbcli_cluster_promote.md | 1 + go.mod | 2 +- go.sum | 4 +- .../template/cluster_operations_template.cue | 2 +- pkg/cluster/cluster.go | 1 - pkg/cmd/cluster/operations.go | 56 +++++++++++++------ pkg/cmd/cluster/operations_test.go | 12 ++-- pkg/cmd/componentdefinition/describe.go | 9 ++- pkg/testing/fake.go | 21 +++---- 9 files changed, 64 insertions(+), 44 deletions(-) diff --git a/docs/user_docs/cli/kbcli_cluster_promote.md b/docs/user_docs/cli/kbcli_cluster_promote.md index a76bb1548..ad6f60d2f 100644 --- a/docs/user_docs/cli/kbcli_cluster_promote.md +++ b/docs/user_docs/cli/kbcli_cluster_promote.md @@ -24,6 +24,7 @@ kbcli cluster promote NAME [--instance ] [flags] --edit Edit the API resource before creating --force skip the pre-checks of the opsRequest to run the opsRequest forcibly -h, --help help for promote + --instance string Specify the instance name that will transfer its role to the candidate pod, If not set, the current primary or leader of the cluster will be used. --name string OpsRequest name. if not specified, it will be randomly generated -o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml) --ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed diff --git a/go.mod b/go.mod index 5bcbaceb2..97bf3e0c1 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 github.com/NimbleMarkets/ntcharts v0.1.2 github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 - github.com/apecloud/kubeblocks v1.0.0-beta.23 + github.com/apecloud/kubeblocks v1.0.0-beta.25 github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/briandowns/spinner v1.23.0 github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad diff --git a/go.sum b/go.sum index 59ecb474a..4f6eaeb14 100644 --- a/go.sum +++ b/go.sum @@ -677,8 +677,8 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo= github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys= -github.com/apecloud/kubeblocks v1.0.0-beta.23 h1:JrQBB9gJ/jtMD9wHv5js26rdfNQgeoU5+GcUEiaAmFU= -github.com/apecloud/kubeblocks v1.0.0-beta.23/go.mod h1:b656nTyvHhwRwOuwNpOPG87Q0Lba3ygGRWoSOacPt5o= +github.com/apecloud/kubeblocks v1.0.0-beta.25 h1:wWnvGSt/0emzvnbKSIVUQ5/nB9IeRrsgtsqFsu2Jfis= +github.com/apecloud/kubeblocks v1.0.0-beta.25/go.mod h1:b656nTyvHhwRwOuwNpOPG87Q0Lba3ygGRWoSOacPt5o= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= diff --git a/pkg/action/template/cluster_operations_template.cue b/pkg/action/template/cluster_operations_template.cue index abc685414..fc37fdae9 100644 --- a/pkg/action/template/cluster_operations_template.cue +++ b/pkg/action/template/cluster_operations_template.cue @@ -262,7 +262,7 @@ content: { switchover: [{ componentObjectName: options.componentObjectName instanceName: options.instance - candidateName: options.instance + candidateName: options.candidate }] } if options.type == "RebuildInstance" { diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 29db32204..81a688edc 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -423,7 +423,6 @@ func (o *ClusterObjects) GetInstanceInfo() []*InstanceInfo { Component: componentName, Status: o.getPodPhase(&pod), Role: getLabelVal(pod.Labels, constant.RoleLabelKey), - AccessMode: getLabelVal(pod.Labels, constant.AccessModeLabelKey), CreatedTime: util.TimeFormat(&pod.CreationTimestamp), } var componentSpec *kbappsv1.ClusterComponentSpec diff --git a/pkg/cmd/cluster/operations.go b/pkg/cmd/cluster/operations.go index c5b9b305d..1a2f47a6f 100755 --- a/pkg/cmd/cluster/operations.go +++ b/pkg/cmd/cluster/operations.go @@ -22,6 +22,7 @@ package cluster import ( "context" "fmt" + "math" "strings" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" @@ -111,6 +112,7 @@ type OperationsOptions struct { Component string `json:"component"` ComponentObjectName string `json:"componentObjectName,omitempty"` Instance string `json:"instance"` + Candidate string `json:"candidate"` BackupName string `json:"-"` Inplace bool `json:"-"` InstanceNames []string `json:"-"` @@ -438,54 +440,75 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error { podObj = &corev1.Pod{} ) - if o.Instance == "" { + if o.Candidate == "" { return fmt.Errorf("--candidate is required") } // if the instance is not specified, do not need to check the validity of the instance // checks the validity of the instance whether it belongs to the current component and ensure it is not the primary or leader instance currently. podKey := client.ObjectKey{ Namespace: clusterObj.Namespace, - Name: o.Instance, + Name: o.Candidate, } if err := util.GetResourceObjectFromGVR(types.PodGVR(), podKey, o.Dynamic, podObj); err != nil || podObj == nil { - return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) + return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Candidate) } o.ComponentObjectName = constant.GenerateClusterComponentName(clusterObj.Name, podObj.Labels[constant.KBAppComponentLabelKey]) getAndValidatePod := func(targetRoles ...string) error { // if the instance is not specified, do not need to check the validity of the instance - if o.Instance == "" { + if o.Candidate == "" { return nil } v, ok := podObj.Labels[constant.RoleLabelKey] if !ok || v == "" { - return fmt.Errorf("instance %s cannot be promoted because it had a invalid role label", o.Instance) + return fmt.Errorf("instance %s cannot be promoted because it had a invalid role label", o.Candidate) } for _, targetRole := range targetRoles { if v == targetRole { - return fmt.Errorf("instanceName %s cannot be promoted because it is already the targetRole %s instance", o.Instance, targetRole) + return fmt.Errorf("instanceName %s cannot be promoted because it is already the targetRole %s instance", o.Candidate, targetRole) } } if podObj.Labels[constant.AppInstanceLabelKey] != clusterObj.Name { - return fmt.Errorf("instanceName %s does not belong to the current cluster, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) + return fmt.Errorf("instanceName %s does not belong to the current cluster, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Candidate) } return nil } getTargetRole := func(roles []appsv1.ReplicaRole) (string, error) { + pods, err := o.Client.CoreV1().Pods(clusterObj.Namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", constant.KBAppComponentLabelKey, podObj.Labels[constant.KBAppComponentLabelKey]), + }) + if err != nil { + return "", err + } + if o.Instance != "" { + for _, pod := range pods.Items { + if pod.Name == o.Instance { + return pod.Labels[constant.RoleLabelKey], nil + } + } + return "", fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance) + } targetRole := "" if len(roles) == 0 { return targetRole, fmt.Errorf("component has no roles definition, does not support switchover") } - for _, role := range roles { - if role.Serviceable && role.Writable { - if targetRole != "" { - return targetRole, fmt.Errorf("componentDefinition has more than role is serviceable and writable, does not support switchover") - } - targetRole = role.Name + // HACK: assume the role with highest priority to be writable + highestPriority := math.MinInt + var role *appsv1.ReplicaRole + for _, r := range compDefObj.Spec.Roles { + if r.UpdatePriority > highestPriority { + highestPriority = r.UpdatePriority + role = &r + } + } + for _, pod := range pods.Items { + if pod.Labels[constant.RoleLabelKey] == role.Name { + o.Instance = pod.Name + break } } - return targetRole, nil + return role.Name, nil } // check componentDefinition exist @@ -497,7 +520,7 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error { return err } if compDefObj.Spec.LifecycleActions == nil || compDefObj.Spec.LifecycleActions.Switchover == nil { - return fmt.Errorf(`this instance "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Instance, compDefKey.Name) + return fmt.Errorf(`this instance "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Candidate, compDefKey.Name) } targetRole, err := getTargetRole(compDefObj.Spec.Roles) if err != nil { @@ -1008,7 +1031,8 @@ func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra cmdutil.CheckErr(o.Run()) }, } - cmd.Flags().StringVar(&o.Instance, "candidate", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"") + cmd.Flags().StringVar(&o.Candidate, "candidate", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"") + cmd.Flags().StringVar(&o.Instance, "instance", "", "Specify the instance name that will transfer its role to the candidate pod, If not set, the current primary or leader of the cluster will be used.") cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before promote the instance") _ = cmd.MarkFlagRequired("candidate") o.addCommonFlags(cmd, f) diff --git a/pkg/cmd/cluster/operations_test.go b/pkg/cmd/cluster/operations_test.go index bb4f351c5..e8de94f32 100644 --- a/pkg/cmd/cluster/operations_test.go +++ b/pkg/cmd/cluster/operations_test.go @@ -359,17 +359,17 @@ var _ = Describe("operations", func() { By("validate failed because o.Instance is illegal ") o.Name = clusterName1 o.Component = testing.ComponentName - o.Instance = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 5) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 5) Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "not found")).Should(BeTrue()) By("validate failed because o.Instance is already leader and cannot be promoted") - o.Instance = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 0) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 0) Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "cannot be promoted because it is already the targetRole")).Should(BeTrue()) By("validate failed because o.Instance does not belong to the current component") - o.Instance = fmt.Sprintf("%s-%s-%d", clusterName, testing.ComponentName, 1) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterName, testing.ComponentName, 1) o.Name = clusterName1 Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current cluster")).Should(BeTrue()) @@ -394,17 +394,17 @@ var _ = Describe("operations", func() { By("validate failed because o.Instance is illegal ") o.Name = clusterNameWithCompDef - o.Instance = fmt.Sprintf("%s-%s-%d", clusterNameWithCompDef, testing.ComponentName, 5) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterNameWithCompDef, testing.ComponentName, 5) Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "not found")).Should(BeTrue()) By("validate failed because o.Instance is already leader and cannot be promoted") - o.Instance = fmt.Sprintf("%s-%s-%d", clusterNameWithCompDef, testing.ComponentName, 0) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterNameWithCompDef, testing.ComponentName, 0) Expect(o.Validate()).ShouldNot(Succeed()) Expect(testing.ContainExpectStrings(o.Validate().Error(), "cannot be promoted because it is already the targetRole")).Should(BeTrue()) By("validate failed because o.Instance does not belong to the current cluster") - o.Instance = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 1) + o.Candidate = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 1) o.Component = testing.ComponentName o.Name = clusterName Expect(o.Validate()).ShouldNot(Succeed()) diff --git a/pkg/cmd/componentdefinition/describe.go b/pkg/cmd/componentdefinition/describe.go index f2e46f87c..1cb4df6ee 100644 --- a/pkg/cmd/componentdefinition/describe.go +++ b/pkg/cmd/componentdefinition/describe.go @@ -198,9 +198,9 @@ func showRoles(roles []kbappsv1.ReplicaRole, out io.Writer) { } fmt.Fprintf(out, "Roles:\n") tbl := printer.NewTablePrinter(out) - tbl.SetHeader("\tNAME", "SERVICE-ABLE", "WRITE-ABLE", "VOTE-ABLE") + tbl.SetHeader("\tNAME", "WITH-QUORUM", "UPDATE-PRIORITY") for _, role := range roles { - tbl.AddRow("\t"+role.Name, role.Serviceable, role.Writable, role.Votable) + tbl.AddRow("\t"+role.Name, role.ParticipatesInQuorum, role.UpdatePriority) } tbl.Print() fmt.Fprint(out, "\n") @@ -228,10 +228,9 @@ func showSystemAccounts(systemAccounts []kbappsv1.SystemAccount, out io.Writer) } fmt.Fprintf(out, "System Accounts:\n") tbl := printer.NewTablePrinter(out) - tbl.SetHeader("\tNAME", "INIT-ACCOUNT", "HAS-SECRET-REF") + tbl.SetHeader("\tNAME", "INIT-ACCOUNT") for _, sa := range systemAccounts { - hasSecretRef := sa.SecretRef != nil - tbl.AddRow("\t"+sa.Name, sa.InitAccount, hasSecretRef) + tbl.AddRow("\t"+sa.Name, sa.InitAccount) } tbl.Print() fmt.Fprint(out, "\n") diff --git a/pkg/testing/fake.go b/pkg/testing/fake.go index f28770ada..679459849 100644 --- a/pkg/testing/fake.go +++ b/pkg/testing/fake.go @@ -314,22 +314,19 @@ func FakeCompDef() *kbappsv1.ComponentDefinition { }, Roles: []kbappsv1.ReplicaRole{ { - Name: "leader", - Serviceable: true, - Writable: true, - Votable: true, + Name: "leader", + ParticipatesInQuorum: true, + UpdatePriority: 2, }, { - Name: "follower", - Serviceable: true, - Writable: false, - Votable: true, + Name: "follower", + ParticipatesInQuorum: true, + UpdatePriority: 1, }, { - Name: "learner", - Serviceable: false, - Writable: false, - Votable: false, + Name: "learner", + ParticipatesInQuorum: true, + UpdatePriority: 0, }, }, SystemAccounts: []kbappsv1.SystemAccount{ From 294213cc4aa7bc6bf494f14da2e8862f93d0605b Mon Sep 17 00:00:00 2001 From: huangzhangshu <109708205+JashBook@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:21:13 +0800 Subject: [PATCH 33/34] chore: release-kbcli.yml runs-on ubuntu-22.04 (#548) (cherry picked from commit f0758f5b9310b708ef4326c21641ff3bcd4f1ac1) --- .github/workflows/release-kbcli.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-kbcli.yml b/.github/workflows/release-kbcli.yml index b2915bf40..1a40bf6fc 100644 --- a/.github/workflows/release-kbcli.yml +++ b/.github/workflows/release-kbcli.yml @@ -38,7 +38,7 @@ jobs: gorelease-assert: needs: [ create-release-jihulab ] name: Upload and release kbcli - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: install lib @@ -106,7 +106,7 @@ jobs: upload-release-assert: needs: gorelease-assert - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -137,7 +137,7 @@ jobs: remove-artifact: needs: upload-release-assert - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Remove kbcli Artifact uses: geekyeggo/delete-artifact@v2 From 1e248aafd4282f8b6931455c9696f147888c0b32 Mon Sep 17 00:00:00 2001 From: apecloud-bot Date: Thu, 6 Feb 2025 09:31:31 +0000 Subject: [PATCH 34/34] chore: auto update kbcli embed chart changes --- addons | 2 +- pkg/cluster/charts/apecloud-mysql.tgz | Bin 4969 -> 4948 bytes pkg/cluster/charts/elasticsearch.tgz | Bin 4086 -> 3684 bytes pkg/cluster/charts/etcd.tgz | Bin 4336 -> 4356 bytes pkg/cluster/charts/kafka.tgz | Bin 5565 -> 5267 bytes pkg/cluster/charts/mongodb.tgz | Bin 4387 -> 4000 bytes pkg/cluster/charts/mysql.tgz | Bin 4337 -> 4002 bytes pkg/cluster/charts/postgresql.tgz | Bin 8496 -> 8089 bytes pkg/cluster/charts/qdrant.tgz | Bin 3960 -> 3519 bytes pkg/cluster/charts/redis.tgz | Bin 5354 -> 5634 bytes 10 files changed, 1 insertion(+), 1 deletion(-) diff --git a/addons b/addons index e40264704..7022699da 160000 --- a/addons +++ b/addons @@ -1 +1 @@ -Subproject commit e402647042a0ce837905ddc7f953c8daf3fa6757 +Subproject commit 7022699dacb36ba808a6b098ac56c5b07504fd04 diff --git a/pkg/cluster/charts/apecloud-mysql.tgz b/pkg/cluster/charts/apecloud-mysql.tgz index 9d428206781c975d44922fe2e13190ed2ef1af59..54ebd99f8ff432cb263a1c42add8792a3a40eb4f 100644 GIT binary patch delta 4922 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 N|NrAuP}l&1004=1&*T6A diff --git a/pkg/cluster/charts/elasticsearch.tgz b/pkg/cluster/charts/elasticsearch.tgz index 594ce6883aa2f9333079dd9a98194387169a579a..31b813980228710335f7ec64e78bafd56e56984f 100644 GIT binary patch delta 3659 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 delta 4317 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 L(3!3x0BisN&`+|n diff --git a/pkg/cluster/charts/kafka.tgz b/pkg/cluster/charts/kafka.tgz index 79f8cf23aba3ec157d6021374aedbb7445962af4..1c5492468bedc1f71b5fb0bc7ea7c09c59c80758 100644 GIT binary patch delta 5253 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 LFQcUF0FnRzGtO^E delta 5554 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 delta 4368 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 Kt~1vFb^ri`KisDP diff --git a/pkg/cluster/charts/mysql.tgz b/pkg/cluster/charts/mysql.tgz index f044ba8f4f43a2e4dc7549a0a006812b999ec775..ee4c8b3f530576858c1d95721cd5c322f78151d8 100644 GIT binary patch delta 3977 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 jefd93E- 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 I`2cbN0Mvz?I{*Lx diff --git a/pkg/cluster/charts/postgresql.tgz b/pkg/cluster/charts/postgresql.tgz index 64d678123a158b3b44a81436af7e866fbe0c414b..43ddcb3227b9f5b0dc84f649993a55c6e889f695 100644 GIT binary patch literal 8089 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 nX#>jd|CI@pzyJT=e*a$%Dc zVQyr3R8em|NM&qo0PH;pd=o|Y1r=ctMD9yFq`+68*&J=tHk_qJPzo&+3UY5YlVs^; zH{IRzptJ=MC?85WL=F+T4>{z%kxLFi1vvyo?jp!hgmNFv|F=hyZJIVk`O5eI?EC$o z$;_KKZ>~4*y_tEj(JW_XAe%%ganjCmkWochFor|ZFe~ZzVnD4{t92nE!e6yo?fF-u z4G#6wgoJ6;VLElFI@nLG303RVen9<#>Ms%OEQc|E>c3r=XXE~#07g+Xhj9c=v3djm z9OIywX43Q^8sjhn#zF+M5eblC37XP_WG#Z(Y_6}GP&5oxBSvV0lo3)m0a-nw1QNBD9j4=Akjb)1_S^G+Gv*GggQ(F3H2%!imIHs$xhO0RD)^}BV=)guyI0C zB#09yYkZFw5ST)677&MV3{4TBOJoZmO<&Ljl2(B8a~POt26VL>U`LY16Im3o&^#v= zj+VB0L`)Mo^Fu zNQhk;X@gNb*9ZW1hAfT`L`3$&JxkjeTpY4e_DSh6OXxj>OS3^ez-;b@x`thYs!_EP zBW)H8RU`Gvc98YtV!S8zM9?#9+k3a9xBZ42FcO4Pk1dPOXgy z!@^)VVuloCFb*2^K%rG@LzEhgQXAS!qt*wj^;#XO)&+-!YFnwp^=h@ETvq%w@Us4s zG13m%7pMUGSpQm0u+DS+2W!I0*MC_+0EmVr%uaGZ9CkrxDoM{mkpR%kLa=~f0R}#Z z>=vUm(To-2IA|1P1dk96aS~%7NX8ffGmwx491RQ**f5rbMnF&;4bo^k12|~4kr)SA z6hQ(4KnEMfU{=6E#!66vY7j@01fB+r&_qxWL`gcH3<1~#3>3st5YQ$NO?RicUQ#{` z0$nhY3j)!Qg#5QYgbl{pITQ&9K#J6&m(b*_NHQ)6STUQ8pv+D+6J>1>=d~XjO+ku_ zw?VxBK$L|{GJ!)$K?W%n0=G!K^Q{I^PJ9W2XTz=g=+F>UEw9{48=*{bbE&u-ZKFxr zoF*Uy0hYrkBSz8`3@W5P1_U5Hf?kFor!JwgfNu(6N^_BSX7B`@pY=c^FN@pkd@gRY z^U^=G5Yx~~GigD9vp`^{2#%j9oe6;%A*V?75;jhGjympqO8O#g;OALeFn3nSOK&wU&$l*rbOo9|YtBrbK z!blb(JYf#wGXRi!T$~6DG=Ts>3kD2E>H$GXeHhM` zRWhtx-&}Liog@);C)j94mW?ye2q})hNN1W>O znv;`Oke~y)8IrUNert583S~8B z#GPm-Il@N5Sd)ldcp?irjQ6Q5WCeC6S`^#XZeF#sm)dZRTCFbBLP_QF+QfJ}5W85u}=y3*;RRTQ04s8lYBC#(PxK4Ahd37`q0yFu`E%FQ4iP40cAuNPI2zS zB{2geS+`437}pZ2mF_brS)8^(rBIr$dpIB{BcwPG?j~J`lyKzeX1W_q5gg4FDyl1{ zsL$zG3K|~O_k_~biN8`<($Y&VYjSltgk7lgU+&C9WaFTzEN$mkZH!^#pQx~pCMgnT}^Ey+!ux=dCVyNgJgvLN?F zsVSWfdO6+`?_{umrbLCDugS=h;2=W*f(1h`n~g|J8uqaxHDnaRFNxY}-=0&O$d!?jw zqQof(u4`zcK;A zNJ)^!8Q?a1kaS5-5DC%=rQ1+wiref!-xdU#NE+jGAuic~+w3izYI#$Mf;T)d%Z=*1 ze&-EW%4;EW7}5*~Bsq}{H5%Fi1e&F9x)4uYISjyODM(F&lh!Z;L$hUnQ(TP22}%z z4Of!71V)@lo}6f0e7G6$?0AurmpxU>;XeRgpPV_4YSM0CW zg`mtqfPo~&5y^tj%OXq1C?gU8C}2~qLDNH8pdnh8!N{4cVPpoU?LO8DQIU@@!s|D0dh zWJ={GWWex5yG?kzz#=VCHpPatL=9LX(h}vY{AZdW%t*^jBmg8}3_;sj@LqH$7C~(c zJrv>`f)YlEsYG;!9*UsJEKVDt>c2vo{g(y! zkFV^PeXPF8@0FX464}$6Znw+tGv_WuyDP6L*U#Kcy!~Y6q1?;kj{U}IPd3W_sOhZ0 z$xR|7CVXCSKks7X?S=azHXQw}%9Oq}SNFY9aeM|A;s0d#Z%=<8)PLEDhWDRDXI4LM zUUn@0%Po_V=_3u-Jz3FYIK6)B&HLvY9o;c_O|P!CB5`o}(#MtJBlp;Ce!3at&iRiH zwA(RRH|qzdoZ=`bV8|WG+6Pw{Fat>Nm1>oVcD5)$c)M z%;smi9yqeM&A9#1&i3zjsA*_8cuIr(df#@M1g5B)A3mx{zcDtiP11q*k#iy|`;Qzl za(^2|N3H44T6GURxR6r623oV`pw}iWT-|EGpUt`*_si*)x-+{GTGtOfrMR;oUlF@YOS$Bn3^seR)|}`;T+KkBJQ$ zTVZ&`b0aG6S#)i{2)~I925S5I<#aj_e|}IvJwqq`(SQn5vNsQBs%7}yVvz;Sb6ek! z{Pu3^&ap#JHK~=iXV$h6EoO{fdaFrjMB`No&F8D?n-X$DZ;hUtUEsg!?HjMW_LPjC zaNvh(NoU`W>pb~<<+`fW4zImAYW$HaJNGY+{kca{quh==zd?ue7~amD7Sy%wztnZ# zwJsVrDZ69Nfxxvv4R3E8dN(_$bwY=F9p)WJKigk3?Dt6%GZyD=9^~*p`R^IcRtD5x z5q7X&!)?mNH`fhW6!`gfpXHF(yZX=ndP9q6x6V(xatwU@OWW^PcXDied?xb)N6OP%i>@bx+0Z9f#TVU~khd{X(yF)DLM zM!4gNC9H0f`x}Q|vaVVj++<#lwc`{=j$Ev<@7SWp!;D{o@P6ixs_hRfxYnR@yIBdl zKB(9>yY}o|7cSaNS(UHWZ@lM5gyH<|X@P?RYZ!)fe@*Zqk68AFz#br)1kE$HqT^RFux4#m~4*k|IA{D6C%3R0@nYx8Ja z=Nr9SlFxFl>^^#=+2wX`vtN$CUM-QG`O0aSbjQ*@#a=OP?dLx?_e-37WuxDlJK7tv zl=(G&J2GTN$dYeA18JmzxkCt9v z)AR!VUH_@S_*a;0iJLrX;QprzCrsQqcH*Q4m8uSVE#~UG&FZdtIwgESm;A3s{d;3Q z^Oxzf=huE?=Jmn!g=-y0ovGSc+ir5L1A!6Mr#~F9!C>pD`{SpRIkAT~wERgMkX`$? z^-W`N;IFGVD`LpLPZ}U^Ryw)zaFc_rdqm!^Gj@)>&caC_?X59mDOT}f#Gn2rj?}!^ zKcBkbh#fqt-o1Oe%MRFJdir;-I>tOzMQ2(+c2s|KuI>5{9a*te&RZW1K6Uqd$F1tG zZ*rWuzsqs1>!Eo=zBs!lW`OB8W6u+7CQLYRE}-GD(Mel0t$?!88~P^7sWpGJ@2eEKsXYc8Kd)tbGQ{L)Q+iYCPq%7dlL9oG>gz zQBQlJO7-58VlE#@+qZN#XgVpgp5mu70c)7OcjhNP3)=m!A?o<#j;F>39-na@=Dt7v z;J`7diLd{>+;HOD(!JEviBD^vm>c?#ovB^V}3kLT)`sRw2Q*Sy{&HKIe z(-$*7v_3kAZS8%~Q9XU^(_5Lp{?ILdl6}yo345ZKOv_dDnOC#z&+~s@9Q*SR-S!MP z_e;;z#?=9(ej|+!P$=)(_M1I4VO5LXCA8nHmHv~-olHB=dKt=Gg zW8#QQp>sgn#^X~myG$8#Y}R(ulpfDcG#K%7-!*6Qu2tT3Px)4t^`m>PBaZ5$=}J}G zJRElV+1InO)>chf;ZR@5Jkdt3KlU!S>dC`dWwpIrS)mcKHqAb(u7HoYd?s}(ig zf6lq6CE(^acdOJ=-$%QR|Hbc*S2xZ5Xm9j_Z=2^dA6nDi^~;n7-!505tb2K0^=)Gw z)_r~APY?F4rH1Y5y6f>rV>O?zc~kZ0<8h}PnVlQ?XJtNIS;2qBzQI|Uce`gr6rA3; zVRy>%eWTXy&e%4qVh+)fY&){)q&BHJv(YzqUcWqh`RIwE74H1I>zY=JUsZR$^qcl} z((THPt}X7c?Qq4Yg!DffwP@YLI5MjF7kBD!{IHTKe@WFtGiuL3ck+X+n=9i}#^i-8Z`-Ben{jU~`>N^YjgPnfynW7V zcqODNxAmF%^HV!c{TYq4Y`|Ua^UbE1$iz&Lyl3U*z0=z$KRl=>-|C1rnK)>hI%V<) z@oVA|d)KBXtQk8gWaQP6!LZ^dY0+!vq_+OyV8aok9uR?jpT&JZ)Y{Z~=6-A4dGj2# zD*u{my><3(*0OW0PG(hm{Y=!;9n%u-IOeH4T-jZD*SNasHy%!#`z3m6M(m@DpS(3~ z{pi%yGnil;?5p#R< zx`62sx_27srq0Z&kd&r9xUx&b+|c=FV>R_|)M?!8S)1V4Ry(I}e?6yL?(7f#)FSu-&^;L_2I@7j>Pq+FXx|&IG&Za_`7_^M^(a;uxlgR zjP07Y`r)g?$F_>U8g;df<-?_oz8JFoqP=04t*TX8bq#a)kE_pa(`SWFzBleJ-f(R# zW^c@jOSM-u+IH>3H>R-F&%PT`@A0E!`S&)|`?Jxisa3OeKTYp?_tnFe1=OEZD+6 zv#m}(`*h5w>iQFZyEJvrgRGTvtKV$bzmo(1{eJiLj)Z6DuHUWH>{~$IW4bRmVZiW&P?6WC2((g{_2k(yhFWVN|-ye$J`ai%zMLQ z-@P*Tu;0*fvbOtQe4p9$dF6!3hr3R} zzX{rT^V6X#r{tYB&VspdQ%;=uEctAY$;mb59bD7p?bvutRlJ~wY0ejq26ICPsrpi% zr}SI#@9*^~Ybs~g4Zf*_d094 zYFU?HoH(+7_Cad*of+u^+s@qh%bm5zXXnoSxNLajE4$yS@TA(M9n~9bnmPAo$Gns8 zq+C7p@de$b)0U9sGnFk`#hYItzc^t1FsIds9eY2^B2<4oIQ`XwtSu`ua~?R_J}RKU zDWFd^dZT)Qqg}s2$F8n8H}m)jl{NQ7w{}&(>eYR9LDk4Zb+Eo&o9;h!XlKyagKdAm z_;$eAZP#xFWT?>HSwD>)ysFL@`h|1Xzn@X%*CtOU(NpvNm&{w+cDCckIRhs+#%|y9 z%#r!)%qRYrtK6{dm|tNbs-FLS(uz$@?wy!;w6iv)!=VGc`-CGW8$D7y%%3*H39dAFBXf?L4)$SRF zozJ0z`_{pFW<`_7Gm_uk{N0_rcfa{wWzM~xk7P~_{vvqRrU@4p54+aCJ8|@AYh}ap zbMRj$T5=KNy12vRv6G?QRyEd)#x^dja;rf?Q|r9SO;XNQg$;*h_WE}7qWc@bp|2b{ z;K!qD&L+&h)pb+us;zIWJUnsboukLIPfp{azPjh1_i@_N!}t3-G?se@@9$_&zJ0^d zuHQh-r%S^InxFN#w`96e`*rMym_BEhEUCTyGr+AU>yOS1-=4W|W&Ss>%sPJY{I&FV z9#u0Z{MAPl)nV_)9{5nXJ9UNWfO?y*azdR( zTRy#ZiJ9XX~^-34IM47V}-g%BqKYA3PJ@B< zme<;Rn>0HQ$vAN6!M*8;tNY{vt&hh)4XOm)#ZrNbt4(Uqmn4H6OoE)VH2nXXX=8W3%vb+Aj0!q35|5h*%U-v&k zJmY_8HJae^`(I@O|H=3t9wMNa=o^C20Mgnl_HDbE4`>ERRGs3+OHc^|O_LC#yakmI zgpm9Vei51DmCIhTcw-V;Nij8wh9?8G$rU;5xuS=N0%v!$H30{``nL#ect~F^?3iU)v7}@kPx7d1f>K|&)dlUP6$^hKWw6}p-)>XC})xd{Bf@u)zEbMF0V!GGh8-@@=5>t758 z@E2kM_|pH>p7Ecx8lAS>{wos@)_-Tn0U?4rurYKpVT4Q&py(9mMK6G*utC6#+kyZ_ z5@~iyxH8H?3WpRYM4(5JUa`@!dhjmI0Bo3@fk7Yz0@gy?Nh2^r&P5lP7}^Rb+6Ylz zGeF~{^!K8RmoBv3=tOhH4rt0t6la)KeOrJ=VK&S_kOZ%RqFvpYXlKG1Y*lud7N>=1 z`YceuL{?FlnuMjmQdseMr#$e0yP!D{7E{oS+e#`Zc42XqN}0tM=ZdXYJ|G?xoG7wn z&xhVGdm@&@OM&N*|IS_dxpx3x@;@w8>#6@~LdxHNlnKc7Kh6SODj%;}p-3OdTg((L z#DC&|CRFeVvcLdw%wE_s6GKJO0xoZex?Zjenj|e6e=@s|vlOb!hjJ;ZeNN#o1Z?1? zzFgyziFbV@erJ{3_lbYdy=zh%~)kKUL6VM8h>liLX0W48BlW-$N)b6f$NM2=v{M^=KzfUv>`+Z=Eo0(fxDQwV| zE{dO^ozWyLu{}IO;a2=b4ohIbKv2S?I$wNr6!HF(?05`y=|;JTf7dgrgM?QqpRTtF z^jBQFNO3XueE4@uIas9_ry|`V2usY)S!jkBCU}L>#PGu2@1E}BrRuFB(*k`Voxqco zes%`45uF*@ZYzO(1b{-md%h9w-}rR7`CSn+Y_w6y*MP8g;SfRKQwh9InL=UZNl6zw8u&IBJJbdYFJ9mAkZnq(67ez+o(l;r0k_c9LZ4UkJ%{{%p8el-xcZz* zfG__~a7dB!zu>U)_+MoL9_N2jlt_bIB7=_e&S7b+Q~Og=AYnEoWJXqo-S%A9)koFoe4$Z zGVUK|lUE_{R$o zD+y;3U;xmLcnXw6^ic3f(R8{ zkt}_1cNB(UI2sJh|6v$5{}214{)1>R9!60Z4o8CrVKj&aqkjhw-k}T|sgR0@2jN%u z)g0Xa6Cyc%gF;fyCUDX7NS;?;qaX@{&`U5!mLQ8M%8Azjl2RfkfbW$c9sna5PT+i+ z(y0d^Fz1pgF0LjpqbW|h-5}_er59z>VGsp9FF_d#nk)0@NBB>(6~S#q_#37f#Cku% zj0^aCF~xsRd4GH^1JCxkx^+EumE#0Ro~JY>x=rt5Ig&64J0#5)BnZ9xp7K8xwd%i~ z<~!_TtNw@MHU01R_WHjIYHIh_s570TR1=U3(i@H@7!RYQKbnrGBo0U9`^U3zhTn(d zBpzaK8V&o?#}N*uNj#26kE1aTz7MD4Fd2ot$73?a?|;2HG89B%G6BCA_J*CX*9k|j z!^e|;H0ejdFdB|V{a&yC-~L{wzmyvKe?ig$^1d&5bn?aozMZC;*1oj z0=x9)tbMm~5(nM^yk1ZVR04tj`tjM%of#JyQ3?}d6*R*s#wig97er7pO;Lj4Fh$6T zl$ZczihsiuF9aye@{}l)f#=PNG}Q*@#RUpM6P$S7!2$eRP6WwH>r%P}62%3Lv3%?u z9MsQuUHD1(4}!+Gzv1zxVSn7wLmBsReDCA zQq5~iCeW+1OT`75BS`uI+bX&83G_l!EiRWAKbO zHt_%yii|Sj|GeZWjjtwfiYY4GU?SV~U&af}NbrZ`><;$W7X9z_hfV)~I2!i%`o9ah z_5l2ccD?-x_zR`-Nw=$4zTqVvNz-P_~1WWc(2B`#P^LjHzadeTj3ce}%WEuQY z<6C8VRTkbP6f3eR3N2IFR*O`WDV(Ed6Dz8oWt7pZ$X1_`_vN$Eir{fxw90()@|TU` zp1k}8VlJ>n!nf`_fNuv>aX>Yv?!j-j9DhU@3|odV8Y~|csiJv`FJ_i|Wqj6vwhr-` zDLQ;gk2a3*3OrTkP=zlB=O~e}5D`b7BL-59ilg&6EK7tYji(%9^A8B@0 z%x3gG_#Gcyd&m;~(K7;`2qX%DRK4gHui#xlQaYoUXxn8(54>N|wr%*8?m-Vj!W3gt zNCe3x@O;ea66jIA6ff(VPw43I(oU!Hn(%I(ebSz&r-KnhFd1yqR z{P1~ex|Fk;2-{Hb%FdFlU&^}j@G}L>1!Zam{&%wTo%9=R*-mf2!`eTt?7P~6YCS3A zQX=i5x*cMEwT8g|B%$??S1G3ib(!vDGmYNMY-tr=+>{D;HmjcaZa}I=G z8HS7HYS!et?{jWX?X=PVD=+`=AQiO5|BHIV;hO(98t(nST~HnWLGoN0`!lsT3vGQP z@0_w^0w)e-k)a|9QGX`aux`>!ZX$47=lRmGSzeiwHcMt6TP|y9Z)kIQkbDG2S%OT# zK-bkinaO|foTq$#H7OH59+aO>aaMvx-~a^9$^v8UsE&J$F>cSIXsMlrGs%`lQWh>&m zII7}D*FqbgEq_|FmO1h^URQQh3mUk9!&F%@!eDp=hjaIBGzh|qb4F+ihbFi(sWZq5 zsdNxNMRkeDbX_yR(cl>7T!C*IIckXqYn{4bE&Fv3qA(0Mb?#U;^{=Z|5;W$fU5IkR zq96n(M)fGXL6DNEDM?hxu@s~^@m7-i-l}{02Gp?sCV!rn7U6eiVDN4EpI)?P|A*mt zZ~u2eH{icpLE1mlsUYHNGddf7OcKsy88DP!ZHpPT812-y;WhB1_dON=kaUy&-xU~i ztNss0P5mG5@n5^418~>EHTY))H1e7SS-qu?D?0qhFaZg`Qx6uEu27_*m|N4#dzZdqR!CwD&LEjSp*)Rd^ z;67_K;0e>L$wnNQE!RT5A+? zdpF3d+s4lWv04MTEJ9|0cQt_DDSysYDInH?et&->sj7c(v<2Y*N^^Wss0s_X$IJbr z(M|Th1_mnk?iYc9w)uaVf&S;8>tXV9uhJHN(zLJ`; zexz^n*nhF4(wC-#{`?8>Tk5|B1AZYEaI60pHT6Fn@BP2skkS9As31;xkw7l^1x-*K z1ApU}Ad6hPZa^;OG0fxq7)VO53TCd16|xwaI??q4@AZq*7ZdmoE`WR}1RldBf?V(- zO<<0yY8PgLXTW%Z!C8)R@X|g1>2$Mo;YCuiRS1-a+M<*opveyqoRFMMX-c&R8vK1( z7(Auw%_<_Lj!^UQT>7i6IWC`E$|X+ApnuO+D$dVwL&bTV-&D~~;igVq;pXaUW54wU zX|zzX)E+kxZ}+_s_jE6GOZ#7*&|h={Y_tDs?|=8AUVoqe-3itGKLcWQE?+B{IvO^> z)U|7W19OhK;njg?g(f$iBv%Xb?zMx3z$I0SmAxK5$A992B|Ib^}`S9D}+Yae`2s@8|d-T?QIQrLzzHQGXJL^f)8Tha20PpYK0hK_Rfkp)I zld{Rw%=Dp$Yr2H*-UY7BEgf&j^v)F=rpQXMqS0qcc3kCn&84i+OF>wGl|-1`GVmm!baf}0?|t$%UC7HIU!*f!vLZ;fi+0twz4ByBt3Z)^YEW!pE2 z|9hj+TKwPN=YMxXb!Bjiyip6*u!QTcrr21xaq+A9+S5UG2X&2OMpB7;XZ73CE%m=T zxxd5$Z1ey6gSGp=!ElfN-U&7C|Ez*99kpY3!caD^g9Epkh#CO6bSnhDD}PX`lb642 z2M{{mXSc=?)=ZGAD{DpCO=%YE4h^Dit?4?)QC;pe`Se(RXm7dIvul;ZAp~z0r5x_+ z%xs#l7MWZx8{c=UR(#@teFxHE*UhgW)owfW;A;MfW+{LC==$2aaE(O*|0(rX>y%=x zc2S?!ZCx}*-0Wh1RNRtoX@CD6BJ|7N2H)oYjo0meZ@jnvJE3;_UoLmu{M(;eIY|l4 zUY8qV>p8G|m*dkXudM}b0m|27%rCmEyqR-ay_vHVP6qBW6om|Ilh1gepSzcL0Lr|L zCKAUeg!q?BRc1*d1fYg>X|1z3D@ zas~+%(hKHPbi7W?4)L=dLhahgko;^cHngz~S+ zEPZf)5ClOm==IG1gCMB?Kj;p+55nHC6ATAIKMWrPVK3|r9)Cb^k2I`=Tq+_S1YbN> zeRBUKKr;Fcg`}K~;IiY9EGz#E{V?zYFTo61f-I&eM_wC9N{Ji+eo%sV0F2CV1Q$_C zBM(4e#wAr;T#sN(QyiU~`2I;zds%cH_@UqN5|pu^nKDIB;lIsM1a}nS@0iXY*5?t% zT);o_2!BX-^g3gm68RBDaf`1GJQJ9RN)d@Pib{Muh{kLH- z>IS23=ywL);o$M#g2$sEXzjWBZcyX@OOoa&?^yxZ#Q($Lpw9oju(#*`UBCgH;h5y9 z0=xC*u6@676Z_r)yq!`BR04tjdHUj~_Lz$qQ3?|y6*R&b#wig9mqbt!r6@shh!8R& zB_=?b;(u_>a{&rxSxOX2-}AJo*Z364DP-~j$JBZABd=~Ac#62&Eru_*Qq z4yyON(NwCf1sXj-g3K_c)wXe#>)JTWYZWt`adCYNYKo9Es`QFH zryAGRjG$9Rmx>EAL6Gz#HZ^kfBj^OCS)5D7XMgk~CXg&{isHcC8d=$F6{CNb4#6|p z*vJD=C}xxy{pU4LX?#6`GfYvTmgW(OpE12V$q1Cl(Obp;GM?g$_4t(r{fImksXpl-!HmP=F z7)MuGsn9YtpDjYG;P^(G-V~MVh+_FHLZN9Y`f8wxGKCWqO=N{-*^Dwe%V&#a(5 zSRg#k@Q249+8-Ji>CsblUxu&k*w=E|T`2B`y40`jzJXJJH z@zvO3FU-#h(8ejgFja@o>CwuWzGxK8_de~;*3%AyAXu26ll6@gJhydgXL!EmF1i1C z2HPIeV8uL7QzGR{GFv-gFj!#FHqARWs9IUjtf%Ln5JljUr+Kw?O)OZ_JpJG2{eKIg z32OI$shyp?eD(I(xvxH`FCSx*{vQm6%lCh{zyEgup%2ed8HTEu&fw=aKS6X2B4>=U ziLRCiF`vx{OXSGg^U}WxtmOaSr454MujIU8!(4JSC7preg$of(T|DAnK*5CQ? zNk2bX41=!uNoVKzcuYS)tK9i4j_VYeDPx z*6o>y&t{xi0_92qZMCIvis{Uk(-TAV2ySkGvN+9?rRn%~z@=)5DaMM6pIoPY<&?Pj zJuN+sx{hR-e^D~0Px(n{2kely*aNkv$Ui&R$`X-@K-Fn60*1<2B{a|z?md#WG&`i)Q+ zVM!eGoGC3NbBSwK#-Km7*e@IJi^GBiE+WGMhb5s4v+$3OO3|~}ty#cztKo<#tWgE3 z=9t$44aYbws8tSeIe$Nrm2SWFW&IUkrT$kK|L?&Sv_bz1yMxY>{x{hBe|7@Z_z#k0 z(#W64##w0T8+sR%B_lX>Q5G{)Bq7T98kR+x@lE*d=)9Z@Hj67W(j>`DvBk2Ic1@r2 zi{uk9$`WJ>db+LF$&CMlXFTPT>rvtG@u2v1hT|e=1P(ypxPPcHf`aOIuaPCqUKCBW z6As4N;xFOYDa3r1afVEtXBf+ou|O4WbvwATRh5Vpx?GhLaPKNtUtN5c(#)-IrCCMX zHmzinP_A@hZs*$C7nHu>j4Ced^1Ia^%$v^LX_dt)9dVyZksoe1+8dNS7ct6J(7;1_=dDQi*2L}MtHVnEza%M5 zh{Ai20h{!{V9EbK7!3FJ|DAw&?Y(n*4RMwqyC@f9>2Th>`2+7^oaHe39uCJTCu-0u zulYF3k4pQI>!AtHR?Sh17?~zqS9eq~8gK!Jsj_4Qet-W64kzxfLC+6L%rT*xT_G~v)=Y5FJBA5Y&@!DIHQ0kHgSu(W$8`l^5Cp3RcPOjI*G)?bYHQO> zM6qC@AAlpHEDF{TQXthS2}?TWgwz+_Qu3{j>K?uV)a1Xh=SdskdvD1goAkd0XabA&A0WRJvSa3>3{uxE&sO@hMm3rermzn=f!35NUp-`#-W|L3S6PI;a{Cio>yP#go}S0M9D zt8PH9=;N&uXAQh#tK=COr7d>zW4Uk*{c!!7Z*T2<^qr53PDbJo+dCsRe$yi zW5H*@c!Ivx4E)!w{KvD^(uL-NU1&JDz!eQAakj3ZUBXp^y2{nf)yi?J8&d0`K&ceh zBVNDtLEOVPfi2~KaYNtf2G}J3hkyM}UH|KJ_x|6VKvn-ULF`WDYfPpH`?X|>%FE0= z$zpDBSv7XpXp-DM{wnW2HC-5MgK}xrzIuK_V?Z<=1LFz~1y1n83!>sFwEp*3(*F3{ z;jeAd{us2M{PyTq_v7e49=2?M&e>UYnvS9MrkddK-Fu)CC^MlEKK!VxGk zL&A6Oeb?uPfme8XCkhTzWCdB_;Fgpfmp)!&lofg_2rD*^#0rI6ob&<(51Zqu?>8I5 zZSJ>AhA5sq?g*|5@o#)5Y)Fk*nA?iD{;jhbHlze>d?anU;J20k&e--<-v6D!VCnte z-TQxc0##;ko4k?>mcoFSQ-9O!TexQU)p*VBpsIqpgki06du8>j!j}ARqJi(43A};- zJHt-D{{HU{_WIvWpsxSbGrZR6X6w?smSjTJZ(FHNjI{ zJ0Fd(IEy<{xQ2j;vV^k9Mmm15cwdv8+E#D3%S-m%(wKX`fVY)RcYjSP%B@Vh-l}Yn z4ffqFH*1T1T3Qw(FiEbaT+olkD(qi8UbKF{nK$2RwU&;Be=m!myM2RW-JWFhx!`%W zhI|iT!FI1FG5-p1Y3463ly=6GwjRNgxp5*GI@{2AWb_2?a8$NCEbI3AYN|zpp7YAB zHaQVn%C>g9?KNyfoqv)1HMk2$%fGGYyM_GU_Wmy&t9MWWHrao=y(Rx&ufPBP?@plR z|Fa7I+)#7u4yL}cD#-yGp9q!Qy~3;@@O_R_oxc8ga{__GeX(^OL4_q!u-UGp-M@e=2SXTgrcz z5puV$L2a`C44379XSkRDJAr2TUu<_({M%10o~DG(-WCUA<2^7>!~Xo)YwE$OgI4P^ zUvyl^%s(qL^MB{WN#8j`QApo*`9dpHvPj+sDB?CWkT^yuU+@Iws0@5-!y6>YFM=w3 z#bPY;*DErxD5?r7mu6plws}e36**Q$8ZD-&kLyU~7cwjP`?Z<{xx3V?CL!%Ox7G#M z@xMRCbVetP3;d!pwu%4y%lY5Kef;lk-~e6|rBE>Qk$-W!*>E*Q22oB^!}uAAFO2Lj zz60;VLL|s3rYTHP9$6$y-k59YrL*$TJi6CcQ_#yvn>senc+3N4}cr-(1j%_vrt;19(RS<++3(&z?!o w&jdGdaXw9uoLF?h|M2`v8S?~B{;V-yd)UJs_MqYa0{{U3|Hq{ZbKAC({h5Eop4?9DoJD>3>rOLo?s879&Ad9v#BMq_op%|C zTuG=&f+0ZLO6vRDZ}3He6iLaB<0O5;Okx{Y0J{rdcL6Mx8HSYigDB?$v%%?tWWryP zIQs4Z({MN(9v>d6pTps>{&O@s8htl9JUJSThQp)dgYSl;gMX8wco8`i6jXVL{OISi3bo6fpeBEC-962G9?^6lG3-BamtbjT#Y@Frp2Ep{@6e9 zLQF9UF$pO0iPr}bQNkyHHzFe*Ktf`iz~wZeQx71+lyNFl7c&~+WH9jkfi1nVEf4*X zKlVc8K}J)dvVWe!8#!0uRL@oT8KW2iIbmVOGWdHw#s7*}aLIjd!DN>UAyPgW4CYiU z@~I!NctBDNB9@1P%Y2Gc1w9ZM;vgn88HAWdY&obb36BP{bK6!~&!=oaN@y)Qk{x2Z8z<^L6la^w%K z0M^L=qm%JLP5vK_$46WF{}^cpp5ct-kpMmRN|sh8h41aa#e#C690>f+)3aatGnU0f z2n>}7mwy1yFo;Noa78jorV(-w45kPv;T%JtiD0l~c?JUGG$I1I?|C~r@SiEkNNnZ4 z?iftRHrLzPDTRtwIZoA>g;=X6Sjr+cU#hZw;DRI}iCBXBuJi?xgtm%ouLPR_r^!6B z&C)~`1t@`>`V&Yq8k20<=NPagB-ygk{52H|(|-cTM9>5y+5Zf)Ql_h7Db7O{@Ig!x zGRGJbF|ajiefGsQ#%acGmK*hMTT!GIRri4kIhcRsTmXR(W2Q!Z!LH?~+c6NZy^mLI!vN|a^@CD1GP|n*?4ZJ>YiYk=DSTQt4l5iN=VNp;c zC)ZQ&JWOW+HVg|HwUZSR9vAOtK=gd|$kP_1c}Pz+(Z)FWYxCttyw${L>S zegb2cuR)s2g%_l`w8oAqOAcdZd(9I)nSXww5?Yt6RLJmsj$Cj*NOQ>rIgvPueKpr~ zM=PMVlRCK0mz`PAiMIo?74_3#;P%c1%g7vo(+}8$v?-s!c<6aM5ae92SV=Bv&q zPj`NLR;f=}L*68=cejfOkm0yOZQiBlnPGK ztFg4R!a+ylJVhX|KoUmCrSQ+LXn?*~YtHh{Vah5JoqTb?@4$x|8vBpSgw-w@_;CLT zYu2(_)+VnqT_rEu=O=(G3sxqzT83vt3eaM00^CJn6biXXbjYSYNqATkYk%TnHMBQtgZG79a~ODtH{(ipumh*gn6UNsWg-1@f0&xZzWot**@hTF->Tk$J$1f z7#(g+^P6;du|?(>_JWy-Kp6WB{L7Nb~T&KrdBo$UX{ z{T~k&7!&^w&XR}NV|4HTMt_G#!}|H((c$rS|Mw{AqX*FYUJrP00zGM&PX+@y^nEi8 zST-N%Uw3-@5?m}(l(2022L?jtr5Q^x6BK!G0>SE+O!Bxl zf#1#FAX~8ekpc>uD+Bb*a&3VuH;Fw{{*MYPtX6Ae#~WMN#*&MQ%(iXqD`{F1VYc2{ zPn6r6(Y}$oK$&6iaet+s(IwbjH+goJAY1Qrr3dLpke4AHBeE>E$BEtEHm>+&@{qVvz|3(oY}98oaXtj zigtgZd#$vdHSYJmDB+{wa9E+xm@$ncp6jws8qYg6i4RWcc(~y_m1H*3S(s~G1cQPC z1-6dvK!HjFMlNf$9El)s#iHD;T@!c})M`703TCF-l(7g&(tosc^`2sdlzOAhbo72( zNOLHImv*aYHXL#Q(Qaj|fT3KOQB~~g)>tEB9YavDN(wW5E9F|2Kk~xpY~}l>n3Q+Y zKiyO+n{yLV72vl=Z>b8KXuK%4G39}0z~Yo8n26P09eYRRH`qL&wR?R5bj;WVxx%LI zE7Mgi4oAjR27jt{@KXb8S$R#XuPC~8ZH$VWBj%6DnAy z&-iVtKD?L##;I8Ds~aBYhOaoC%Q=Si$=WPlp;5%v=TwIRg$}jyp+j+DP7!2j$jrL&Do@e2Sde(57i1WQ|Evp#L>l zH173BZ_;ekG&^Ca6Swd;qh?o+5w4 zO8h~sns1dK)rIKEf4ASdUu*ULukg+rynJ=>{D0gRH{t>MSmXaW93G!+@Bh9~8u{=O z3hh=?I&s6VZ+=leV|kL$WG;(2N{tzlB;*tCTc7pYWK-Mvf49IW#fj?>!mZ-g+ND z_J5@|myPtQ&f{(?J-EFsp1G(3>$k%v@bM$~Z?*eLo7>Q>)VX$wPLvjQUcsop0|&+0 z>zMwDf+&5uDm|Jj=Uc7ZB|S6=qC8w72#qUF4}AC&XcEdD#9^i4OxYc_?YaZqZo))S zxatnKXPYuNSX>j@cS-G&@Jia|97AK17=Pdk^%}=sf|;_Eh3C51IoyzlViMLmD_(Xv zys^Id7IMi2#!my~Zd^mom`$eN23}M~wr*2y2BnS3+uMS0S?~sDlfq8ZxU|ium0pXc zpfpxDuU5j%!@Dh3DKk2Y2 zNOSlT-senI_-TDBC1{$_q@4G<1bRQw-fG3tvHLOl(^uT55ux#gRe!eUVqz0PO6H~* zOq|^U2?FH&jD^VEY+3salJK{T3V(c+1n88lFu09y)1&oDU7^dKs3^jWAT|T(t%~+S zfPb1y58P%dLfs1D4*6+Bt=`zXGIXp5k8LQleZ8 zW#RN(yF=2IEH05Sg1RlTGCO-9>u`Y8AFrCZ!Rjb`iX>YkBQ#u2nRyHx2c=S8;J3`9RCr zws+dStoy~Av=$?3<<-a|tW4YI!!6EFSOK+#*02;>AwJuhXvb;o;@AY)3hQcGt!gyX zRk-`cV^x>8Yx3G7kVchW*MFmr*LS-y(O^ofKD}tdu@2?fz7<2H#SCdPLK;nwW&@i9C=9D;iuU!wSGQAy#@*Ri)US=62ag=LB0}XIbwPCiiFSynRd+qx1t* z->W%+PhjNL{kVLVe_0@_WKSE4y zR?h}X)x1HQjuZY}Th}b^UP1Lk8q54mPGh*j$T@eZ;?sQga!|*q1F71p)K=zm5H_+5>>OPYieduQpz zD2PkRFX?-pbMWQtXdAk}M;AKyM0E?&tD2tH5E7WhG*Kn;!VVvwkOy?O9=sxV$kIMxs>k$su7)9`8pcGW+QD9Aw8zE~uADUhn|@SG@1JW%aCyuEAnbIc+J8yUDn&{+EU%#dOPsC+#H%p zV9sM<_kRpUMgzWAvC7-JHHLZF`TcD3eyls~n|?|we03RbW{O*DG-aV7=y?*Rj5?>e zDG`ea3{2O>J$)v0XtaK{Km}hg0ZrYlgxv+|R%0T9h1yS)x0fAT4Zd3W7l-aOvbDOO zbXjox&uveCYpG5CUy+E0MBqbg0M^O>;i~r|M3Ddry3RoqA9FDY^8X_#&3Svy9HYC;#W|z*~}0mUDRV>^b-37Jo-w zI`}k1GSJ`|`@{3Ec)&uGIS~skzv{;l)%CAFr@-~+7#{9pAnVruc(h{w9gMg8|3^t3 zU-xMI2FEXSnvrbT{`C!b8ip(>9{Ks6`~467wTMW!;nlwO@ugYuPnkN_|AT!DV(t1r zJYL!V9iMF1|6`;=3p$^Fus1Y%Hh)w4Bd&z`Fuo6A^XA-%wGwN*t^nRl$zRE={PViqNvcWcOk~-wS^BBCi4!_r9@U`bZ z_3uB9hbM>Qt^9wC^hwT@mHW`Ypi|`o`W1ey(sZ-+uY*C{Hx|TN`){VuzRYA0r(Fa8n`J;g zkR<{Jkvn zf1=`T`EJi#rkSWw9C~=kd)3wqRF7O6PkGn7t*kh|#FmQlAnmHCS%28psVQu)E?WC_ z2c*`51#z{*Q)-TmO$oNv{3pdS^@1%J+Ul zxnoWOA`pmx(UCe6HrF({YQZ$6UZ}go#CK zbbLq5_6r{mbts$3p&*GJJQ4`Zc$Mf11{(9GS7)agDkmXz>wlx5q#a;i#S^YVio;ZY z*G)qy+q>cMpBY>UQ|cs3+7G<{gEYtW1*CpJuYb2H?@#sUR9g7Gf9*laF{E z-5s9kZJ^*clz;!`tnKsc|HsG2EBpU#{I|zR?qaYW&~2;0K3lNP-j2-beka@g#B#(3NcVR2d@}oq??7x|&o=fWc0`bkkKq(mLl8ROB7fhF#A?$n#GZ@y3VxE7zcshq zXxTO5VLXJpQL*G!In&0l2AoVUtuZDl@lD+5CL4s_YYi#V8pWgw*LR0(f|KT5AHT?(>85W TNBaK&00960Q)cm`0GI#(yn!E$ delta 5343 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