Skip to content

Commit

Permalink
Add [antctl mc] command
Browse files Browse the repository at this point in the history
Signed-off-by: hjiajing <hjiajing@vmware.com>
  • Loading branch information
hjiajing committed Mar 1, 2022
1 parent e23cf3b commit bb1b385
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 1 deletion.
7 changes: 7 additions & 0 deletions pkg/antctl/antctl.go
Expand Up @@ -24,6 +24,7 @@ import (
"antrea.io/antrea/pkg/agent/openflow"
fallbackversion "antrea.io/antrea/pkg/antctl/fallback/version"
"antrea.io/antrea/pkg/antctl/raw/featuregates"
"antrea.io/antrea/pkg/antctl/raw/multicluster"
"antrea.io/antrea/pkg/antctl/raw/proxy"
"antrea.io/antrea/pkg/antctl/raw/supportbundle"
"antrea.io/antrea/pkg/antctl/raw/traceflow"
Expand Down Expand Up @@ -531,6 +532,12 @@ var CommandList = &commandList{
supportController: true,
commandGroup: get,
},
{
cobraCommand: multicluster.GetCmd,
supportAgent: false,
supportController: false,
commandGroup: mc,
},
},
codec: scheme.Codecs,
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/antctl/command_definition.go
Expand Up @@ -76,6 +76,7 @@ const (
flat commandGroup = iota
get
query
mc
)

var groupCommands = map[commandGroup]*cobra.Command{
Expand All @@ -89,6 +90,11 @@ var groupCommands = map[commandGroup]*cobra.Command{
Short: "Execute a user-provided query",
Long: "Execute a user-provided query",
},
mc: {
Use: "mc",
Short: "Sub-commands of multi-cluster feature",
Long: "Sub-commands of multi-cluster feature",
},
}

type endpointResponder interface {
Expand Down
3 changes: 2 additions & 1 deletion pkg/antctl/command_list.go
Expand Up @@ -63,7 +63,8 @@ func (cl *commandList) applyToRootCommand(root *cobra.Command, client AntctlClie

for _, cmd := range cl.rawCommands {
if (runtime.Mode == runtime.ModeAgent && cmd.supportAgent) ||
(runtime.Mode == runtime.ModeController && cmd.supportController) {
(runtime.Mode == runtime.ModeController && cmd.supportController) ||
(!runtime.InPod && cmd.commandGroup == mc) {
if groupCommand, ok := groupCommands[cmd.commandGroup]; ok {
groupCommand.AddCommand(cmd.cobraCommand)
} else {
Expand Down
56 changes: 56 additions & 0 deletions pkg/antctl/raw/multicluster/command.go
@@ -0,0 +1,56 @@
// 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 (
"bytes"
"encoding/json"

"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)

const (
singleType = "single"
multipleType = "multiple"
)

var GetCmd = &cobra.Command{
Use: "get",
Short: "Display one or many resources in a ClusterSet",
}

func jsonOutput(obj interface{}) (bytes.Buffer, error) {
byteJson, err := json.Marshal(obj)
if err != nil {
return bytes.Buffer{}, err
}
var prettyJson bytes.Buffer
err = json.Indent(&prettyJson, byteJson, "", " ")
if err != nil {
return bytes.Buffer{}, err
}

return prettyJson, nil
}

func yamlOutput(obj interface{}) ([]byte, error) {
yamlByte, err := yaml.Marshal(obj)
if err != nil {
return nil, err
}

return yamlByte, nil
}
208 changes: 208 additions & 0 deletions pkg/antctl/raw/multicluster/resourceimport.go
@@ -0,0 +1,208 @@
// 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 (
"context"
"fmt"
"sort"
"strings"

"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"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"
)

var command *cobra.Command

type resourceImportOptions struct {
namespace string
outputFormat string
allNamespaces bool
}

var options *resourceImportOptions

var resourceImportExample = strings.Trim(`
Gel all resource imports of a cluster set in default Namespace
$ antctl mc get resourceimport
Get all resource imports of a cluster set in all Namespaces
$ antctl mc get resourceimport -A
Get all resource imports of specified namespace
$ antctl mc get resourceimport -n <NAMESPACE>
Get all resource imports and print json format
$ antctl mc get resourceimport -o json
Get a specified resourceimport
$ antctl mc get resourceimport <RESOURCEIMPORT> -n <NAMESPACE>
`, "\n")

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

func init() {
command = &cobra.Command{
Use: "resourceimport",
Aliases: []string{
"resourceimports",
"ri",
},
Short: "Print Multi-Cluster ResourceImports",
Args: cobra.MaximumNArgs(1),
Example: resourceImportExample,
RunE: runE,
}
o := &resourceImportOptions{}
options = o
command.Flags().StringVarP(&o.namespace, "namespace", "n", "", "Namespace of ResourceImports")
command.Flags().StringVarP(&o.outputFormat, "output", "o", "", "Output format. Supported formats: json|yaml")
command.Flags().BoolVarP(&o.allNamespaces, "all-namespaces", "A", false, "If present, list ResourceImports across all Namespaces")

GetCmd.AddCommand(command)
}

func runE(cmd *cobra.Command, args []string) error {
options.validateAndComplete()
argsNum := len(args)
if options.allNamespaces && argsNum > 0 {
return fmt.Errorf("a resource cannot be retrieved by name across all Namespaces")
}

kubeconfig, err := raw.ResolveKubeconfig(cmd)
if err != nil {
return err
}
kubeconfig.GroupVersion = &schema.GroupVersion{Group: "", Version: ""}
restconfigTmpl := rest.CopyConfig(kubeconfig)
raw.SetupKubeconfig(restconfigTmpl)

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

var outputType string
if argsNum > 0 {
outputType = singleType
} else {
outputType = multipleType
}

var res interface{}
switch outputType {
case singleType:
resourceImportName := args[0]
resourceImport := multiclusterv1alpha1.ResourceImport{}
err = k8sClient.Get(context.TODO(), types.NamespacedName{
Namespace: options.namespace,
Name: resourceImportName,
}, &resourceImport)
if err != nil {
if apierrors.IsNotFound(err) {
return fmt.Errorf("ResourceImport %s not found in Namespace %s", resourceImportName, options.namespace)
}

return err
}
res = resourceImport
case multipleType:
resourceImportList := &multiclusterv1alpha1.ResourceImportList{}
err = k8sClient.List(context.TODO(), resourceImportList, &client.ListOptions{Namespace: options.namespace})
if err != nil {
return err
}

if len(resourceImportList.Items) == 0 {
if options.namespace != "" {
fmt.Printf("No resource found in Namespace %s\n", options.namespace)
} else {
fmt.Println("No resource found in all Namespaces")
}
return nil
}
res = resourceImportList.Items
}

switch options.outputFormat {
case "json":
prettyJson, err := jsonOutput(res)
if err != nil {
return err
}
fmt.Println(prettyJson.String())
case "yaml":
yamlByte, err := yamlOutput(res)
if err != nil {
return err
}
fmt.Println(string(yamlByte))
default:
if outputType == singleType {
r := res.(multiclusterv1alpha1.ResourceImport)
fmt.Print(resourceImportOutput([]multiclusterv1alpha1.ResourceImport{r}))
} else {
r := res.([]multiclusterv1alpha1.ResourceImport)
fmt.Print(resourceImportOutput(r))
}
}

return nil
}

func resourceImportOutput(resourceImports []multiclusterv1alpha1.ResourceImport) string {
var output strings.Builder
formatter := "%-25s%-100s%-25s\n"
output.Write([]byte(fmt.Sprintf(formatter, "NAMESPACE", "NAME", "KIND")))
sort.SliceStable(resourceImports, func(i, j int) bool {
return strings.Compare(resourceImports[i].Namespace, resourceImports[j].Namespace) == 1
})

for _, ri := range resourceImports {
fmt.Fprintf(&output, formatter, ri.Namespace, ri.Name, ri.Spec.Kind)
}

return output.String()
}

0 comments on commit bb1b385

Please sign in to comment.