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 }