From 7a0d2b552bc5dc1ebde470f1ef0b3246e8e6db43 Mon Sep 17 00:00:00 2001 From: Somefive Date: Wed, 13 Apr 2022 13:58:35 +0800 Subject: [PATCH] Feat: support alias in cluster (#3630) * Feat: support alias in cluster Signed-off-by: Somefive * Fix: add test for cluster alias Signed-off-by: Somefive --- apis/types/multicluster.go | 10 +++++- pkg/multicluster/cluster_management.go | 13 +++++++ pkg/multicluster/virtual_cluster.go | 28 +++++++++++++++ references/cli/cluster.go | 34 ++++++++++++++++--- .../multicluster_test.go | 8 +++++ 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/apis/types/multicluster.go b/apis/types/multicluster.go index 417f8f2af..f76adb659 100644 --- a/apis/types/multicluster.go +++ b/apis/types/multicluster.go @@ -16,7 +16,10 @@ limitations under the License. package types -import "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1" +import ( + "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1" + "github.com/oam-dev/cluster-gateway/pkg/config" +) const ( // CredentialTypeInternal identifies the virtual cluster from internal kubevela system @@ -29,3 +32,8 @@ const ( // ClustersArg indicates the argument for specific clusters to install addon ClustersArg = "clusters" ) + +var ( + // AnnotationClusterAlias the annotation key for cluster alias + AnnotationClusterAlias = config.MetaApiGroupName + "/cluster-alias" +) diff --git a/pkg/multicluster/cluster_management.go b/pkg/multicluster/cluster_management.go index b27c9887d..d0513ac41 100644 --- a/pkg/multicluster/cluster_management.go +++ b/pkg/multicluster/cluster_management.go @@ -449,6 +449,19 @@ func RenameCluster(ctx context.Context, k8sClient client.Client, oldClusterName return nil } +// AliasCluster alias cluster +func AliasCluster(ctx context.Context, cli client.Client, clusterName string, aliasName string) error { + if clusterName == ClusterLocalName { + return ErrReservedLocalClusterName + } + vc, err := GetVirtualCluster(ctx, cli, clusterName) + if err != nil { + return err + } + setClusterAlias(vc.Object, aliasName) + return cli.Update(ctx, vc.Object) +} + // ensureClusterNotExists will check the cluster is not existed in control plane func ensureClusterNotExists(ctx context.Context, c client.Client, clusterName string) error { _, err := GetVirtualCluster(ctx, c, clusterName) diff --git a/pkg/multicluster/virtual_cluster.go b/pkg/multicluster/virtual_cluster.go index 9620e2353..747f73fb5 100644 --- a/pkg/multicluster/virtual_cluster.go +++ b/pkg/multicluster/virtual_cluster.go @@ -18,6 +18,7 @@ package multicluster import ( "context" + "fmt" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -38,6 +39,7 @@ import ( // like cluster secret or ocm managed cluster type VirtualCluster struct { Name string + Alias string Type v1alpha1.CredentialType EndPoint string Accepted bool @@ -46,6 +48,30 @@ type VirtualCluster struct { Object client.Object } +// FullName the name with alias if available +func (vc *VirtualCluster) FullName() string { + if vc.Alias != "" { + return fmt.Sprintf("%s (%s)", vc.Name, vc.Alias) + } + return vc.Name +} + +func getClusterAlias(o client.Object) string { + if annots := o.GetAnnotations(); annots != nil { + return annots[types.AnnotationClusterAlias] + } + return "" +} + +func setClusterAlias(o client.Object, alias string) { + annots := o.GetAnnotations() + if annots == nil { + annots = map[string]string{} + } + annots[types.AnnotationClusterAlias] = alias + o.SetAnnotations(annots) +} + // NewVirtualClusterFromLocal return virtual cluster corresponding to local cluster func NewVirtualClusterFromLocal() *VirtualCluster { return &VirtualCluster{ @@ -74,6 +100,7 @@ func NewVirtualClusterFromSecret(secret *corev1.Secret) (*VirtualCluster, error) } return &VirtualCluster{ Name: secret.Name, + Alias: getClusterAlias(secret), Type: v1alpha1.CredentialType(credType), EndPoint: endpoint, Accepted: true, @@ -90,6 +117,7 @@ func NewVirtualClusterFromManagedCluster(managedCluster *clusterv1.ManagedCluste } return &VirtualCluster{ Name: managedCluster.Name, + Alias: getClusterAlias(managedCluster), Type: types.CredentialTypeOCMManagedCluster, EndPoint: types.ClusterBlankEndpoint, Accepted: managedCluster.Spec.HubAcceptsClient, diff --git a/references/cli/cluster.go b/references/cli/cluster.go index c418b1e5d..4622fdb67 100644 --- a/references/cli/cluster.go +++ b/references/cli/cluster.go @@ -88,6 +88,7 @@ func ClusterCommandGroup(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Comm NewClusterDetachCommand(&c), NewClusterProbeCommand(&c), NewClusterLabelCommandGroup(&c), + NewClusterAliasCommand(&c), ) return cmd } @@ -101,7 +102,7 @@ func NewClusterListCommand(c *common.Args) *cobra.Command { Long: "list worker clusters managed by KubeVela.", Args: cobra.ExactValidArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - table := newUITable().AddRow("CLUSTER", "TYPE", "ENDPOINT", "ACCEPTED", "LABELS") + table := newUITable().AddRow("CLUSTER", "ALIAS", "TYPE", "ENDPOINT", "ACCEPTED", "LABELS") client, err := c.GetClient() if err != nil { return err @@ -123,9 +124,9 @@ func NewClusterListCommand(c *common.Args) *cobra.Command { } for i, l := range labels { if i == 0 { - table.AddRow(cluster.Name, cluster.Type, cluster.EndPoint, fmt.Sprintf("%v", cluster.Accepted), l) + table.AddRow(cluster.Name, cluster.Alias, cluster.Type, cluster.EndPoint, fmt.Sprintf("%v", cluster.Accepted), l) } else { - table.AddRow("", "", "", "", l) + table.AddRow("", "", "", "", "", l) } } } @@ -255,6 +256,29 @@ func NewClusterDetachCommand(c *common.Args) *cobra.Command { return cmd } +// NewClusterAliasCommand create an alias to the named cluster +func NewClusterAliasCommand(c *common.Args) *cobra.Command { + cmd := &cobra.Command{ + Use: "alias CLUSTER_NAME ALIAS", + Short: "alias a named cluster.", + Long: "alias a named cluster.", + Args: cobra.ExactValidArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clusterName, aliasName := args[0], args[1] + k8sClient, err := c.GetClient() + if err != nil { + return err + } + if err = multicluster.AliasCluster(context.Background(), k8sClient, clusterName, aliasName); err != nil { + return err + } + cmd.Printf("Alias cluster %s as %s.\n", clusterName, aliasName) + return nil + }, + } + return cmd +} + // NewClusterProbeCommand create command to help user try health probe for existing cluster // TODO(somefive): move prob logic into cluster management func NewClusterProbeCommand(c *common.Args) *cobra.Command { @@ -299,12 +323,12 @@ func NewClusterLabelCommandGroup(c *common.Args) *cobra.Command { func updateClusterLabelAndPrint(cmd *cobra.Command, cli client.Client, vc *multicluster.VirtualCluster, clusterName string) (err error) { if err = cli.Update(context.Background(), vc.Object); err != nil { - return errors.Errorf("failed to update labels for cluster %s (type: %s)", vc.Name, vc.Type) + return errors.Errorf("failed to update labels for cluster %s, type: %s", vc.FullName(), vc.Type) } if vc, err = multicluster.GetVirtualCluster(context.Background(), cli, clusterName); err != nil { return errors.Wrapf(err, "failed to get updated cluster %s", clusterName) } - cmd.Printf("Successfully update labels for cluster %s (type: %s).\n", vc.Name, vc.Type) + cmd.Printf("Successfully update labels for cluster %s, type: %s.\n", vc.FullName(), vc.Type) if len(vc.Labels) == 0 { cmd.Println("No valid label exists.") } diff --git a/test/e2e-multicluster-test/multicluster_test.go b/test/e2e-multicluster-test/multicluster_test.go index 014669705..b36b329b5 100644 --- a/test/e2e-multicluster-test/multicluster_test.go +++ b/test/e2e-multicluster-test/multicluster_test.go @@ -111,6 +111,14 @@ var _ = Describe("Test multicluster scenario", func() { Expect(out).ShouldNot(ContainSubstring("purpose")) }) + It("Test alias for cluster", func() { + _, err := execCommand("cluster", "alias", WorkerClusterName, "alias-worker") + Expect(err).Should(Succeed()) + out, err := execCommand("cluster", "list") + Expect(err).Should(Succeed()) + Expect(out).Should(ContainSubstring("alias-worker")) + }) + It("Test detach cluster with application use", func() { const testClusterName = "test-cluster" _, err := execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", testClusterName)