Skip to content

Commit

Permalink
Merge pull request #2202 from carlory/karmadactl-factory
Browse files Browse the repository at this point in the history
introduce factory interface for karmadactl
  • Loading branch information
karmada-bot committed Sep 2, 2022
2 parents 910018c + eb31ff4 commit fd1c0b2
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 19 deletions.
7 changes: 6 additions & 1 deletion pkg/karmadactl/karmadactl.go
Expand Up @@ -16,12 +16,16 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/addons"
"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit"
"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
)

var (
rootCmdShort = "%s controls a Kubernetes Cluster Federation."
rootCmdLong = "%s controls a Kubernetes Cluster Federation."

// It composes the set of values necessary for obtaining a REST client config with default values set.
defaultConfigFlags = genericclioptions.NewConfigFlags(true).WithDeprecatedPasswordFlag().WithDiscoveryBurst(300).WithDiscoveryQPS(50.0)
)

// NewKarmadaCtlCommand creates the `karmadactl` command.
Expand Down Expand Up @@ -51,6 +55,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
_ = flag.CommandLine.Parse(nil)

karmadaConfig := NewKarmadaConfig(clientcmd.NewDefaultPathOptions())
f := util.NewFactory(defaultConfigFlags)
ioStreams := genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
groups := templates.CommandGroups{
{
Expand Down Expand Up @@ -82,7 +87,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
{
Message: "Troubleshooting and Debugging Commands:",
Commands: []*cobra.Command{
NewCmdLogs(karmadaConfig, parentCommand, ioStreams),
NewCmdLogs(f, parentCommand, ioStreams),
NewCmdExec(karmadaConfig, parentCommand, ioStreams),
NewCmdDescribe(karmadaConfig, parentCommand, ioStreams),
},
Expand Down
31 changes: 13 additions & 18 deletions pkg/karmadactl/logs.go
Expand Up @@ -14,7 +14,7 @@ import (
"k8s.io/kubectl/pkg/util/templates"

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"
)

const (
Expand Down Expand Up @@ -50,7 +50,7 @@ var (
)

// NewCmdLogs new logs command.
func NewCmdLogs(karmadaConfig KarmadaConfig, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command {
func NewCmdLogs(f util.Factory, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command {
o := &LogsOptions{
KubectlLogsOptions: kubectllogs.NewLogsOptions(streams, false),
}
Expand All @@ -61,7 +61,7 @@ func NewCmdLogs(karmadaConfig KarmadaConfig, parentCommand string, streams gener
SilenceUsage: true,
Example: fmt.Sprintf(logsExample, parentCommand),
RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(karmadaConfig, cmd, args); err != nil {
if err := o.Complete(cmd, args, f); err != nil {
return err
}
if err := o.Validate(); err != nil {
Expand All @@ -74,25 +74,25 @@ func NewCmdLogs(karmadaConfig KarmadaConfig, parentCommand string, streams gener
},
}

o.GlobalCommandOptions.AddFlags(cmd.Flags())
flags := cmd.Flags()
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")
flags.StringVarP(&o.Cluster, "cluster", "C", "", "Specify a member cluster")
o.KubectlLogsOptions.AddFlags(cmd)
cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "If present, the namespace scope for this CLI request")
cmd.Flags().StringVarP(&o.Cluster, "cluster", "C", "", "Specify a member cluster")

return cmd
}

// LogsOptions contains the input to the logs command.
type LogsOptions struct {
// global flags
options.GlobalCommandOptions
// flags specific to logs
KubectlLogsOptions *kubectllogs.LogsOptions
Namespace string
Cluster string
}

// Complete ensures that options are valid and marshals them if necessary
func (o *LogsOptions) Complete(karmadaConfig KarmadaConfig, cmd *cobra.Command, args []string) error {
func (o *LogsOptions) Complete(cmd *cobra.Command, args []string, f util.Factory) error {
if o.Cluster == "" {
return fmt.Errorf("must specify a cluster")
}
Expand All @@ -112,17 +112,11 @@ func (o *LogsOptions) Complete(karmadaConfig KarmadaConfig, cmd *cobra.Command,
return cmdutil.UsageErrorf(cmd, "%s", logsUsageErrStr)
}

karmadaRestConfig, err := karmadaConfig.GetRestConfig(o.KarmadaContext, o.KubeConfig)
if err != nil {
return fmt.Errorf("failed to get control plane rest config. context: %s, kube-config: %s, error: %v",
o.KarmadaContext, o.KubeConfig, err)
}
clusterInfo, err := getClusterInfo(karmadaRestConfig, o.Cluster, o.KubeConfig, o.KarmadaContext)
memberFactory, err := f.FactoryForMemberCluster(o.Cluster)
if err != nil {
return err
}
f := getFactory(o.Cluster, clusterInfo, o.Namespace)
return o.KubectlLogsOptions.Complete(f, cmd, args)
return o.KubectlLogsOptions.Complete(memberFactory, cmd, args)
}

// Validate checks to the LogsOptions to see if there is sufficient information run the command
Expand All @@ -136,6 +130,7 @@ func (o *LogsOptions) Run() error {
}

// getClusterInfo get information of cluster
// TODO(@carlory): remove it when all sub command accepts factory as input parameter.
func getClusterInfo(karmadaRestConfig *rest.Config, clusterName, kubeConfig, karmadaContext string) (map[string]*ClusterInfo, error) {
clusterClient := karmadaclientset.NewForConfigOrDie(karmadaRestConfig).ClusterV1alpha1().Clusters()

Expand Down
104 changes: 104 additions & 0 deletions pkg/karmadactl/util/factory.go
@@ -0,0 +1,104 @@
package util

import (
"context"
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"

karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
)

const proxyURL = "/apis/cluster.karmada.io/v1alpha1/clusters/%s/proxy/"

// The Factory interface provides 2 major features compared to the cmdutil.Factory:
// 1. provides a method to get a karmada clientset
// 2. provides a method to get a cmdutil.Factory for the member cluster
type Factory interface {
cmdutil.Factory

// KarmadaClientSet returns a karmada clientset
KarmadaClientSet() (karmadaclientset.Interface, error)

// FactoryForMemberCluster returns a cmdutil.Factory for the member cluster
FactoryForMemberCluster(clusterName string) (cmdutil.Factory, error)
}

var _ Factory = &factoryImpl{}

// factoryImpl is the implementation of Factory
type factoryImpl struct {
cmdutil.Factory

// kubeConfigFlags holds all the flags specificed by user.
// These flags will be inherited by the member cluster's client.
kubeConfigFlags *genericclioptions.ConfigFlags
}

// NewFactory returns a new factory
func NewFactory(kubeConfigFlags *genericclioptions.ConfigFlags) Factory {
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
f := &factoryImpl{
kubeConfigFlags: kubeConfigFlags,
Factory: cmdutil.NewFactory(matchVersionKubeConfigFlags),
}
return f
}

// KarmadaClientSet returns a karmada clientset
func (f *factoryImpl) KarmadaClientSet() (karmadaclientset.Interface, error) {
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
return karmadaclientset.NewForConfig(clientConfig)
}

// FactoryForMemberCluster returns a cmdutil.Factory for the member cluster
func (f *factoryImpl) FactoryForMemberCluster(clusterName string) (cmdutil.Factory, error) {
// Get client config of the karmada, and use it to create a cmdutil.Factory for the member cluster later.
clientConfig, err := f.ToRESTConfig()
if err != nil {
return nil, err
}
karmadaAPIServer := clientConfig.Host

// Check if the given cluster is joined to karmada
client, err := karmadaclientset.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
_, err = client.ClusterV1alpha1().Clusters().Get(context.TODO(), clusterName, metav1.GetOptions{})
if err != nil {
return nil, err
}

// Inherit all properties from the original flags specified by user except for the kube-apiserver address.
kubeConfigFlags := &genericclioptions.ConfigFlags{
CacheDir: f.kubeConfigFlags.CacheDir,
KubeConfig: f.kubeConfigFlags.KubeConfig,
ClusterName: f.kubeConfigFlags.ClusterName,
AuthInfoName: f.kubeConfigFlags.AuthInfoName,
Context: f.kubeConfigFlags.Context,
Namespace: f.kubeConfigFlags.Namespace,
Insecure: f.kubeConfigFlags.Insecure,
CertFile: f.kubeConfigFlags.CertFile,
KeyFile: f.kubeConfigFlags.KeyFile,
CAFile: f.kubeConfigFlags.CAFile,
BearerToken: f.kubeConfigFlags.BearerToken,
Impersonate: f.kubeConfigFlags.Impersonate,
ImpersonateUID: f.kubeConfigFlags.ImpersonateUID,
ImpersonateGroup: f.kubeConfigFlags.ImpersonateGroup,
Username: f.kubeConfigFlags.Username,
Password: f.kubeConfigFlags.Password,
Timeout: f.kubeConfigFlags.Timeout,
WrapConfigFn: f.kubeConfigFlags.WrapConfigFn,
}
// Override the kube-apiserver address.
memberAPIserver := karmadaAPIServer + fmt.Sprintf(proxyURL, clusterName)
kubeConfigFlags.APIServer = &memberAPIserver
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
return cmdutil.NewFactory(matchVersionKubeConfigFlags), nil
}

0 comments on commit fd1c0b2

Please sign in to comment.