Skip to content

Commit

Permalink
Add [antctl mc] command
Browse files Browse the repository at this point in the history
Signed-off-by: hjiajing <[email protected]>
  • Loading branch information
hjiajing committed Mar 9, 2022
1 parent 24f85ce commit c16e529
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 1 deletion.
7 changes: 7 additions & 0 deletions pkg/antctl/antctl.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
81 changes: 81 additions & 0 deletions pkg/antctl/raw/multicluster/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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"
"fmt"

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

const (
resourceImportOutputFormatter = "%-25s%-100s%-25s\n"
)

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

type tableOutput interface {
print(resources interface{}, single bool)
}

func output(resources interface{}, single bool, outputFormat string, resourceType tableOutput) error {
switch outputFormat {
case "json":
prettyJson, err := jsonOutput(resources)
if err != nil {
return err
}
fmt.Println(prettyJson.String())
case "yaml":
yamlByte, err := yamlOutput(resources)
if err != nil {
return err
}
fmt.Print(string(yamlByte))
default:
resourceType.print(resources, single)
}

return nil
}

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
}
201 changes: 201 additions & 0 deletions pkg/antctl/raw/multicluster/resourceimport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// 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
}

type resourceImport struct{}

var resourceimport *resourceImport

var options *resourceImportOptions

var resourceImportExamples = 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 in the specified Namespace
$ antctl mc get resourceimport -n <NAMESPACE>
Get all resource imports and print them in JSON format
$ antctl mc get resourceimport -o json
Get the 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: resourceImportExamples,
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
}

singleResource := false
if argsNum > 0 {
singleResource = true
}
var res interface{}

if singleResource {
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
}

gvks, unversioned, err := k8sClient.Scheme().ObjectKinds(&resourceImport)
if err != nil {
return err
}
if !unversioned && len(gvks) == 1 {
resourceImport.SetGroupVersionKind(gvks[0])
}
res = resourceImport
} else {
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
}

return output(res, singleResource, options.outputFormat, resourceimport)
}

func (resource *resourceImport) print(resourceImports interface{}, single bool) {
var resourceImportOutput strings.Builder
riList := []multiclusterv1alpha1.ResourceImport{}
if single {
r := resourceImports.(multiclusterv1alpha1.ResourceImport)
riList = append(riList, r)
} else {
r := resourceImports.([]multiclusterv1alpha1.ResourceImport)
riList = append(riList, r...)
}
resourceImportOutput.Write([]byte(fmt.Sprintf(resourceImportOutputFormatter, "NAMESPACE", "NAME", "KIND")))
sort.SliceStable(resourceImports, func(i, j int) bool {
return strings.Compare(riList[i].Namespace, riList[j].Namespace) == 1
})

for _, ri := range riList {
fmt.Fprintf(&resourceImportOutput, resourceImportOutputFormatter, ri.Namespace, ri.Name, ri.Spec.Kind)
}

fmt.Print(resourceImportOutput.String())
}

0 comments on commit c16e529

Please sign in to comment.