Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Antctl for multicluster #3287

Merged
merged 1 commit into from Mar 22, 2022
Merged

Conversation

bangqipropel
Copy link
Contributor

@bangqipropel bangqipropel commented Feb 9, 2022

antctl multicluster command for clusterset & clustersets, resourceexport

For command lines for ClusterSets, we have

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>

For command lines for ResourceExport, we have

Gel 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>

@luolanzone
Copy link
Contributor

luolanzone commented Feb 9, 2022

@bangqipropel please checkout latest code from main and make sure you have correct sign-off info in your commits.

@bangqipropel
Copy link
Contributor Author

@bangqipropel please checkout latest code from main.

@luolanzone Doing it now, VM stuck suddenly, will push the new one after this

@codecov-commenter
Copy link

codecov-commenter commented Feb 9, 2022

Codecov Report

Merging #3287 (bae9741) into main (b04af1b) will decrease coverage by 10.53%.
The diff coverage is n/a.

Impacted file tree graph

@@             Coverage Diff             @@
##             main    #3287       +/-   ##
===========================================
- Coverage   65.57%   55.03%   -10.54%     
===========================================
  Files         268      374      +106     
  Lines       26780    41323    +14543     
===========================================
+ Hits        17560    22743     +5183     
- Misses       7315    16189     +8874     
- Partials     1905     2391      +486     
Flag Coverage Δ
integration-tests 35.86% <ø> (?)
kind-e2e-tests 55.65% <ø> (-0.15%) ⬇️
unit-tests 42.58% <ø> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
pkg/agent/controller/networkpolicy/reject.go 78.22% <0.00%> (-9.68%) ⬇️
pkg/apiserver/certificate/certificate.go 69.44% <0.00%> (-6.95%) ⬇️
pkg/controller/grouping/controller.go 64.65% <0.00%> (-2.59%) ⬇️
pkg/agent/openflow/pipeline.go 78.13% <0.00%> (-1.33%) ⬇️
pkg/ovs/openflow/ofctrl_bridge.go 55.28% <0.00%> (-1.21%) ⬇️
pkg/agent/openflow/client.go 62.39% <0.00%> (-0.82%) ⬇️
pkg/agent/agent.go 53.11% <0.00%> (-0.54%) ⬇️
antrea/pkg/agent/proxy/types/types.go 10.52% <0.00%> (ø)
antrea/pkg/ovs/ovsconfig/ovs_client.go 55.72% <0.00%> (ø)
...rea/pkg/agent/cniserver/pod_configuration_linux.go 26.31% <0.00%> (ø)
... and 107 more

@bangqipropel bangqipropel force-pushed the antctl_for_multicluster branch 2 times, most recently from 10378d7 to 6d79091 Compare February 9, 2022 05:40
pkg/antctl/raw/resourceexport/command.go Outdated Show resolved Hide resolved
"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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please reorganize the import section, all Antrea package should be placed in last section, you can refer to other files. the CI build also failed due to this. https://github.com/antrea-io/antrea/runs/5120537819?check_suite_focus=true, you can run make golangci-lint to double check your fix.

}
},
}
Command.Flags().StringVarP(&option.namespace, "namespace", "n", "", "source of the Traceflow: Namespace/Pod, Pod, or IP")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment of this option is incorrect.

formatter := "%-50s%-50s%-50s\n"
output.Write([]byte(fmt.Sprintf(formatter, "NAME", "NAMESPACE", "ClusterID")))
for _, r := range resExports {
fmt.Fprintf(&output, formatter, r.Name, r.Namespace, r.Labels["sourceClusterID"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to place Namespace before Name like kubectl do. is there is way to guarantee the order of output? I mean it's better to put ClusterID in first column and list all ResourceExport together from one cluster.

func output(clusterSets []multiclusterv1alpha1.ClusterSet) string {
var output strings.Builder
formatter := "%-15s%-15s%-18s%-10s%-30s%-50s%-10s\n"
output.Write([]byte(fmt.Sprintf(formatter, "ClusterID", "NAMESPACE", "Type", "Status", "LastTransitionTime", "Message", "Reason")))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make sure all column name are upper case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried locally, the format doesn't look good:

ClusterID      NAMESPACE      Type              Status    LastTransitionTime            Message                                           Reason
test-cluster-westdocs           Ready             True      2022-02-07 18:05:49 +0800 CST                                                   Connected
test-cluster-westdocs           ImportsResources  True      2022-02-07 18:05:49 +0800 CST Local cluster is the elected leader of member: test-cluster-westElectedLeader
test-cluster-northdocs           Ready             False     2022-02-09 12:36:37 +0800 CST No MemberClusterAnnounce update after 2022-02-09 04:36:18.960999439 +0000 UTC m=+153058.719069367Disconnected
test-cluster-northdocs           ImportsResources  False     2022-02-09 12:36:37 +0800 CST No MemberClusterAnnounce update after 2022-02-09 04:36:18.960999439 +0000 UTC m=+153058.719069367Disconnected
test-cluster-eastdocs           Ready             False     2022-02-09 12:36:37 +0800 CST No MemberClusterAnnounce update after 2022-02-09 04:36:18.960981033 +0000 UTC m=+153058.719050945Disconnected
test-cluster-eastdocs           ImportsResources  False     2022-02-09 12:36:37 +0800 CST No MemberClusterAnnounce update after 2022-02-09 04:36:18.960981033 +0000 UTC m=+153058.719050945Disconnected
test-cluster-northkube-system    Ready             True      2022-01-21 15:02:02 +0800 CST
test-cluster-northkube-system    IsElectedLeader   True      2022-01-21 15:02:04 +0800 CST This leader cluster is an elected leader for local cluster

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bangqipropel I think you can remove message column.

@bangqipropel bangqipropel force-pushed the antctl_for_multicluster branch 4 times, most recently from 8f7f77b to 3035a9b Compare February 9, 2022 23:29
Command = &cobra.Command{
Use: "clusterset",
Aliases: []string{"clustersets"},
Short: "Print multicluster clustersets",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/multicluster/Multi-cluster

err = runE(cmd, "")
}
if err != nil {
fmt.Println(fmt.Errorf("please input the correct numbers of argumrnts here"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why you need fmt.Errorf here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/argumrnts/arguments/
s/please/Please/
Please fix all same issue in other file.

}
},
}
Command.Flags().StringVarP(&option.namespace, "namespace", "n", "", "namespace of the clusterset")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/namespace of/Namespace of/

Comment on lines 81 to 93
scheme := runtime.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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can wrap this as a method considering it's duplicated in each file.

err = k8sClient.List(context.Background(), clusterSetList, &client.ListOptions{Namespace: option.namespace})
}
if err != nil {
return fmt.Errorf("failed to list Clustersets: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Clustersets/ClusterSets/

output.Write([]byte(fmt.Sprintf(formatter, "CLUSTERID", "NAMESPACE", "TYPE", "STATUS", "LASTTRANSACTIONTIME", "MESSAGE", "REASON")))
for _, clusterSet := range clusterSets {
for _, r := range clusterSet.Status.ClusterStatuses {
for _, condition := range r.Conditions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the basic information in ClusterSet spec?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a column named 'ClUSTERSETID', otherwise the output only shows the overall status and user is unable to tell which ClusterSet it belongs to:

CLUSTERID                NAMESPACE                TYPE                        STATUS              REASON
test-cluster-east        docs                     Ready                       Unknown             NeverConnected
test-cluster-east        docs                     ImportsResources            False               NeverConnected
test-cluster-west        docs                     Ready                       True                Connected
test-cluster-west        docs                     ImportsResources            True                ElectedLeader
test-cluster-north       docs                     Ready                       Unknown             NeverConnected
test-cluster-north       docs                     ImportsResources            False               NeverConnected

@aravindakidambi any suggestion here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do some thing like this:
(1) Command to show summary of the ClusterSet
(2) Command to show details of a ClusterSet, customer needs to provide a cluster-set-id to see the details
The above command seems more like (2) for me

For (1), we should just have these columns

CLUSTERSETID         TOTAL_CLUSTERS  NUM_READY_CLUSTERS OVERALL_CONDITION REASON
test-clusterset-1    4               3                  Ready=True           
test-cluster-2       2               0                  Ready=Unknown     NoReadyCluster

"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix import issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

err = runE(cmd, "")
}
if err != nil {
fmt.Println(fmt.Errorf("please input the correct numbers of argumrnts here"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Command = &cobra.Command{
Use: "resourceexport",
Aliases: []string{"resourceexports"},
Short: "Print multicluster resourceexports",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

formatter := "%-50s%-50s%-50s\n"
output.Write([]byte(fmt.Sprintf(formatter, "NAME", "NAMESPACE", "CLUSTERID")))
for _, r := range resExports {
fmt.Fprintf(&output, formatter, r.Namespace, r.Name, r.Labels["sourceClusterID"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discussed offline, it's better to move ClusterID as first column.

@bangqipropel
Copy link
Contributor Author

#3287 (comment) @luolanzone May I know what this means? We want the information from clusterset.spec? I thought we have already can see if it is leader or member from condition.Type?

Comment on lines 24 to 30
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you still have the import issue, please merge these two sections as below:

	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/rest"
	"sigs.k8s.io/controller-runtime/pkg/client"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

},
}
Command.Flags().StringVarP(&option.namespace, "namespace", "n", "", "Namespace of the clusterset")
Command.Flags().StringVarP(&option.outputFormat, "output", "o", "", "Output format. Supported formats: yaml")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to following code, json is also supported. this should be 'json|yaml'

}
},
}
Command.Flags().StringVarP(&option.namespace, "namespace", "n", "", "Namespace of the clusterset")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/clusterset/ClusterSet/

Command = &cobra.Command{
Use: "clusterset",
Aliases: []string{"clustersets"},
Short: "Print Multi-cluster clustersets",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/clustersets/ClusterSets/
all CRDs name should be started with upper case: eg: resourceexports -> ResourceExports, please update corresponding name in all other files.

Comment on lines 101 to 153
if option.outputFormat == "" {
fmt.Println(output(clusterSets))
} else if option.outputFormat == "json" {
bytesJSON, _ := json.Marshal(clusterSets)
var prettyJSON bytes.Buffer
err = json.Indent(&prettyJSON, bytesJSON, "", " ")
if err != nil {
return err
}
fmt.Println(prettyJSON.String())
} else if option.outputFormat == "yaml" {
yamlOutput, _ := yaml.Marshal(clusterSets)
fmt.Println(string(yamlOutput))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to use 'switch' to organize the code.

output.Write([]byte(fmt.Sprintf(formatter, "CLUSTERID", "NAMESPACE", "TYPE", "STATUS", "LASTTRANSACTIONTIME", "MESSAGE", "REASON")))
for _, clusterSet := range clusterSets {
for _, r := range clusterSet.Status.ClusterStatuses {
for _, condition := range r.Conditions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a column named 'ClUSTERSETID', otherwise the output only shows the overall status and user is unable to tell which ClusterSet it belongs to:

CLUSTERID                NAMESPACE                TYPE                        STATUS              REASON
test-cluster-east        docs                     Ready                       Unknown             NeverConnected
test-cluster-east        docs                     ImportsResources            False               NeverConnected
test-cluster-west        docs                     Ready                       True                Connected
test-cluster-west        docs                     ImportsResources            True                ElectedLeader
test-cluster-north       docs                     Ready                       Unknown             NeverConnected
test-cluster-north       docs                     ImportsResources            False               NeverConnected

@aravindakidambi any suggestion here?

"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

func output(resExports []multiclusterv1alpha1.ResourceExport) string {
var output strings.Builder
formatter := "%-50s%-50s%-50s\n"
output.Write([]byte(fmt.Sprintf(formatter, "CLUSTERID", "NAME", "NAMESPACE")))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the sequence is wrong, it's inconsistent with output, it should be "NAMESPACE", "NAME".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to support -o for all commands.

@bangqipropel
Copy link
Contributor Author

bangqipropel commented Feb 15, 2022

#3287 (comment) I think for this table output itself is good, but do we still need the yaml/json output for the non-specify Cluster-id command? It seems too big difference between the table output and the json/yaml output for the non-specify Cluster-ID command. @luolanzone @aravindakidambi

Comment on lines 540 to 541
supportAgent: true,
supportController: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we only support to run it out-of-cluster, right? if so, both of them should be false.

} else {
_ = runE(cmd, "")
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is better to put clusterID argument in options struct and use flags to get it .

func output(resImports []k8smcsv1alpha1.ServiceImport) string {
var output strings.Builder
formatter := "%-50s%-50s%-50s\n"
output.Write([]byte(fmt.Sprintf(formatter, "NAMESPACE", "NAME", "ClusterID")))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLUSTERID maybe better.

@bangqipropel bangqipropel force-pushed the antctl_for_multicluster branch 2 times, most recently from 8d268ea to c933fcc Compare February 23, 2022 00:46
@lgtm-com
Copy link

lgtm-com bot commented Feb 23, 2022

This pull request introduces 1 alert when merging c933fcc into 3f30d84 - view on LGTM.com

new alerts:

  • 1 for Useless assignment to local variable

@luolanzone
Copy link
Contributor

Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 0 {
_ = runE(cmd, args[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to handle returned error, otherwise, it will do nothing when there is an error returned by runE and user will be unable to know what's going on. please fix it in all new added commands

Comment on lines 64 to 65
var seviceExportMap map[string][]k8smcsv1alpha1.ServiceExport
seviceExportMap = make(map[string][]k8smcsv1alpha1.ServiceExport)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seviceExportMap := make(map[string][]k8smcsv1alpha1.ServiceExport)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/seviceExportMap/serviceExportMap/
please fix all for this typo.


clusterClaimList := &multiclusterv1alpha1.ClusterClaimList{}

err = k8sClient.List(context.Background(), clusterClaimList, &client.ListOptions{Namespace: metav1.NamespaceAll})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err is not handled.


err = k8sClient.List(context.Background(), clusterClaimList, &client.ListOptions{Namespace: metav1.NamespaceAll})

clusterID := clusterClaimList.Items[0].Value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better to check the length of clusterClaimList first, there might be no ClusterClaim at all or two ClusterClaims, one is for ClusterSet, another is for local cluster, please check the guide doc https://github.com/antrea-io/antrea/blob/main/docs/multicluster/getting-started.md.
better to wrap the logic into a function to do following check:

  1. when there is only no ClusterClaim, skip the cluster.
  2. when there are two clusterclaims, make sure you get the value from the one with name id.k8s.io, otherwise, you are getting ClusterSet ID.
  3. when there are more than two ClusterClaims, skip it.

}

results := res.Items
if ClusterID != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's quite confusing to use two variables with similar names. you can change the parameter var as 'targetClusterID'.

results := res.Items
if ClusterID != "" {
if clusterID == ClusterID {
resExports := results
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your purpose to add a new var? I suppose you can use results directly.
btw, you are handling ServiceExport,not ResourceExport, you can name it as svcExports if needed.

Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 0 {
_ = runE(cmd, args[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

res := &k8smcsv1alpha1.ServiceImportList{}

err = k8sClient.List(context.TODO(), res, &client.ListOptions{Namespace: metav1.NamespaceAll})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove the blank line here.

@bangqipropel bangqipropel force-pushed the antctl_for_multicluster branch 2 times, most recently from 278bd14 to ad0e219 Compare February 28, 2022 05:55
@bangqipropel
Copy link
Contributor Author

@luolanzone @hjiajing @aravindakidambi this can be reivewed, thanks!

@@ -26,6 +26,7 @@ import (
"k8s.io/component-base/logs"

"antrea.io/antrea/pkg/antctl"
_ "antrea.io/antrea/pkg/antctl/raw/multicluster/get"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of leveraging the obscure side effect of importing and adding the package of one subcommand here while other subcommands are initialized in another fashion, it would be better to make the multicluster.GetCmd initialized in its own package and the initialization should be done automatically when importing the command. Otherwise it would lead to an unmanageable maintenance effort to its consumer: "If you want to use A, you should also import B, C, etc.

I would suggest to move init function to pkg/antctl/raw/multicluster/commands.go:

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

func init() {
	GetCmd.AddCommand(get.NewClusterSetCommand())
	GetCmd.AddCommand(get.NewResourceExportCommand())
	GetCmd.AddCommand(get.NewResourceImportCommand())
}

Copy link
Member

@tnqn tnqn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please summarize the added commands and their examples in the PR description?


func AddSchemeToClientCfg(kubeconfig *rest.Config) (client.Client, error) {
var err error
scheme := k8sruntime.NewScheme()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more common to declare a global schema and initialize it in init function, then all consumers can share the schema.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tnqn editing on other comments, but for this one, I know the meaning, but init() cannot throw out an error, at first we wanted to throw out error on this, so do we think it is not that important to throw out an error?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initializing a schema is a static routine which has no external dependency, it either throws error because of a programming error which should be identified during development or never throw error. In many code it's wrapped with utilruntime.Must() to ensure it never fails.

utilruntime.Must(AddToScheme(scheme))


if len(clusterSets) == 0 {
if optionsClusterSet.namespace != "" {
fmt.Printf("No resource found in Namespace %s\n", optionsClusterSet.namespace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this information normally goes to stderr to make user able to distinguish it from valid output easily.

fmt.Fprintln(cmd.ErrOrStderr(), "No resources found")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mean for the allNamespace? if yes, changed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean both cases, the point is outputing non-data message to stderr, not mixed to stdout.

default:
clusterSetsNum := len(clusterSets)
for i, singleclusterset := range clusterSets {
err := output(singleclusterset, true, optionsResourceExport.outputFormat, clusterset.Transform)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the above two conditions duplicated with what output has done?

Get all ClusterSets and print them in JSON format
$ antctl mc get clusterset -o json
Get the specified ClusterSet
$ antctl mc get clusterset <clusterSetID>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use uppercase for all placeholders consistently

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed

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, "clusterid", "", "", "List of the ResourceExport of specific clusterID")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cmdResourceExport.Flags().StringVarP(&o.clusterID, "clusterid", "", "", "List of the ResourceExport of specific clusterID")
cmdResourceExport.Flags().StringVarP(&o.clusterID, "cluster-id", "", "", "List of the ResourceExport of specific clusterID")

To follow same convention that multi words are connected with "-" to make it easier to remember the writing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed


if len(resExports) == 0 {
if optionsResourceExport.namespace != "" {
fmt.Printf("No resource found in Namespace %s\n", optionsResourceExport.namespace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

)

type Response struct {
ClusterID string `json:"clusterid" yaml:"clusterid"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the convention is using lowerCamelCase for field names in json and yaml output

return result, nil
}

func objectTransform(o interface{}, r interface{}, c interface{}) (interface{}, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it uses interface{} as the types and do another conversion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

var _ common.TableOutput = new(Response)

func (r Response) GetTableHeader() []string {
return []string{"CLUSTERID", "NAMESPACE", "CLUSTERSETID", "TYPE", "STATUS", "REASON"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention is "CLUSTER-ID", "CLUSTER-SET-ID"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

)

type Response struct {
ClusterID string `json:"clusterid" yaml:"clusterid"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@luolanzone luolanzone added this to the Antrea v1.6 release milestone Mar 16, 2022
@bangqipropel bangqipropel force-pushed the antctl_for_multicluster branch 8 times, most recently from ed591c2 to ab786bc Compare March 18, 2022 05:55
@bangqipropel
Copy link
Contributor Author

@tnqn can this be reviewed again? thanks!

Copy link
Member

@tnqn tnqn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, some nits

@@ -146,3 +148,12 @@ func CreateControllerClientCfg(k8sClientset kubernetes.Interface, antreaClientse
cfg.Host = fmt.Sprintf("https://%s", net.JoinHostPort(nodeIP, fmt.Sprint(controllerInfo.APIPort)))
return cfg, nil
}

func AddSchemeToClientCfg(kubeconfig *rest.Config) (client.Client, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this is function is not very necessary now as it's actually oneline function after reducing the redundance, and AddSchemeToClientCfg sounds it will only mutate the client config but it actually creates a client:

func AddSchemeToClientCfg(kubeconfig *rest.Config) (client.Client, error) {
        return client.New(kubeconfig, client.Options{Scheme: multiclusterscheme.Scheme})
}

Maybe we could just call client.New(kubeconfig, client.Options{Scheme: multiclusterscheme.Scheme}) in its caller?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it done?

return nil
}

switch optionsClusterSet.outputFormat {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it could reuse what output function has done and move some logic to clusterset.Transform so this file doesn't have format related code, e.g.

return output(clusterSets, singleResource, optionsResourceExport.outputFormat, resourceexport.Transform)
func Transform(r interface{}, single bool) (interface{}, error) {
	if single {
		return objectTransform(r)
	}
	return listTransform(r)
}

func listTransform(l interface{}) (interface{}, error) {
	clusterSets := l.([]multiclusterv1alpha1.ClusterSet)
        ...
}

func objectTransform(o interface{}) (interface{}, error) {
	clusterSet := o.(multiclusterv1alpha1.ClusterSet)
        ...
}

func clusterTransform(clusterSet multiclusterv1alpha1.ClusterSet,
        status multiclusterv1alpha1.ClusterStatus,
	condition multiclusterv1alpha1.ClusterCondition) (interface{}, error) {
        ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tnqn Hi Quan, I tried some ways, but the blockers for me is here: we don't want to change the function TableOutputForGetCommands, but we want to output the table output result one by one, so the "tableHeader" will always output once, but Jianjun wanted to output them multiple times here: #3287 (comment)
So the only left way seems to be also ouput the yaml and json format one-by-one, but this seems a little weird for me, because if we do this, the json and yaml output for clusterset will be totally different to other kind of resources,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

var optionsResourceExport *resourceExportOptions

var resourceExportExamples = strings.Trim(`
Gel all ResourceExports of ClusterSet in default Namesapce
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Gel/Get/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

func (r Response) GetTableRow(maxColumnLength int) []string {
return []string{r.ClusterID, r.Namespace, r.Name}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it include kind? otherwise it would not straightforward to know what's this resource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

tnqn
tnqn previously approved these changes Mar 21, 2022
Copy link
Member

@tnqn tnqn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM except one nit, and PR needs rebasing

@@ -146,3 +148,12 @@ func CreateControllerClientCfg(k8sClientset kubernetes.Interface, antreaClientse
cfg.Host = fmt.Sprintf("https://%s", net.JoinHostPort(nodeIP, fmt.Sprint(controllerInfo.APIPort)))
return cfg, nil
}

func AddSchemeToClientCfg(kubeconfig *rest.Config) (client.Client, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it done?

return nil
}

switch optionsClusterSet.outputFormat {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it.

@tnqn
Copy link
Member

tnqn commented Mar 22, 2022

@bangqipropel could you remove the first commit that has been merged via #3341 from this one and make the commit message more specific like #3341 (comment).

antctl: add multicluster subcommands for clusterset and resourceexport

Example:

# antctl mc get clusterset -A
...

# antctl mc get resourceexport -A
...

@tnqn
Copy link
Member

tnqn commented Mar 22, 2022

@bangqipropel

  1. The updated commit message title is exactly same as 9879f4c, which will look confusing from git log (someone would think it's a duplicate commit). I suggested one title in Antctl for multicluster #3287 (comment), feel free to use it or change to one that makes more sense to you, but please be more specific as this is already the second commit that adds multicluster subcommand.
  2. Please add the command that gets the example output as well.
  3. Please add an example for resourceexport.

Add subcommands to get two more antrea multicluster resources, ClusterSet and ResourceExport. We could list or get mc resource by antctl mc get <Resource>.

Example:

➜  antctl mc get resourceexport -A
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

➜  antctl mc get clusterset -A
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 resources like serviceimport or serviceexport could be added to the sub-command if necessary.

Signed-off-by: zbangqi <zbangqi@vmware.com>
@tnqn
Copy link
Member

tnqn commented Mar 22, 2022

/skip-all
/test-multicluster-e2e

@tnqn
Copy link
Member

tnqn commented Mar 22, 2022

I will edit the commit message when merging it, please format the body (wrap the body at 72 characters) in the future. For more details, check this guide and links it refer to: https://github.com/antrea-io/antrea/blob/main/CONTRIBUTING.md#getting-reviewers

@bangqipropel
Copy link
Contributor Author

@tnqn Thanks, I am checking this file now

@tnqn tnqn merged commit 7764255 into antrea-io:main Mar 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/multi-cluster Issues or PRs related to multi cluster.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants