Skip to content

Commit

Permalink
antctl: add multicluster subcommand
Browse files Browse the repository at this point in the history
Add a new subcommand for antrea multicluster and add get verb for query resources. We could list or get mc resource by antctl mc get <Resource>.

Example:

CLUSTER-ID NAMESPACE                 NAME                                   KIND
cluster-a  leader-ns                 cluster-a-testns-stale-busybus-service Service
cluster-a  leader-ns                 cluster-a-testns-stale-nginx-service   Service
cluster-a1 leader-nsforholdingalotof cluster-b-testns-stale-nginx-service   Service

CLUSTER-ID NAMESPACE CLUSTER-SET-ID  TYPE            STATUS REASON
cluster-a  leader-ns test-clusterset IsElectedLeader True   <NONE>
cluster-a  leader-ns test-clusterset Ready           True   <NONE>

CLUSTER-ID NAMESPACE                 CLUSTER-SET-ID   TYPE            STATUS REASON
cluster-a1 leader-nsforholdingalotof test-clusterset1 IsElectedLeader True   <NONE>
cluster-a1 leader-nsforholdingalotof test-clusterset1 Ready           True   <NONE>

In the future, other verb like create or delete also could be added to
the sub-command mc if it is nesscessary.

Signed-off-by: zbangqi <zbangqi@vmware.com>
  • Loading branch information
zbangqi committed Mar 22, 2022
1 parent b04af1b commit 6511a6a
Show file tree
Hide file tree
Showing 5 changed files with 468 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pkg/antctl/raw/multicluster/commands.go
Expand Up @@ -26,5 +26,7 @@ var GetCmd = &cobra.Command{
}

func init() {
GetCmd.AddCommand(get.NewClusterSetCommand())
GetCmd.AddCommand(get.NewResourceImportCommand())
GetCmd.AddCommand(get.NewResourceExportCommand())
}
162 changes: 162 additions & 0 deletions pkg/antctl/raw/multicluster/get/clusterset.go
@@ -0,0 +1,162 @@
// 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 get

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

multiclusterv1alpha1 "antrea.io/antrea/multicluster/apis/multicluster/v1alpha1"
"antrea.io/antrea/pkg/antctl/raw"
multiclusterscheme "antrea.io/antrea/pkg/antctl/raw/multicluster/scheme"
"antrea.io/antrea/pkg/antctl/transform/clusterset"
)

var cmdClusterSet *cobra.Command

type clusterSetOptions struct {
namespace string
outputFormat string
allNamespaces bool
}

var optionsClusterSet *clusterSetOptions

var clusterSetExamples = strings.Trim(`
Gel all ClusterSets in default Namesapce
$ antctl mc get clusterset
Get all ClusterSets in all Namespaces
$ antctl mc get clusterset -A
Get all ClusterSets in the specified Namespace
$ antctl mc get clusterset -n <NAMESPACE>
Get all ClusterSets and print them in JSON format
$ antctl mc get clusterset -o json
Get the specified ClusterSet
$ antctl mc get clusterset <CLUSTERSETID>
`, "\n")

func (o *clusterSetOptions) validateAndComplete() {
if o.allNamespaces {
o.namespace = metav1.NamespaceAll
return
}
if o.namespace == "" {
o.namespace = metav1.NamespaceDefault
return
}
}

func NewClusterSetCommand() *cobra.Command {
cmdClusterSet = &cobra.Command{
Use: "clusterset",
Aliases: []string{
"clustersets",
},
Short: "Print Multi-cluster ClusterSets",
Args: cobra.MaximumNArgs(1),
Example: clusterSetExamples,
RunE: runEClusterSet,
}
o := &clusterSetOptions{}
optionsClusterSet = o
cmdClusterSet.Flags().StringVarP(&o.namespace, "namespace", "n", "", "Namespace of ClusterSets")
cmdClusterSet.Flags().StringVarP(&o.outputFormat, "output", "o", "", "Output format. Supported formats: json|yaml")
cmdClusterSet.Flags().BoolVarP(&o.allNamespaces, "all-namespaces", "A", false, "If present, list ClusterSets across all namespaces")

return cmdClusterSet
}

func runEClusterSet(cmd *cobra.Command, args []string) error {
optionsClusterSet.validateAndComplete()

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

argsNum := len(args)
singleResource := false
if argsNum > 0 {
singleResource = true
}

k8sClient, err := client.New(kubeconfig, client.Options{Scheme: multiclusterscheme.Scheme})
if err != nil {
return err
}

var clusterSets []multiclusterv1alpha1.ClusterSet
if singleResource {
clusterSetName := args[0]
clusterSet := multiclusterv1alpha1.ClusterSet{}
err = k8sClient.Get(context.TODO(), types.NamespacedName{
Namespace: optionsClusterSet.namespace,
Name: clusterSetName,
}, &clusterSet)
if err != nil {
return err
}
gvks, unversioned, err := k8sClient.Scheme().ObjectKinds(&clusterSet)
if err != nil {
return err
}
if !unversioned && len(gvks) == 1 {
clusterSet.SetGroupVersionKind(gvks[0])
}
clusterSets = append(clusterSets, clusterSet)
} else {
clusterSetList := &multiclusterv1alpha1.ClusterSetList{}
err = k8sClient.List(context.TODO(), clusterSetList, &client.ListOptions{Namespace: optionsClusterSet.namespace})
if err != nil {
return err
}
clusterSets = clusterSetList.Items
}

if len(clusterSets) == 0 {
if optionsClusterSet.namespace != "" {
fmt.Fprintf(cmd.ErrOrStderr(), "No resource found in Namespace %s\n", optionsClusterSet.namespace)
} else {
fmt.Fprintln(cmd.ErrOrStderr(), "No resources found")
}
return nil
}

switch optionsClusterSet.outputFormat {
case "json", "yaml":
err := output(clusterSets, true, optionsClusterSet.outputFormat, clusterset.Transform)
if err != nil {
return err
}
default:
clusterSetsNum := len(clusterSets)
for i, singleclusterset := range clusterSets {
err := output(singleclusterset, true, optionsClusterSet.outputFormat, clusterset.Transform)
if err != nil {
return err
}
if i != clusterSetsNum-1 {
fmt.Print("\n")
}
}
}
return nil
}
156 changes: 156 additions & 0 deletions pkg/antctl/raw/multicluster/get/resourceexport.go
@@ -0,0 +1,156 @@
// 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 get

import (
"context"
"fmt"
"strings"

"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

multiclusterv1alpha1 "antrea.io/antrea/multicluster/apis/multicluster/v1alpha1"
"antrea.io/antrea/pkg/antctl/raw"
multiclusterscheme "antrea.io/antrea/pkg/antctl/raw/multicluster/scheme"
"antrea.io/antrea/pkg/antctl/transform/resourceexport"
)

var cmdResourceExport *cobra.Command

type resourceExportOptions struct {
namespace string
outputFormat string
allNamespaces bool
clusterID string
}

var optionsResourceExport *resourceExportOptions

var resourceExportExamples = strings.Trim(`
Get all ResourceExports of ClusterSet in default Namesapce
$ antctl mc get resourceexport
Get all ResourceExports of ClusterSet in all Namespaces
$ antctl mc get resourceexport -A
Get all ResourceExports in the specified Namespace
$ antctl mc get resourceexport -n <NAMESPACE>
Get all ResourceExports and print them in JSON format
$ antctl mc get resourceexport -o json
Get the specified ResourceExport
$ antctl mc get resourceexport <RESOURCEEXPORT> -n <NAMESPACE>
`, "\n")

func (o *resourceExportOptions) validateAndComplete() {
if o.allNamespaces {
o.namespace = metav1.NamespaceAll
return
}
if o.namespace == "" {
o.namespace = metav1.NamespaceDefault
return
}
}

func NewResourceExportCommand() *cobra.Command {
cmdResourceExport = &cobra.Command{
Use: "resourceexport",
Aliases: []string{
"resourceexports",
"re",
},
Short: "Print Multi-cluster ResourceExports",
Args: cobra.MaximumNArgs(1),
Example: resourceExportExamples,
RunE: runEResourceExport,
}
o := &resourceExportOptions{}
optionsResourceExport = o
cmdResourceExport.Flags().StringVarP(&o.namespace, "namespace", "n", "", "Namespace of ResourceExport")
cmdResourceExport.Flags().StringVarP(&o.outputFormat, "output", "o", "", "Output format. Supported formats: json|yaml")
cmdResourceExport.Flags().BoolVarP(&o.allNamespaces, "all-namespaces", "A", false, "If present, list ResourceExport across all namespaces")
cmdResourceExport.Flags().StringVarP(&o.clusterID, "cluster-id", "", "", "List of the ResourceExport of specific clusterID")

return cmdResourceExport
}

func runEResourceExport(cmd *cobra.Command, args []string) error {
optionsResourceExport.validateAndComplete()

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

argsNum := len(args)
singleResource := false
if argsNum > 0 {
singleResource = true
}
var resExports []multiclusterv1alpha1.ResourceExport
k8sClient, err := client.New(kubeconfig, client.Options{Scheme: multiclusterscheme.Scheme})
if err != nil {
return err
}

if singleResource {
resourceExportName := args[0]
resourceExport := multiclusterv1alpha1.ResourceExport{}
err = k8sClient.Get(context.TODO(), types.NamespacedName{
Namespace: optionsResourceExport.namespace,
Name: resourceExportName,
}, &resourceExport)
if err != nil {
return err
}
gvks, unversioned, err := k8sClient.Scheme().ObjectKinds(&resourceExport)
if err != nil {
return err
}
if !unversioned && len(gvks) == 1 {
resourceExport.SetGroupVersionKind(gvks[0])
}
resExports = append(resExports, resourceExport)
} else {
var labels map[string]string
if optionsResourceExport.clusterID != "" {
labels = map[string]string{"sourceClusterID": optionsResourceExport.clusterID}
}
selector := metav1.LabelSelector{MatchLabels: labels}
labelSelector, _ := metav1.LabelSelectorAsSelector(&selector)
resourceExportList := &multiclusterv1alpha1.ResourceExportList{}
err = k8sClient.List(context.TODO(), resourceExportList, &client.ListOptions{
Namespace: optionsResourceExport.namespace,
LabelSelector: labelSelector,
})
if err != nil {
return err
}
resExports = resourceExportList.Items
}

if len(resExports) == 0 {
if optionsResourceExport.namespace != "" {
fmt.Fprintf(cmd.ErrOrStderr(), "No resources found in Namespace %s\n", optionsResourceExport.namespace)
} else {
fmt.Fprintln(cmd.ErrOrStderr(), "No resources found")
}
return nil
}

return output(resExports, false, optionsResourceExport.outputFormat, resourceexport.Transform)

}

0 comments on commit 6511a6a

Please sign in to comment.