Skip to content

Commit

Permalink
multi-cluster bootstrap in antctl
Browse files Browse the repository at this point in the history
Add new subcommands to Create or Delete multi-cluster Resources.

Signed-off-by: hjiajing <hjiajing@vmware.com>
  • Loading branch information
hjiajing committed Mar 22, 2022
1 parent fc2e855 commit 3f0c050
Show file tree
Hide file tree
Showing 7 changed files with 819 additions and 0 deletions.
128 changes: 128 additions & 0 deletions pkg/antctl/raw/multicluster/add/member_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2022 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package add

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
k8sscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
mcsscheme "sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme"

multiclusterv1alpha1 "antrea.io/antrea/multicluster/apis/multicluster/v1alpha1"
antreamcscheme "antrea.io/antrea/multicluster/pkg/client/clientset/versioned/scheme"
"antrea.io/antrea/pkg/antctl/raw"
)

type memberClusterOptions struct {
namespace string
clusterSet string
serviceAccount string
}

var memberClusterOpt *memberClusterOptions

var memberClusterExamples = strings.Trim(`
Add a new member cluster to a ClusterSet
$ antctl mc add member-cluster <CLUSTER_ID> -n <NAMESPACE> --cluster-set <CLUSTER_SET> --service-account <SERVICE_ACCOUNT>
`, "\n")

func (o *memberClusterOptions) validateAndComplete() error {
if o.namespace == "" {
return fmt.Errorf("the Namespace cannot be empty")
}
if o.clusterSet == "" {
return fmt.Errorf("the cluster-set cannot be empty")
}
if o.serviceAccount == "" {
return fmt.Errorf("the service-account cannot be empty")
}

return nil
}

func NewMemberClusterCmd() *cobra.Command {
command := &cobra.Command{
Use: "member-cluster",
Args: cobra.MaximumNArgs(1),
Short: "Add a new member cluster to a ClusterSet",
Long: "Add a new member cluster to a ClusterSet",
Example: memberClusterExamples,
RunE: memberClusterRunE,
}

o := &memberClusterOptions{}
memberClusterOpt = o
command.Flags().StringVarP(&o.namespace, "namespace", "n", "", "Namespace of member cluster")
command.Flags().StringVarP(&o.clusterSet, "cluster-set", "", "", "ClusterSet to add a new member cluster")
command.Flags().StringVarP(&o.serviceAccount, "service-account", "", "", "ServiceAccount of the member cluster")

return command
}

func memberClusterRunE(cmd *cobra.Command, args []string) error {
if err := memberClusterOpt.validateAndComplete(); err != nil {
return err
}
if len(args) != 1 {
return fmt.Errorf("exactly one NAME is required, got %d", len(args))
}

kubeconfig, err := raw.ResolveKubeconfig(cmd)
if err != nil {
return err
}
restconfigTmpl := rest.CopyConfig(kubeconfig)
raw.SetupKubeconfig(restconfigTmpl)

scheme := k8sruntime.NewScheme()
if err = mcsscheme.AddToScheme(scheme); err != nil {
return err
}
if err = antreamcscheme.AddToScheme(scheme); err != nil {
return err
}
if err = k8sscheme.AddToScheme(scheme); err != nil {
return err
}
k8sClient, err := client.New(kubeconfig, client.Options{Scheme: scheme})
if err != nil {
return err
}

memberClusterID := args[0]
clusterSet := &multiclusterv1alpha1.ClusterSet{}
if err := k8sClient.Get(context.TODO(), types.NamespacedName{Name: memberClusterOpt.clusterSet, Namespace: memberClusterOpt.namespace}, clusterSet); err != nil {
return err
}
for _, member := range clusterSet.Spec.Members {
if member.ClusterID == memberClusterID {
return fmt.Errorf("the member cluster %s is already added to the ClusterSet %s", memberClusterID, memberClusterOpt.clusterSet)
}
}
clusterSet.Spec.Members = append(clusterSet.Spec.Members, multiclusterv1alpha1.MemberCluster{ClusterID: memberClusterID, ServiceAccount: memberClusterOpt.serviceAccount})
if err := k8sClient.Update(context.TODO(), clusterSet); err != nil {
return err
}

fmt.Fprintf(cmd.OutOrStdout(), "member cluster %s is added to ClusterSet %s successfully", memberClusterID, memberClusterOpt.clusterSet)
return nil
}
47 changes: 47 additions & 0 deletions pkg/antctl/raw/multicluster/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2022 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package multicluster

import (
"github.com/spf13/cobra"

"antrea.io/antrea/pkg/antctl/raw/multicluster/add"
"antrea.io/antrea/pkg/antctl/raw/multicluster/create"
deleteCmd "antrea.io/antrea/pkg/antctl/raw/multicluster/delete"
)

var CreateCmd = &cobra.Command{
Use: "create",
Short: "Create resources in a ClusterSet",
}

var AddCmd = &cobra.Command{
Use: "add",
Short: "Add new Member Cluster to a ClusterSet",
}

var DeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete Resources in a ClusterSet",
}

func init() {
CreateCmd.AddCommand(create.NewClusterClaimCmd())
CreateCmd.AddCommand(create.NewClusterSetCmd())
DeleteCmd.AddCommand(deleteCmd.NewMemberClusterCmd())
DeleteCmd.AddCommand(deleteCmd.NewClusterSetCmd())
DeleteCmd.AddCommand(deleteCmd.NewClusterClaimCmd())
AddCmd.AddCommand(add.NewMemberClusterCmd())
}
133 changes: 133 additions & 0 deletions pkg/antctl/raw/multicluster/create/clusterclaim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2022 Antrea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package create

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
k8sscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
mcsscheme "sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme"

multiclusterv1alpha1 "antrea.io/antrea/multicluster/apis/multicluster/v1alpha1"
antreamcscheme "antrea.io/antrea/multicluster/pkg/client/clientset/versioned/scheme"
"antrea.io/antrea/pkg/antctl/raw"
)

type clusterClaimOptions struct {
namespace string
clusterID string
clusterSet bool
}

var clusterClaimOpt *clusterClaimOptions

var clusterClaimExamples = strings.Trim(`
Create a ClusterClaim in a Kubernetes cluster
$ antctl mc create cluster-claim <CLUSTER_CLAIM_NAME> -n <NAMESPACE> --cluster-id <CLUSTER_ID>
`, "\n")

func (o *clusterClaimOptions) validateAndComplete() error {
if o.namespace == "" {
return fmt.Errorf("the Namespace cannot be empty")
}

return nil
}

func NewClusterClaimCmd() *cobra.Command {
command := &cobra.Command{
Use: "cluster-claim",
Args: cobra.MaximumNArgs(1),
Short: "Create a ClusterClaim in a Kubernetes cluster",
Long: "Create a ClusterClaim in a Kubernetes cluster",
Example: clusterClaimExamples,
RunE: clusterClaimRunE,
}

o := &clusterClaimOptions{}
clusterClaimOpt = o
command.Flags().StringVarP(&o.namespace, "namespace", "n", "", "Namespace of the ClusterClaim")
command.Flags().StringVarP(&o.clusterID, "cluster-id", "", "", "Cluster ID of the ClusterClaim. If not set, will use <NAME>-id")
command.Flags().BoolVarP(&o.clusterSet, "cluster-set", "", false, "If present, a ClusterClaim of ClusterSet will be created")

return command
}

func clusterClaimRunE(cmd *cobra.Command, args []string) error {
if err := clusterClaimOpt.validateAndComplete(); err != nil {
return err
}
if len(args) != 1 {
return fmt.Errorf("exactly one NAME is required, got %d", len(args))
}

kubeconfig, err := raw.ResolveKubeconfig(cmd)
if err != nil {
return err
}
restconfigTmpl := rest.CopyConfig(kubeconfig)
raw.SetupKubeconfig(restconfigTmpl)

scheme := k8sruntime.NewScheme()
if err = mcsscheme.AddToScheme(scheme); err != nil {
return err
}
if err = antreamcscheme.AddToScheme(scheme); err != nil {
return err
}
if err = k8sscheme.AddToScheme(scheme); err != nil {
return err
}
k8sClient, err := client.New(kubeconfig, client.Options{Scheme: scheme})
if err != nil {
return err
}

clusterClaimValue := args[0]
clusterID := clusterClaimOpt.clusterID
var name string
if clusterClaimOpt.clusterSet {
name = multiclusterv1alpha1.WellKnownClusterClaimClusterSet
} else {
name = multiclusterv1alpha1.WellKnownClusterClaimID
}
if clusterID == "" {
clusterID = clusterClaimValue + "-id"
fmt.Fprintf(cmd.OutOrStdout(), "the cluster ID is not set, use %s as default", clusterID)
}

clusterClaim := &multiclusterv1alpha1.ClusterClaim{
ObjectMeta: metav1.ObjectMeta{
Name: clusterID,
Namespace: clusterClaimOpt.namespace,
},
Value: clusterClaimValue,
Name: name,
}

if err := k8sClient.Create(context.TODO(), clusterClaim); err != nil {
return err
}
fmt.Fprintf(cmd.OutOrStdout(), "ClusterClaim %s created", clusterClaimValue)

return nil
}
Loading

0 comments on commit 3f0c050

Please sign in to comment.