Merge pull request #2589 from helen-frank/feature/getFactory

karmadactl get uses factory to access cluster
This commit is contained in:
karmada-bot 2022-10-21 21:51:15 +08:00 committed by GitHub
commit dbb24d3b62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 97 deletions

View File

@ -32,12 +32,10 @@ import (
"k8s.io/kubectl/pkg/util/interrupt" "k8s.io/kubectl/pkg/util/interrupt"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
"github.com/karmada-io/karmada/pkg/util/gclient" "github.com/karmada-io/karmada/pkg/util/gclient"
"github.com/karmada-io/karmada/pkg/util/helper" "github.com/karmada-io/karmada/pkg/util/helper"
@ -61,31 +59,31 @@ var (
getExample = templates.Examples(` getExample = templates.Examples(`
# List all pods in ps output format # List all pods in ps output format
%[1]s get pods %[1]s get pods
# List all pods in ps output format with more information (such as node name) # List all pods in ps output format with more information (such as node name)
%[1]s get pods -o wide %[1]s get pods -o wide
# List all pods of member1 cluster in ps output format # List all pods of member1 cluster in ps output format
%[1]s get pods -C member1 %[1]s get pods -C member1
# List a single replicasets controller with specified NAME in ps output format # List a single replicasets controller with specified NAME in ps output format
%[1]s get replicasets nginx %[1]s get replicasets nginx
# List deployments in JSON output format, in the "v1" version of the "apps" API group # List deployments in JSON output format, in the "v1" version of the "apps" API group
%[1]s get deployments.v1.apps -o json %[1]s get deployments.v1.apps -o json
# Return only the phase value of the specified resource # Return only the phase value of the specified resource
%[1]s get -o template deployment/nginx -C member1 --template={{.spec.replicas}} %[1]s get -o template deployment/nginx -C member1 --template={{.spec.replicas}}
# List all replication controllers and services together in ps output format # List all replication controllers and services together in ps output format
%[1]s get rs,services %[1]s get rs,services
# List one or more resources by their type and names # List one or more resources by their type and names
%[1]s get rs/nginx-cb87b6d88 service/kubernetes`) %[1]s get rs/nginx-cb87b6d88 service/kubernetes`)
) )
// NewCmdGet New get command // NewCmdGet New get command
func NewCmdGet(karmadaConfig KarmadaConfig, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command { func NewCmdGet(f util.Factory, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command {
o := NewCommandGetOptions(streams) o := NewCommandGetOptions(streams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "get [NAME | -l label | -n namespace]", Use: "get [NAME | -l label | -n namespace]",
@ -94,13 +92,13 @@ func NewCmdGet(karmadaConfig KarmadaConfig, parentCommand string, streams generi
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Example: fmt.Sprintf(getExample, parentCommand), Example: fmt.Sprintf(getExample, parentCommand),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(karmadaConfig); err != nil { if err := o.Complete(f); err != nil {
return err return err
} }
if err := o.Validate(cmd); err != nil { if err := o.Validate(cmd); err != nil {
return err return err
} }
if err := o.Run(karmadaConfig, cmd, args); err != nil { if err := o.Run(f, cmd, args); err != nil {
return err return err
} }
return nil return nil
@ -110,26 +108,25 @@ func NewCmdGet(karmadaConfig KarmadaConfig, parentCommand string, streams generi
}, },
} }
o.GlobalCommandOptions.AddFlags(cmd.Flags())
o.PrintFlags.AddFlags(cmd) o.PrintFlags.AddFlags(cmd)
flags := cmd.Flags()
cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "-n=namespace or -n namespace") flags.StringVarP(&o.LabelSelector, "labels", "l", "", "-l=label or -l label")
cmd.Flags().StringVarP(&o.LabelSelector, "labels", "l", "", "-l=label or -l label") flags.StringSliceVarP(&o.Clusters, "clusters", "C", []string{}, "-C=member1,member2")
cmd.Flags().StringSliceVarP(&o.Clusters, "clusters", "C", []string{}, "-C=member1,member2") flags.BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") flags.BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.") flags.BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.") flags.BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.") flags.BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.")
cmd.Flags().BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.") flags.StringVar(defaultConfigFlags.KubeConfig, "kubeconfig", *defaultConfigFlags.KubeConfig, "Path to the kubeconfig file to use for CLI requests.")
flags.StringVar(defaultConfigFlags.Context, "karmada-context", *defaultConfigFlags.Context, "The name of the kubeconfig context to use")
flags.StringVarP(defaultConfigFlags.Namespace, "namespace", "n", *defaultConfigFlags.Namespace, "If present, the namespace scope for this CLI request")
return cmd return cmd
} }
// CommandGetOptions contains the input to the get command. // CommandGetOptions contains the input to the get command.
type CommandGetOptions struct { type CommandGetOptions struct {
// global flags
options.GlobalCommandOptions
Clusters []string Clusters []string
PrintFlags *get.PrintFlags PrintFlags *get.PrintFlags
@ -148,8 +145,8 @@ type CommandGetOptions struct {
LabelSelector string LabelSelector string
FieldSelector string FieldSelector string
AllNamespaces bool
Namespace string Namespace string
AllNamespaces bool
ExplicitNamespace bool ExplicitNamespace bool
ServerPrint bool ServerPrint bool
@ -173,16 +170,13 @@ func NewCommandGetOptions(streams genericclioptions.IOStreams) *CommandGetOption
} }
// Complete takes the command arguments and infers any remaining options. // Complete takes the command arguments and infers any remaining options.
func (g *CommandGetOptions) Complete(karmadaConfig KarmadaConfig) error { func (g *CommandGetOptions) Complete(f util.Factory) error {
newScheme := gclient.NewSchema() newScheme := gclient.NewSchema()
namespace, _, err := f.ToRawKubeConfigLoader().Namespace()
if g.Namespace == "" { if err != nil {
namespace, _, err := karmadaConfig.GetClientConfig(g.KarmadaContext, g.KubeConfig).Namespace() return err
if err != nil {
return err
}
g.Namespace = namespace
} }
g.Namespace = namespace
templateArg := "" templateArg := ""
if g.PrintFlags.TemplateFlags != nil && g.PrintFlags.TemplateFlags.TemplateArgument != nil { if g.PrintFlags.TemplateFlags != nil && g.PrintFlags.TemplateFlags.TemplateArgument != nil {
@ -265,7 +259,7 @@ type OtherPrint struct {
} }
// Run performs the get operation. // Run performs the get operation.
func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command, args []string) error { func (g *CommandGetOptions) Run(f util.Factory, cmd *cobra.Command, args []string) error {
mux := sync.Mutex{} mux := sync.Mutex{}
var wg sync.WaitGroup var wg sync.WaitGroup
@ -282,22 +276,35 @@ func (g *CommandGetOptions) Run(karmadaConfig KarmadaConfig, cmd *cobra.Command,
g.ServerPrint = false g.ServerPrint = false
} }
clusterInfos := make(map[string]*ClusterInfo)
RBInfo = make(map[string]*OtherPrint) RBInfo = make(map[string]*OtherPrint)
karmadaRestConfig, err := clusterInfoInit(g, karmadaConfig, clusterInfos) gclient, err := f.KarmadaClientSet()
if err != nil { if err != nil {
return err return err
} }
clusterInfos, err := getClusterInKarmadaForClient(gclient)
if err != nil {
return fmt.Errorf("method getClusterInKarmadaForClient get cluster info in karmada failed, err is: %w", err)
}
if err := g.getRBInKarmada(gclient); err != nil {
return err
}
if len(g.Clusters) <= 0 {
for c := range clusterInfos {
g.Clusters = append(g.Clusters, c)
}
}
wg.Add(len(g.Clusters)) wg.Add(len(g.Clusters))
for idx := range g.Clusters { for idx := range g.Clusters {
err = g.setClusterProxyInfo(karmadaRestConfig, g.Clusters[idx], clusterInfos) memberFactory, err := f.FactoryForMemberCluster(g.Clusters[idx])
if err != nil { if err != nil {
return err return err
} }
f := getFactory(g.Clusters[idx], clusterInfos, "") go g.getObjInfo(&wg, &mux, memberFactory, g.Clusters[idx], &objs, &watchObjs, &allErrs, args)
go g.getObjInfo(&wg, &mux, f, g.Clusters[idx], &objs, &watchObjs, &allErrs, args)
} }
wg.Wait() wg.Wait()
@ -405,7 +412,7 @@ func (g *CommandGetOptions) printObjs(objs []Obj, allErrs *[]error, args []strin
func (g *CommandGetOptions) printIfNotFindResource(written int, allErrs *[]error, allResourcesNamespaced bool) { func (g *CommandGetOptions) printIfNotFindResource(written int, allErrs *[]error, allResourcesNamespaced bool) {
if written == 0 && !g.IgnoreNotFound && len(*allErrs) == 0 { if written == 0 && !g.IgnoreNotFound && len(*allErrs) == 0 {
if allResourcesNamespaced { if allResourcesNamespaced {
fmt.Fprintf(g.ErrOut, "No resources found in %s namespace.\n", g.Namespace) fmt.Fprintf(g.ErrOut, "No resources found in %s namespace.\n", *defaultConfigFlags.Namespace)
} else { } else {
fmt.Fprintln(g.ErrOut, "No resources found") fmt.Fprintln(g.ErrOut, "No resources found")
} }
@ -422,7 +429,8 @@ func (g *CommandGetOptions) checkPrintWithNamespace(mapping *meta.RESTMapping) b
// getObjInfo get obj info in member cluster // getObjInfo get obj info in member cluster
func (g *CommandGetOptions) getObjInfo(wg *sync.WaitGroup, mux *sync.Mutex, f cmdutil.Factory, func (g *CommandGetOptions) getObjInfo(wg *sync.WaitGroup, mux *sync.Mutex, f cmdutil.Factory,
cluster string, objs *[]Obj, watchObjs *[]WatchObj, allErrs *[]error, args []string) { cluster string, objs *[]Obj, watchObjs *[]WatchObj, allErrs *[]error, args []string,
) {
defer wg.Done() defer wg.Done()
restClient, err := f.RESTClient() restClient, err := f.RESTClient()
@ -437,7 +445,6 @@ func (g *CommandGetOptions) getObjInfo(wg *sync.WaitGroup, mux *sync.Mutex, f cm
*allErrs = append(*allErrs, fmt.Errorf("cluster(%s) is inaccessible, please check authorization or network", cluster)) *allErrs = append(*allErrs, fmt.Errorf("cluster(%s) is inaccessible, please check authorization or network", cluster))
return return
} }
r := f.NewBuilder(). r := f.NewBuilder().
Unstructured(). Unstructured().
NamespaceParam(g.Namespace).DefaultNamespace().AllNamespaces(g.AllNamespaces). NamespaceParam(g.Namespace).DefaultNamespace().AllNamespaces(g.AllNamespaces).
@ -867,29 +874,6 @@ type ClusterInfo struct {
ClusterSyncMode clusterv1alpha1.ClusterSyncMode ClusterSyncMode clusterv1alpha1.ClusterSyncMode
} }
func clusterInfoInit(g *CommandGetOptions, karmadaConfig KarmadaConfig, clusterInfos map[string]*ClusterInfo) (*rest.Config, error) {
karmadaRestConfig, err := karmadaConfig.GetRestConfig(g.KarmadaContext, g.KubeConfig)
if err != nil {
return nil, fmt.Errorf("failed to get control plane rest config. context: %s, kube-config: %s, error: %v",
g.KarmadaContext, g.KubeConfig, err)
}
if err := getClusterInKarmada(karmadaRestConfig, clusterInfos); err != nil {
return nil, fmt.Errorf("method getClusterInKarmada get cluster info in karmada failed, err is: %w", err)
}
if err := g.getRBInKarmada(g.Namespace, karmadaRestConfig); err != nil {
return nil, err
}
if len(g.Clusters) <= 0 {
for c := range clusterInfos {
g.Clusters = append(g.Clusters, c)
}
}
return karmadaRestConfig, nil
}
func getFactory(clusterName string, clusterInfos map[string]*ClusterInfo, namespace string) cmdutil.Factory { func getFactory(clusterName string, clusterInfos map[string]*ClusterInfo, namespace string) cmdutil.Factory {
kubeConfigFlags := NewConfigFlags(true).WithDeprecatedPasswordFlag() kubeConfigFlags := NewConfigFlags(true).WithDeprecatedPasswordFlag()
// Build member cluster kubeConfigFlags // Build member cluster kubeConfigFlags
@ -920,27 +904,21 @@ func (g *CommandGetOptions) transformRequests(req *rest.Request) {
}, ",")) }, ","))
} }
func (g *CommandGetOptions) getRBInKarmada(namespace string, config *rest.Config) error { func (g *CommandGetOptions) getRBInKarmada(gclient karmadaclientset.Interface) error {
rbList := &workv1alpha2.ResourceBindingList{} var rbList *workv1alpha2.ResourceBindingList
crbList := &workv1alpha2.ClusterResourceBindingList{} var crbList *workv1alpha2.ClusterResourceBindingList
var err error
gClient, err := gclient.NewForConfig(config)
if err != nil {
return err
}
if !g.AllNamespaces { if !g.AllNamespaces {
err = gClient.List(context.TODO(), rbList, &client.ListOptions{ rbList, err = gclient.WorkV1alpha2().ResourceBindings(*defaultConfigFlags.Namespace).List(context.TODO(), metav1.ListOptions{})
Namespace: namespace,
})
} else { } else {
err = gClient.List(context.TODO(), rbList, &client.ListOptions{}) rbList, err = gclient.WorkV1alpha2().ResourceBindings("").List(context.TODO(), metav1.ListOptions{})
} }
if err != nil { if err != nil {
return err return err
} }
if err = gClient.List(context.TODO(), crbList, &client.ListOptions{}); err != nil { if crbList, err = gclient.WorkV1alpha2().ClusterResourceBindings().List(context.TODO(), metav1.ListOptions{}); err != nil {
return err return err
} }
@ -971,28 +949,22 @@ func (g *CommandGetOptions) getRBInKarmada(namespace string, config *rest.Config
return nil return nil
} }
// setClusterProxyInfo set proxy information of cluster // getClusterInKarmadaForClient get cluster info in karmada cluster
func (g *CommandGetOptions) setClusterProxyInfo(karmadaRestConfig *rest.Config, name string, clusterInfos map[string]*ClusterInfo) error { func getClusterInKarmadaForClient(gclient karmadaclientset.Interface) (map[string]*ClusterInfo, error) {
clusterClient := karmadaclientset.NewForConfigOrDie(karmadaRestConfig).ClusterV1alpha1().Clusters() clusterList, err := gclient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
// check if the cluster exists in the Karmada control plane
_, err := clusterClient.Get(context.TODO(), name, metav1.GetOptions{})
if err != nil { if err != nil {
return err return nil, err
} }
clusterInfos[name].APIEndpoint = karmadaRestConfig.Host + fmt.Sprintf(proxyURL, name) clusterInfos := make(map[string]*ClusterInfo, len(clusterList.Items))
clusterInfos[name].KubeConfig = g.KubeConfig for i := range clusterList.Items {
clusterInfos[name].Context = g.KarmadaContext cluster := &ClusterInfo{
if clusterInfos[name].KubeConfig == "" { APIEndpoint: clusterList.Items[i].Spec.APIEndpoint,
env := os.Getenv("KUBECONFIG") ClusterSyncMode: clusterList.Items[i].Spec.SyncMode,
if env != "" {
clusterInfos[name].KubeConfig = env
} else {
clusterInfos[name].KubeConfig = defaultKubeConfig
} }
clusterInfos[clusterList.Items[i].GetName()] = cluster
} }
return nil return clusterInfos, nil
} }
// getClusterInKarmada get cluster info in karmada cluster // getClusterInKarmada get cluster info in karmada cluster
@ -1005,7 +977,6 @@ func getClusterInKarmada(client *rest.Config, clusterInfos map[string]*ClusterIn
if err = gClient.List(context.TODO(), clusterList); err != nil { if err = gClient.List(context.TODO(), clusterList); err != nil {
return err return err
} }
for i := range clusterList.Items { for i := range clusterList.Items {
cluster := &ClusterInfo{ cluster := &ClusterInfo{
APIEndpoint: clusterList.Items[i].Spec.APIEndpoint, APIEndpoint: clusterList.Items[i].Spec.APIEndpoint,

View File

@ -61,7 +61,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
{ {
Message: "Basic Commands:", Message: "Basic Commands:",
Commands: []*cobra.Command{ Commands: []*cobra.Command{
NewCmdGet(karmadaConfig, parentCommand, ioStreams), NewCmdGet(f, parentCommand, ioStreams),
}, },
}, },
{ {