From d8f92e0b44acb2209cc792c74255ce4e8521e9fd Mon Sep 17 00:00:00 2001 From: Somefive Date: Mon, 3 Apr 2023 10:12:44 +0800 Subject: [PATCH] Feat: vela cluster join support overwrite (#5784) Signed-off-by: Somefive --- pkg/multicluster/cluster_management.go | 71 +++++++++++++------ references/cli/cluster.go | 9 ++- .../multicluster_test.go | 2 + 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/pkg/multicluster/cluster_management.go b/pkg/multicluster/cluster_management.go index 35251596c..2080c44af 100644 --- a/pkg/multicluster/cluster_management.go +++ b/pkg/multicluster/cluster_management.go @@ -24,6 +24,7 @@ import ( "time" "github.com/briandowns/spinner" + "github.com/kubevela/pkg/util/k8s" prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1" clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1" clustercommon "github.com/oam-dev/cluster-gateway/pkg/common" @@ -63,6 +64,10 @@ type KubeClusterConfig struct { *clientcmdapi.Cluster *clientcmdapi.AuthInfo + // ClusterAlreadyExistCallback callback for handling cluster already exist, + // if no error returned, the logic will pass through + ClusterAlreadyExistCallback func(string) bool + // Logs records intermediate logs (which do not return error) during running Logs bytes.Buffer } @@ -115,7 +120,7 @@ func (clusterConfig *KubeClusterConfig) PostRegistration(ctx context.Context, cl return nil } -func (clusterConfig *KubeClusterConfig) createClusterSecret(ctx context.Context, cli client.Client, withEndpoint bool) error { +func (clusterConfig *KubeClusterConfig) createOrUpdateClusterSecret(ctx context.Context, cli client.Client, withEndpoint bool) error { var credentialType clusterv1alpha1.CredentialType data := map[string][]byte{} if withEndpoint { @@ -132,26 +137,40 @@ func (clusterConfig *KubeClusterConfig) createClusterSecret(ctx context.Context, data["tls.crt"] = clusterConfig.AuthInfo.ClientCertificateData data["tls.key"] = clusterConfig.AuthInfo.ClientKeyData } - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: clusterConfig.ClusterName, - Namespace: ClusterGatewaySecretNamespace, - Labels: map[string]string{ - clustercommon.LabelKeyClusterCredentialType: string(credentialType), - }, - }, - Type: corev1.SecretTypeOpaque, - Data: data, + secret := &corev1.Secret{} + if err := cli.Get(ctx, apitypes.NamespacedName{Name: clusterConfig.ClusterName, Namespace: ClusterGatewaySecretNamespace}, secret); client.IgnoreNotFound(err) != nil { + return err } - return cli.Create(ctx, secret) + secret.Name = clusterConfig.ClusterName + secret.Namespace = ClusterGatewaySecretNamespace + secret.Type = corev1.SecretTypeOpaque + _ = k8s.AddLabel(secret, clustercommon.LabelKeyClusterCredentialType, string(credentialType)) + secret.Data = data + if secret.ResourceVersion == "" { + return cli.Create(ctx, secret) + } + return cli.Update(ctx, secret) } // RegisterByVelaSecret create cluster secrets for KubeVela to use func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error { - if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil { - return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName) + cluster, err := prismclusterv1alpha1.NewClusterClient(cli).Get(ctx, clusterConfig.ClusterName) + if client.IgnoreNotFound(err) != nil { + return err } - if err := clusterConfig.createClusterSecret(ctx, cli, true); err != nil { + if cluster != nil { + if clusterConfig.ClusterAlreadyExistCallback == nil { + return fmt.Errorf("cluster %s already exists", cluster.Name) + } + if !clusterConfig.ClusterAlreadyExistCallback(clusterConfig.ClusterName) { + return nil + } + if cluster.Spec.CredentialType == prismclusterv1alpha1.CredentialTypeInternal || cluster.Spec.CredentialType == prismclusterv1alpha1.CredentialTypeOCMManagedCluster { + return fmt.Errorf("cannot override %s typed cluster", cluster.Spec.CredentialType) + } + } + + if err := clusterConfig.createOrUpdateClusterSecret(ctx, cli, true); err != nil { return errors.Wrapf(err, "failed to add cluster to kubernetes") } return clusterConfig.PostRegistration(ctx, cli) @@ -330,12 +349,13 @@ const ( // JoinClusterArgs args for join cluster type JoinClusterArgs struct { - engine string - createNamespace string - ioStreams cmdutil.IOStreams - hubConfig *rest.Config - inClusterBootstrap *bool - trackingSpinnerFactory func(string) *spinner.Spinner + engine string + createNamespace string + ioStreams cmdutil.IOStreams + hubConfig *rest.Config + inClusterBootstrap *bool + trackingSpinnerFactory func(string) *spinner.Spinner + clusterAlreadyExistCallback func(string) bool } func newJoinClusterArgs(options ...JoinClusterOption) *JoinClusterArgs { @@ -369,6 +389,14 @@ func (op JoinClusterEngineOption) ApplyToArgs(args *JoinClusterArgs) { args.engine = string(op) } +// JoinClusterAlreadyExistCallback configure the callback when cluster already exist +type JoinClusterAlreadyExistCallback func(string) bool + +// ApplyToArgs apply to args +func (op JoinClusterAlreadyExistCallback) ApplyToArgs(args *JoinClusterArgs) { + args.clusterAlreadyExistCallback = op +} + // JoinClusterOCMOptions options used when joining clusters by ocm, only support cli for now type JoinClusterOCMOptions struct { IoStreams cmdutil.IOStreams @@ -395,6 +423,7 @@ func JoinClusterByKubeConfig(ctx context.Context, cli client.Client, kubeconfigP if err := clusterConfig.SetClusterName(clusterName).SetCreateNamespace(args.createNamespace).Validate(); err != nil { return nil, err } + clusterConfig.ClusterAlreadyExistCallback = args.clusterAlreadyExistCallback switch args.engine { case ClusterGateWayEngine: if err = clusterConfig.RegisterByVelaSecret(ctx, cli); err != nil { diff --git a/references/cli/cluster.go b/references/cli/cluster.go index 9584909e6..9b80cf48c 100644 --- a/references/cli/cluster.go +++ b/references/cli/cluster.go @@ -201,7 +201,14 @@ func NewClusterJoinCommand(c *common.Args, ioStreams cmdutil.IOStreams) *cobra.C IoStreams: ioStreams, HubConfig: restConfig, TrackingSpinnerFactory: newTrackingSpinner, - }) + }, + multicluster.JoinClusterAlreadyExistCallback(func(name string) bool { + if !NewUserInput().AskBool(fmt.Sprintf("Cluster %s already exists, do you want to overwrite it?", name), &UserInputOptions{AssumeYes: assumeYes}) { + _, _ = fmt.Fprintf(cmd.OutOrStdout(), "Terminated.\n") + return false + } + return true + })) if err != nil { return err } diff --git a/test/e2e-multicluster-test/multicluster_test.go b/test/e2e-multicluster-test/multicluster_test.go index 7415a43e7..5f6a7defa 100644 --- a/test/e2e-multicluster-test/multicluster_test.go +++ b/test/e2e-multicluster-test/multicluster_test.go @@ -86,6 +86,8 @@ var _ = Describe("Test multicluster scenario", func() { Expect(err).Should(Succeed()) _, err = execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", oldClusterName) Expect(err).Should(Succeed()) + _, err = execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", oldClusterName, "-y") + Expect(err).Should(Succeed()) out, err := execCommand("cluster", "list") Expect(err).Should(Succeed()) Expect(out).Should(ContainSubstring(oldClusterName))