Merge pull request #2694 from lonelyCZ/pr-promote-factory

karmadactl promote uses factory to access cluster
This commit is contained in:
karmada-bot 2022-11-07 09:56:48 +08:00 committed by GitHub
commit 052de02155
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 143 additions and 243 deletions

View File

@ -33,7 +33,6 @@ import (
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
utilpointer "k8s.io/utils/pointer" utilpointer "k8s.io/utils/pointer"
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/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
@ -295,18 +294,18 @@ func (g *CommandGetOptions) Run(f util.Factory, cmd *cobra.Command, args []strin
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 { if err := g.getRBInKarmada(gclient); err != nil {
return err return err
} }
if len(g.Clusters) <= 0 { if len(g.Clusters) <= 0 {
for c := range clusterInfos { clusterList, err := gclient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
g.Clusters = append(g.Clusters, c) if err != nil {
return fmt.Errorf("failed to list all member clusters in control plane, err: %w", err)
}
for i := range clusterList.Items {
g.Clusters = append(g.Clusters, clusterList.Items[i].Name)
} }
} }
@ -877,33 +876,6 @@ func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
} }
// ClusterInfo Information about the member in the karmada cluster.
type ClusterInfo struct {
KubeConfig string
Context string
APIEndpoint string
ClusterSyncMode clusterv1alpha1.ClusterSyncMode
}
func getFactory(clusterName string, clusterInfos map[string]*ClusterInfo, namespace string) cmdutil.Factory {
kubeConfigFlags := NewConfigFlags(true).WithDeprecatedPasswordFlag()
// Build member cluster kubeConfigFlags
kubeConfigFlags.APIServer = stringptr(clusterInfos[clusterName].APIEndpoint)
// Use kubeconfig to access member cluster
kubeConfigFlags.KubeConfig = stringptr(clusterInfos[clusterName].KubeConfig)
kubeConfigFlags.Context = stringptr(clusterInfos[clusterName].Context)
kubeConfigFlags.usePersistentConfig = true
if namespace != "" {
kubeConfigFlags.Namespace = stringptr(namespace)
}
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
return cmdutil.NewFactory(matchVersionKubeConfigFlags)
}
func (g *CommandGetOptions) transformRequests(req *rest.Request) { func (g *CommandGetOptions) transformRequests(req *rest.Request) {
if !g.ServerPrint || !g.IsHumanReadablePrinter { if !g.ServerPrint || !g.IsHumanReadablePrinter {
return return
@ -961,44 +933,6 @@ func (g *CommandGetOptions) getRBInKarmada(gclient karmadaclientset.Interface) e
return nil return nil
} }
// getClusterInKarmadaForClient get cluster info in karmada cluster
func getClusterInKarmadaForClient(gclient karmadaclientset.Interface) (map[string]*ClusterInfo, error) {
clusterList, err := gclient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
clusterInfos := make(map[string]*ClusterInfo, len(clusterList.Items))
for i := range clusterList.Items {
cluster := &ClusterInfo{
APIEndpoint: clusterList.Items[i].Spec.APIEndpoint,
ClusterSyncMode: clusterList.Items[i].Spec.SyncMode,
}
clusterInfos[clusterList.Items[i].GetName()] = cluster
}
return clusterInfos, nil
}
// getClusterInKarmada get cluster info in karmada cluster
func getClusterInKarmada(client *rest.Config, clusterInfos map[string]*ClusterInfo) error {
clusterList := &clusterv1alpha1.ClusterList{}
gClient, err := gclient.NewForConfig(client)
if err != nil {
return err
}
if err = gClient.List(context.TODO(), clusterList); err != nil {
return err
}
for i := range clusterList.Items {
cluster := &ClusterInfo{
APIEndpoint: clusterList.Items[i].Spec.APIEndpoint,
ClusterSyncMode: clusterList.Items[i].Spec.SyncMode,
}
clusterInfos[clusterList.Items[i].GetName()] = cluster
}
return nil
}
func getRBKey(gvk schema.GroupVersionKind, row metav1.TableRow, cluster string) string { func getRBKey(gvk schema.GroupVersionKind, row metav1.TableRow, cluster string) string {
resourceName, _ := row.Cells[0].(string) resourceName, _ := row.Cells[0].(string)
rbKey := names.GenerateBindingName(gvk.Kind, resourceName) rbKey := names.GenerateBindingName(gvk.Kind, resourceName)

View File

@ -96,7 +96,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
Message: "Advanced Commands:", Message: "Advanced Commands:",
Commands: []*cobra.Command{ Commands: []*cobra.Command{
NewCmdApply(f, parentCommand, ioStreams), NewCmdApply(f, parentCommand, ioStreams),
NewCmdPromote(karmadaConfig, parentCommand), NewCmdPromote(f, parentCommand),
}, },
}, },
} }

View File

@ -23,7 +23,6 @@ import (
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/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/resourceinterpreter/defaultinterpreter/prune" "github.com/karmada-io/karmada/pkg/resourceinterpreter/defaultinterpreter/prune"
"github.com/karmada-io/karmada/pkg/util/gclient" "github.com/karmada-io/karmada/pkg/util/gclient"
@ -55,7 +54,7 @@ var (
) )
// NewCmdPromote defines the `promote` command that promote resources from legacy clusters // NewCmdPromote defines the `promote` command that promote resources from legacy clusters
func NewCmdPromote(karmadaConfig KarmadaConfig, parentCommand string) *cobra.Command { func NewCmdPromote(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandPromoteOption{} opts := CommandPromoteOption{}
opts.JSONYamlPrintFlags = genericclioptions.NewJSONYamlPrintFlags() opts.JSONYamlPrintFlags = genericclioptions.NewJSONYamlPrintFlags()
@ -67,13 +66,13 @@ func NewCmdPromote(karmadaConfig KarmadaConfig, parentCommand string) *cobra.Com
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := opts.Complete(args); err != nil { if err := opts.Complete(f, args); err != nil {
return err return err
} }
if err := opts.Validate(); err != nil { if err := opts.Validate(); err != nil {
return err return err
} }
if err := RunPromote(karmadaConfig, opts, args); err != nil { if err := opts.Run(f, args); err != nil {
return err return err
} }
return nil return nil
@ -86,13 +85,14 @@ func NewCmdPromote(karmadaConfig KarmadaConfig, parentCommand string) *cobra.Com
flag := cmd.Flags() flag := cmd.Flags()
opts.AddFlags(flag) opts.AddFlags(flag)
flag.StringVar(defaultConfigFlags.KubeConfig, "kubeconfig", *defaultConfigFlags.KubeConfig, "Path to the kubeconfig file to use for CLI requests.")
flag.StringVar(defaultConfigFlags.Context, "karmada-context", *defaultConfigFlags.Context, "The name of the kubeconfig context to use")
return cmd return cmd
} }
// CommandPromoteOption holds all command options for promote // CommandPromoteOption holds all command options for promote
type CommandPromoteOption struct { type CommandPromoteOption struct {
options.GlobalCommandOptions
// Cluster is the name of legacy cluster // Cluster is the name of legacy cluster
Cluster string Cluster string
@ -121,11 +121,9 @@ type CommandPromoteOption struct {
// AddFlags adds flags to the specified FlagSet. // AddFlags adds flags to the specified FlagSet.
func (o *CommandPromoteOption) AddFlags(flags *pflag.FlagSet) { func (o *CommandPromoteOption) AddFlags(flags *pflag.FlagSet) {
o.GlobalCommandOptions.AddFlags(flags)
flags.StringVarP(&o.OutputFormat, "output", "o", "", "Output format. One of: json|yaml") flags.StringVarP(&o.OutputFormat, "output", "o", "", "Output format. One of: json|yaml")
flags.StringVarP(&o.Namespace, "namespace", "n", "default", "-n=namespace or -n namespace") flags.StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "If present, the namespace scope for this CLI request")
flags.StringVarP(&o.Cluster, "cluster", "C", "", "the name of legacy cluster (eg -C=member1)") flags.StringVarP(&o.Cluster, "cluster", "C", "", "the name of legacy cluster (eg -C=member1)")
flags.StringVar(&o.ClusterContext, "cluster-context", "", flags.StringVar(&o.ClusterContext, "cluster-context", "",
"Context name of legacy cluster in kubeconfig. Only works when there are multiple contexts in the kubeconfig.") "Context name of legacy cluster in kubeconfig. Only works when there are multiple contexts in the kubeconfig.")
@ -135,7 +133,9 @@ func (o *CommandPromoteOption) AddFlags(flags *pflag.FlagSet) {
} }
// Complete ensures that options are valid and marshals them if necessary // Complete ensures that options are valid and marshals them if necessary
func (o *CommandPromoteOption) Complete(args []string) error { func (o *CommandPromoteOption) Complete(f util.Factory, args []string) error {
var err error
if len(args) != 2 { if len(args) != 2 {
return fmt.Errorf("incorrect command format, please use correct command format") return fmt.Errorf("incorrect command format, please use correct command format")
} }
@ -159,6 +159,18 @@ func (o *CommandPromoteOption) Complete(args []string) error {
} }
} }
if o.Namespace == "" {
o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return fmt.Errorf("failed to get namespace from Factory. error: %w", err)
}
}
// If '--cluster-context' not specified, take the cluster name as the context.
if len(o.ClusterContext) == 0 {
o.ClusterContext = o.Cluster
}
return nil return nil
} }
@ -172,81 +184,67 @@ func (o *CommandPromoteOption) Validate() error {
return fmt.Errorf("output format is only one of json and yaml") return fmt.Errorf("output format is only one of json and yaml")
} }
// If '--cluster-context' not specified, take the cluster name as the context.
if len(o.ClusterContext) == 0 {
o.ClusterContext = o.Cluster
}
return nil return nil
} }
// RunPromote promote resources from legacy clusters // Run promote resources from legacy clusters
func RunPromote(karmadaConfig KarmadaConfig, opts CommandPromoteOption, args []string) error { func (o *CommandPromoteOption) Run(f util.Factory, args []string) error {
// Get control plane karmada-apiserver client var memberClusterFactory cmdutil.Factory
controlPlaneRestConfig, err := karmadaConfig.GetRestConfig(opts.KarmadaContext, opts.KubeConfig) var err error
if err != nil {
return fmt.Errorf("failed to get control plane rest config. context: %s, kubeconfig: %s, error: %v",
opts.KarmadaContext, opts.KubeConfig, err)
}
clusterInfos := make(map[string]*ClusterInfo) if o.ClusterKubeConfig != "" {
if err := getClusterInKarmada(controlPlaneRestConfig, clusterInfos); err != nil {
return fmt.Errorf("failed to get cluster info in karmada control plane. err: %v", err)
}
if _, exist := clusterInfos[opts.Cluster]; !exist {
return fmt.Errorf("cluster(%s) does't exist in karmada control plane", opts.Cluster)
}
var f cmdutil.Factory
if opts.ClusterKubeConfig != "" {
kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag() kubeConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag()
kubeConfigFlags.KubeConfig = &opts.ClusterKubeConfig kubeConfigFlags.KubeConfig = &o.ClusterKubeConfig
kubeConfigFlags.Context = &opts.ClusterContext kubeConfigFlags.Context = &o.ClusterContext
f = cmdutil.NewFactory(kubeConfigFlags) memberClusterFactory = cmdutil.NewFactory(kubeConfigFlags)
} else { } else {
opts.setClusterProxyInfo(controlPlaneRestConfig, opts.Cluster, clusterInfos) memberClusterFactory, err = f.FactoryForMemberCluster(o.Cluster)
f = getFactory(opts.Cluster, clusterInfos, "") if err != nil {
return fmt.Errorf("failed to get Factory of the member cluster. err: %w", err)
}
} }
objInfo, err := opts.getObjInfo(f, opts.Cluster, args) objInfo, err := o.getObjInfo(memberClusterFactory, o.Cluster, args)
if err != nil { if err != nil {
return fmt.Errorf("failed to get resource in cluster(%s). err: %v", opts.Cluster, err) return fmt.Errorf("failed to get resource in cluster(%s). err: %w", o.Cluster, err)
} }
obj := objInfo.Info.Object.(*unstructured.Unstructured) obj := objInfo.Info.Object.(*unstructured.Unstructured)
opts.gvk = obj.GetObjectKind().GroupVersionKind() o.gvk = obj.GetObjectKind().GroupVersionKind()
controlPlaneRestConfig, err := f.ToRESTConfig()
if err != nil {
return fmt.Errorf("failed to get control plane rest config. err: %w", err)
}
mapper, err := restmapper.MapperProvider(controlPlaneRestConfig) mapper, err := restmapper.MapperProvider(controlPlaneRestConfig)
if err != nil { if err != nil {
return fmt.Errorf("failed to create restmapper: %v", err) return fmt.Errorf("failed to create restmapper: %v", err)
} }
gvr, err := restmapper.GetGroupVersionResource(mapper, opts.gvk) gvr, err := restmapper.GetGroupVersionResource(mapper, o.gvk)
if err != nil { if err != nil {
return fmt.Errorf("failed to get gvr from %q: %v", opts.gvk, err) return fmt.Errorf("failed to get gvr from %q: %v", o.gvk, err)
} }
return promote(controlPlaneRestConfig, obj, gvr, opts) return o.promote(controlPlaneRestConfig, obj, gvr)
} }
func promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured, gvr schema.GroupVersionResource, opts CommandPromoteOption) error { func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured, gvr schema.GroupVersionResource) error {
if err := preprocessResource(obj); err != nil { if err := preprocessResource(obj); err != nil {
return fmt.Errorf("failed to preprocess resource %q(%s/%s) in control plane: %v", gvr, opts.Namespace, opts.name, err) return fmt.Errorf("failed to preprocess resource %q(%s/%s) in control plane: %v", gvr, o.Namespace, o.name, err)
} }
if opts.OutputFormat != "" { if o.OutputFormat != "" {
// only print the resource template and Policy // only print the resource template and Policy
err := printObjectAndPolicy(obj, gvr, opts) err := o.printObjectAndPolicy(obj, gvr)
return err return err
} }
if opts.DryRun { if o.DryRun {
return nil return nil
} }
@ -255,51 +253,51 @@ func promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured
karmadaClient := karmadaclientset.NewForConfigOrDie(controlPlaneRestConfig) karmadaClient := karmadaclientset.NewForConfigOrDie(controlPlaneRestConfig)
if len(obj.GetNamespace()) == 0 { if len(obj.GetNamespace()) == 0 {
_, err := controlPlaneDynamicClient.Resource(gvr).Get(context.TODO(), opts.name, metav1.GetOptions{}) _, err := controlPlaneDynamicClient.Resource(gvr).Get(context.TODO(), o.name, metav1.GetOptions{})
if err == nil { if err == nil {
fmt.Printf("Resource %q(%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n", fmt.Printf("Resource %q(%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n",
gvr, opts.name) gvr, o.name)
return nil return nil
} }
if !apierrors.IsNotFound(err) { if !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to get resource %q(%s) in control plane: %v", gvr, opts.name, err) return fmt.Errorf("failed to get resource %q(%s) in control plane: %v", gvr, o.name, err)
} }
_, err = controlPlaneDynamicClient.Resource(gvr).Create(context.TODO(), obj, metav1.CreateOptions{}) _, err = controlPlaneDynamicClient.Resource(gvr).Create(context.TODO(), obj, metav1.CreateOptions{})
if err != nil { if err != nil {
return fmt.Errorf("failed to create resource %q(%s) in control plane: %v", gvr, opts.name, err) return fmt.Errorf("failed to create resource %q(%s) in control plane: %v", gvr, o.name, err)
} }
err = createOrUpdateClusterPropagationPolicy(karmadaClient, gvr, opts) err = o.createClusterPropagationPolicy(karmadaClient, gvr)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Resource %q(%s) is promoted successfully\n", gvr, opts.name) fmt.Printf("Resource %q(%s) is promoted successfully\n", gvr, o.name)
} else { } else {
_, err := controlPlaneDynamicClient.Resource(gvr).Namespace(opts.Namespace).Get(context.TODO(), opts.name, metav1.GetOptions{}) _, err := controlPlaneDynamicClient.Resource(gvr).Namespace(o.Namespace).Get(context.TODO(), o.name, metav1.GetOptions{})
if err == nil { if err == nil {
fmt.Printf("Resource %q(%s/%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n", fmt.Printf("Resource %q(%s/%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n",
gvr, opts.Namespace, opts.name) gvr, o.Namespace, o.name)
return nil return nil
} }
if !apierrors.IsNotFound(err) { if !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to get resource %q(%s/%s) in control plane: %v", gvr, opts.Namespace, opts.name, err) return fmt.Errorf("failed to get resource %q(%s/%s) in control plane: %v", gvr, o.Namespace, o.name, err)
} }
_, err = controlPlaneDynamicClient.Resource(gvr).Namespace(opts.Namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) _, err = controlPlaneDynamicClient.Resource(gvr).Namespace(o.Namespace).Create(context.TODO(), obj, metav1.CreateOptions{})
if err != nil { if err != nil {
return fmt.Errorf("failed to create resource %q(%s/%s) in control plane: %v", gvr, opts.Namespace, opts.name, err) return fmt.Errorf("failed to create resource %q(%s/%s) in control plane: %v", gvr, o.Namespace, o.name, err)
} }
err = createOrUpdatePropagationPolicy(karmadaClient, gvr, opts) err = o.createPropagationPolicy(karmadaClient, gvr)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("Resource %q(%s/%s) is promoted successfully\n", gvr, opts.Namespace, opts.name) fmt.Printf("Resource %q(%s/%s) is promoted successfully\n", gvr, o.Namespace, o.name)
} }
return nil return nil
@ -307,12 +305,11 @@ func promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured
// getObjInfo get obj info in member cluster // getObjInfo get obj info in member cluster
func (o *CommandPromoteOption) getObjInfo(f cmdutil.Factory, cluster string, args []string) (*Obj, error) { func (o *CommandPromoteOption) getObjInfo(f cmdutil.Factory, cluster string, args []string) (*Obj, error) {
chunkSize := int64(500)
r := f.NewBuilder(). r := f.NewBuilder().
Unstructured(). Unstructured().
NamespaceParam(o.Namespace). NamespaceParam(o.Namespace).
FilenameParam(false, &o.FilenameOptions). FilenameParam(false, &o.FilenameOptions).
RequestChunksOf(chunkSize). RequestChunksOf(500).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
ContinueOnError(). ContinueOnError().
Latest(). Latest().
@ -338,24 +335,9 @@ func (o *CommandPromoteOption) getObjInfo(f cmdutil.Factory, cluster string, arg
return obj, nil return obj, nil
} }
// setClusterProxyInfo set proxy information of cluster
func (o *CommandPromoteOption) setClusterProxyInfo(karmadaRestConfig *rest.Config, name string, clusterInfos map[string]*ClusterInfo) {
clusterInfos[name].APIEndpoint = karmadaRestConfig.Host + fmt.Sprintf(proxyURL, name)
clusterInfos[name].KubeConfig = o.KubeConfig
clusterInfos[name].Context = o.KarmadaContext
if clusterInfos[name].KubeConfig == "" {
env := os.Getenv("KUBECONFIG")
if env != "" {
clusterInfos[name].KubeConfig = env
} else {
clusterInfos[name].KubeConfig = defaultKubeConfig
}
}
}
// printObjectAndPolicy print the converted resource // printObjectAndPolicy print the converted resource
func printObjectAndPolicy(obj *unstructured.Unstructured, gvr schema.GroupVersionResource, opts CommandPromoteOption) error { func (o *CommandPromoteOption) printObjectAndPolicy(obj *unstructured.Unstructured, gvr schema.GroupVersionResource) error {
printer, err := opts.Printer(nil, nil, false, false) printer, err := o.Printer(nil, nil, false, false)
if err != nil { if err != nil {
return fmt.Errorf("failed to initialize k8s printer. err: %v", err) return fmt.Errorf("failed to initialize k8s printer. err: %v", err)
} }
@ -365,12 +347,12 @@ func printObjectAndPolicy(obj *unstructured.Unstructured, gvr schema.GroupVersio
} }
if len(obj.GetNamespace()) == 0 { if len(obj.GetNamespace()) == 0 {
cpp := buildClusterPropagationPolicy(gvr, opts) cpp := buildClusterPropagationPolicy(o.name, o.Cluster, gvr, o.gvk)
if err = printer.PrintObj(cpp, os.Stdout); err != nil { if err = printer.PrintObj(cpp, os.Stdout); err != nil {
return fmt.Errorf("failed to print the ClusterPropagationPolicy. err: %v", err) return fmt.Errorf("failed to print the ClusterPropagationPolicy. err: %v", err)
} }
} else { } else {
pp := buildPropagationPolicy(gvr, opts) pp := buildPropagationPolicy(o.name, o.Namespace, o.Cluster, gvr, o.gvk)
if err = printer.PrintObj(pp, os.Stdout); err != nil { if err = printer.PrintObj(pp, os.Stdout); err != nil {
return fmt.Errorf("failed to print the PropagationPolicy. err: %v", err) return fmt.Errorf("failed to print the PropagationPolicy. err: %v", err)
} }
@ -379,6 +361,44 @@ func printObjectAndPolicy(obj *unstructured.Unstructured, gvr schema.GroupVersio
return nil return nil
} }
// createPropagationPolicy create PropagationPolicy in karmada control plane
func (o *CommandPromoteOption) createPropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource) error {
name := names.GeneratePolicyName(o.Namespace, o.name, o.gvk.String())
_, err := karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) {
pp := buildPropagationPolicy(o.name, o.Namespace, o.Cluster, gvr, o.gvk)
_, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Create(context.TODO(), pp, metav1.CreateOptions{})
return err
}
if err != nil {
return fmt.Errorf("failed to get PropagationPolicy(%s/%s) in control plane: %v", o.Namespace, name, err)
}
// PropagationPolicy already exists, not to create it
return fmt.Errorf("the PropagationPolicy(%s/%s) already exist, please edit it to propagate resource", o.Namespace, name)
}
// createClusterPropagationPolicy create ClusterPropagationPolicy in karmada control plane
func (o *CommandPromoteOption) createClusterPropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource) error {
name := names.GeneratePolicyName("", o.name, o.gvk.String())
_, err := karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) {
cpp := buildClusterPropagationPolicy(o.name, o.Cluster, gvr, o.gvk)
_, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), cpp, metav1.CreateOptions{})
return err
}
if err != nil {
return fmt.Errorf("failed to get ClusterPropagationPolicy(%s) in control plane: %v", name, err)
}
// ClusterPropagationPolicy already exists, not to create it
return fmt.Errorf("the ClusterPropagationPolicy(%s) already exist, please edit it to propagate resource", name)
}
// preprocessResource delete redundant fields to convert resource as template // preprocessResource delete redundant fields to convert resource as template
func preprocessResource(obj *unstructured.Unstructured) error { func preprocessResource(obj *unstructured.Unstructured) error {
// remove fields that generated by kube-apiserver and no need(or can't) propagate to member clusters. // remove fields that generated by kube-apiserver and no need(or can't) propagate to member clusters.
@ -403,64 +423,26 @@ func addOverwriteAnnotation(obj *unstructured.Unstructured) {
} }
} }
// createOrUpdatePropagationPolicy create PropagationPolicy in karmada control plane
func createOrUpdatePropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource, opts CommandPromoteOption) error {
name := names.GeneratePolicyName(opts.Namespace, opts.name, opts.gvk.String())
_, err := karmadaClient.PolicyV1alpha1().PropagationPolicies(opts.Namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) {
pp := buildPropagationPolicy(gvr, opts)
_, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(opts.Namespace).Create(context.TODO(), pp, metav1.CreateOptions{})
return err
}
if err != nil {
return fmt.Errorf("failed to get PropagationPolicy(%s/%s) in control plane: %v", opts.Namespace, name, err)
}
// PropagationPolicy already exists, not to create it
return fmt.Errorf("the PropagationPolicy(%s/%s) already exist, please edit it to propagate resource", opts.Namespace, name)
}
// createOrUpdateClusterPropagationPolicy create ClusterPropagationPolicy in karmada control plane
func createOrUpdateClusterPropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource, opts CommandPromoteOption) error {
name := names.GeneratePolicyName("", opts.name, opts.gvk.String())
_, err := karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) {
cpp := buildClusterPropagationPolicy(gvr, opts)
_, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), cpp, metav1.CreateOptions{})
return err
}
if err != nil {
return fmt.Errorf("failed to get ClusterPropagationPolicy(%s) in control plane: %v", name, err)
}
// ClusterPropagationPolicy already exists, not to create it
return fmt.Errorf("the ClusterPropagationPolicy(%s) already exist, please edit it to propagate resource", name)
}
// buildPropagationPolicy build PropagationPolicy according to resource and cluster // buildPropagationPolicy build PropagationPolicy according to resource and cluster
func buildPropagationPolicy(gvr schema.GroupVersionResource, opts CommandPromoteOption) *policyv1alpha1.PropagationPolicy { func buildPropagationPolicy(resourceName, namespace, cluster string, gvr schema.GroupVersionResource, gvk schema.GroupVersionKind) *policyv1alpha1.PropagationPolicy {
name := names.GeneratePolicyName(opts.Namespace, opts.name, opts.gvk.String()) policyName := names.GeneratePolicyName(namespace, resourceName, gvk.String())
pp := &policyv1alpha1.PropagationPolicy{ pp := &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: policyName,
Namespace: opts.Namespace, Namespace: namespace,
}, },
Spec: policyv1alpha1.PropagationSpec{ Spec: policyv1alpha1.PropagationSpec{
ResourceSelectors: []policyv1alpha1.ResourceSelector{ ResourceSelectors: []policyv1alpha1.ResourceSelector{
{ {
APIVersion: gvr.GroupVersion().String(), APIVersion: gvr.GroupVersion().String(),
Kind: opts.gvk.Kind, Kind: gvk.Kind,
Name: opts.name, Name: resourceName,
}, },
}, },
Placement: policyv1alpha1.Placement{ Placement: policyv1alpha1.Placement{
ClusterAffinity: &policyv1alpha1.ClusterAffinity{ ClusterAffinity: &policyv1alpha1.ClusterAffinity{
ClusterNames: []string{opts.Cluster}, ClusterNames: []string{cluster},
}, },
}, },
}, },
@ -470,24 +452,24 @@ func buildPropagationPolicy(gvr schema.GroupVersionResource, opts CommandPromote
} }
// buildClusterPropagationPolicy build ClusterPropagationPolicy according to resource and cluster // buildClusterPropagationPolicy build ClusterPropagationPolicy according to resource and cluster
func buildClusterPropagationPolicy(gvr schema.GroupVersionResource, opts CommandPromoteOption) *policyv1alpha1.ClusterPropagationPolicy { func buildClusterPropagationPolicy(resourceName, cluster string, gvr schema.GroupVersionResource, gvk schema.GroupVersionKind) *policyv1alpha1.ClusterPropagationPolicy {
name := names.GeneratePolicyName("", opts.name, opts.gvk.String()) policyName := names.GeneratePolicyName("", resourceName, gvk.String())
cpp := &policyv1alpha1.ClusterPropagationPolicy{ cpp := &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: policyName,
}, },
Spec: policyv1alpha1.PropagationSpec{ Spec: policyv1alpha1.PropagationSpec{
ResourceSelectors: []policyv1alpha1.ResourceSelector{ ResourceSelectors: []policyv1alpha1.ResourceSelector{
{ {
APIVersion: gvr.GroupVersion().String(), APIVersion: gvr.GroupVersion().String(),
Kind: opts.gvk.Kind, Kind: gvk.Kind,
Name: opts.name, Name: resourceName,
}, },
}, },
Placement: policyv1alpha1.Placement{ Placement: policyv1alpha1.Placement{
ClusterAffinity: &policyv1alpha1.ClusterAffinity{ ClusterAffinity: &policyv1alpha1.ClusterAffinity{
ClusterNames: []string{opts.Cluster}, ClusterNames: []string{cluster},
}, },
}, },
}, },

View File

@ -33,14 +33,16 @@ import (
) )
var _ = ginkgo.Describe("Karmadactl promote testing", func() { var _ = ginkgo.Describe("Karmadactl promote testing", func() {
var karmadaConfig karmadactl.KarmadaConfig
var member1 string var member1 string
var member1Client kubernetes.Interface var member1Client kubernetes.Interface
var f cmdutil.Factory
ginkgo.BeforeEach(func() { ginkgo.BeforeEach(func() {
karmadaConfig = karmadactl.NewKarmadaConfig(clientcmd.NewDefaultPathOptions())
member1 = "member1" member1 = "member1"
member1Client = framework.GetClusterClient(member1) member1Client = framework.GetClusterClient(member1)
defaultConfigFlags := genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0)
defaultConfigFlags.Context = &karmadaContext
f = cmdutil.NewFactory(defaultConfigFlags)
}) })
ginkgo.Context("Test promoting namespaced resource: deployment", func() { ginkgo.Context("Test promoting namespaced resource: deployment", func() {
@ -85,18 +87,15 @@ var _ = ginkgo.Describe("Karmadactl promote testing", func() {
// Step 2, promote namespace used by the deployment from member1 to karmada // Step 2, promote namespace used by the deployment from member1 to karmada
ginkgo.By(fmt.Sprintf("Promoting namespace %s from member: %s to karmada control plane", deploymentNamespace, member1), func() { ginkgo.By(fmt.Sprintf("Promoting namespace %s from member: %s to karmada control plane", deploymentNamespace, member1), func() {
namespaceOpts = karmadactl.CommandPromoteOption{ namespaceOpts = karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Cluster: member1, Cluster: member1,
} }
args := []string{"namespace", deploymentNamespace} args := []string{"namespace", deploymentNamespace}
// init args: place namespace name to CommandPromoteOption.name // init args: place namespace name to CommandPromoteOption.name
err := namespaceOpts.Complete(args) err := namespaceOpts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
// use karmadactl to promote a namespace from member1 // use karmadactl to promote a namespace from member1
err = karmadactl.RunPromote(karmadaConfig, namespaceOpts, args) err = namespaceOpts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
framework.WaitNamespacePresentOnClusterByClient(kubeClient, deploymentNamespace) framework.WaitNamespacePresentOnClusterByClient(kubeClient, deploymentNamespace)
@ -105,19 +104,16 @@ var _ = ginkgo.Describe("Karmadactl promote testing", func() {
// Step 3, promote deployment from cluster member1 to karmada // Step 3, promote deployment from cluster member1 to karmada
ginkgo.By(fmt.Sprintf("Promoting deployment %s from member: %s to karmada", deploymentName, member1), func() { ginkgo.By(fmt.Sprintf("Promoting deployment %s from member: %s to karmada", deploymentName, member1), func() {
deploymentOpts = karmadactl.CommandPromoteOption{ deploymentOpts = karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Namespace: deploymentNamespace, Namespace: deploymentNamespace,
Cluster: member1, Cluster: member1,
} }
args := []string{"deployment", deploymentName} args := []string{"deployment", deploymentName}
// init args: place deployment name to CommandPromoteOption.name // init args: place deployment name to CommandPromoteOption.name
err := deploymentOpts.Complete(args) err := deploymentOpts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
// use karmadactl to promote a deployment from member1 // use karmadactl to promote a deployment from member1
err = karmadactl.RunPromote(karmadaConfig, deploymentOpts, args) err = deploymentOpts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}) })
@ -196,35 +192,29 @@ var _ = ginkgo.Describe("Karmadactl promote testing", func() {
// Step2, promote clusterrole and clusterrolebinding from member1 // Step2, promote clusterrole and clusterrolebinding from member1
ginkgo.By(fmt.Sprintf("Promoting clusterrole %s and clusterrolebindings %s from member to karmada", clusterRoleName, clusterRoleBindingName), func() { ginkgo.By(fmt.Sprintf("Promoting clusterrole %s and clusterrolebindings %s from member to karmada", clusterRoleName, clusterRoleBindingName), func() {
clusterRoleOpts = karmadactl.CommandPromoteOption{ clusterRoleOpts = karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Cluster: member1, Cluster: member1,
} }
args := []string{"clusterrole", clusterRoleName} args := []string{"clusterrole", clusterRoleName}
// init args: place clusterrole name to CommandPromoteOption.name // init args: place clusterrole name to CommandPromoteOption.name
err := clusterRoleOpts.Complete(args) err := clusterRoleOpts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
// use karmadactl to promote clusterrole from member1 // use karmadactl to promote clusterrole from member1
err = karmadactl.RunPromote(karmadaConfig, clusterRoleOpts, args) err = clusterRoleOpts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
clusterRoleBindingOpts = karmadactl.CommandPromoteOption{ clusterRoleBindingOpts = karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Cluster: member1, Cluster: member1,
} }
args = []string{"clusterrolebinding", clusterRoleBindingName} args = []string{"clusterrolebinding", clusterRoleBindingName}
// init args: place clusterrolebinding name to CommandPromoteOption.name // init args: place clusterrolebinding name to CommandPromoteOption.name
err = clusterRoleBindingOpts.Complete(args) err = clusterRoleBindingOpts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
// use karmadactl to promote clusterrolebinding from member1 // use karmadactl to promote clusterrolebinding from member1
err = karmadactl.RunPromote(karmadaConfig, clusterRoleBindingOpts, args) err = clusterRoleBindingOpts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}) })
@ -267,16 +257,13 @@ var _ = ginkgo.Describe("Karmadactl promote testing", func() {
ginkgo.By(fmt.Sprintf("Promoting namespace %s from member: %s to karmada control plane", serviceNamespace, member1), func() { ginkgo.By(fmt.Sprintf("Promoting namespace %s from member: %s to karmada control plane", serviceNamespace, member1), func() {
opts := karmadactl.CommandPromoteOption{ opts := karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Cluster: member1, Cluster: member1,
} }
args := []string{"namespace", serviceNamespace} args := []string{"namespace", serviceNamespace}
err := opts.Complete(args) err := opts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
err = karmadactl.RunPromote(karmadaConfig, opts, args) err = opts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
framework.WaitNamespacePresentOnClusterByClient(kubeClient, serviceNamespace) framework.WaitNamespacePresentOnClusterByClient(kubeClient, serviceNamespace)
@ -284,17 +271,14 @@ var _ = ginkgo.Describe("Karmadactl promote testing", func() {
ginkgo.By(fmt.Sprintf("Promoting service %s from member: %s to karmada control plane", serviceName, member1), func() { ginkgo.By(fmt.Sprintf("Promoting service %s from member: %s to karmada control plane", serviceName, member1), func() {
opts := karmadactl.CommandPromoteOption{ opts := karmadactl.CommandPromoteOption{
GlobalCommandOptions: options.GlobalCommandOptions{
KarmadaContext: karmadaContext,
},
Namespace: serviceNamespace, Namespace: serviceNamespace,
Cluster: member1, Cluster: member1,
} }
args := []string{"service", serviceName} args := []string{"service", serviceName}
err := opts.Complete(args) err := opts.Complete(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
err = karmadactl.RunPromote(karmadaConfig, opts, args) err = opts.Run(f, args)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
}) })