diff --git a/Makefile b/Makefile index 8f18b74a..dbfb08ef 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ clean: cluster-ip: $(KUBECTL) config use-context $(HUB_KUBECONFIG_CONTEXT) --kubeconfig $(HUB_KUBECONFIG) - CLUSTER_IP?=$(shell $(KUBECTL) --kubeconfig $(HUB_KUBECONFIG) get svc kubernetes -n default -o jsonpath="{.spec.clusterIP}") + $(eval CLUSTER_IP?=$(shell $(KUBECTL) --kubeconfig $(HUB_KUBECONFIG) get svc kubernetes -n default -o jsonpath="{.spec.clusterIP}")) hub-kubeconfig-secret: cluster-ip $(KUBECTL) config use-context $(SPOKE_KUBECONFIG_CONTEXT) --kubeconfig $(SPOKE_KUBECONFIG) @@ -48,8 +48,10 @@ hub-kubeconfig-secret: cluster-ip $(KUBECTL) delete secret hub-kubeconfig-secret -n open-cluster-management-agent --ignore-not-found --kubeconfig $(SPOKE_KUBECONFIG) $(KUBECTL) config use-context $(HUB_KUBECONFIG_CONTEXT) --kubeconfig $(HUB_KUBECONFIG) $(KUBECTL) config view --flatten --minify --kubeconfig $(HUB_KUBECONFIG) > hub-kubeconfig -ifeq ($(HUB_KUBECONFIG), $(SPOKE_KUBECONFIG)) && ($(HUB_KUBECONFIG_CONTEXT), $(SPOKE_KUBECONFIG_CONTEXT)) +ifeq ($(HUB_KUBECONFIG), $(SPOKE_KUBECONFIG)) +ifeq ($(HUB_KUBECONFIG_CONTEXT), $(SPOKE_KUBECONFIG_CONTEXT)) $(KUBECTL) config set clusters.$(HUB_KUBECONFIG_CONTEXT).server https://$(CLUSTER_IP) --kubeconfig hub-kubeconfig +endif endif $(KUBECTL) config use-context $(SPOKE_KUBECONFIG_CONTEXT) --kubeconfig $(SPOKE_KUBECONFIG) $(KUBECTL) create secret generic hub-kubeconfig-secret --from-file=kubeconfig=hub-kubeconfig -n open-cluster-management-agent --kubeconfig $(SPOKE_KUBECONFIG) diff --git a/deploy/webhook/kustomization.yaml b/deploy/webhook/kustomization.yaml index a967ceac..24691009 100644 --- a/deploy/webhook/kustomization.yaml +++ b/deploy/webhook/kustomization.yaml @@ -1,6 +1,6 @@ # Adds namespace to all resources. -namespace: open-cluster-management +namespace: open-cluster-management-hub # Value of this field is prepended to the # names of all resources, e.g. a deployment named diff --git a/pkg/spoke/spokeagent.go b/pkg/spoke/spokeagent.go index aaeda26e..886e240d 100644 --- a/pkg/spoke/spokeagent.go +++ b/pkg/spoke/spokeagent.go @@ -2,6 +2,7 @@ package spoke import ( "context" + "fmt" "time" "open-cluster-management.io/work/pkg/helper" @@ -16,6 +17,7 @@ import ( apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" workclientset "open-cluster-management.io/api/client/work/clientset/versioned" @@ -24,10 +26,11 @@ import ( // WorkloadAgentOptions defines the flags for workload agent type WorkloadAgentOptions struct { - HubKubeconfigFile string - SpokeClusterName string - QPS float32 - Burst int + HubKubeconfigFile string + SpokeKubeconfigFile string + SpokeClusterName string + QPS float32 + Burst int } // NewWorkloadAgentOptions returns the flags with default value set @@ -43,6 +46,8 @@ func (o *WorkloadAgentOptions) AddFlags(cmd *cobra.Command) { flags := cmd.Flags() // This command only supports reading from config flags.StringVar(&o.HubKubeconfigFile, "hub-kubeconfig", o.HubKubeconfigFile, "Location of kubeconfig file to connect to hub cluster.") + flags.StringVar(&o.SpokeKubeconfigFile, "spoke-kubeconfig", o.SpokeKubeconfigFile, + "Location of kubeconfig file to connect to spoke cluster. If this is not set, will use '--kubeconfig' to build client to connect to the managed cluster.") flags.StringVar(&o.SpokeClusterName, "spoke-cluster-name", o.SpokeClusterName, "Name of spoke cluster.") flags.Float32Var(&o.QPS, "spoke-kube-api-qps", o.QPS, "QPS to use while talking with apiserver on spoke cluster.") flags.IntVar(&o.Burst, "spoke-kube-api-burst", o.Burst, "Burst to use while talking with apiserver on spoke cluster.") @@ -64,8 +69,13 @@ func (o *WorkloadAgentOptions) RunWorkloadAgent(ctx context.Context, controllerC // Only watch the cluster namespace on hub workInformerFactory := workinformers.NewSharedInformerFactoryWithOptions(hubWorkClient, 5*time.Minute, workinformers.WithNamespace(o.SpokeClusterName)) - // Build dynamic client and informer for spoke cluster - spokeRestConfig := controllerContext.KubeConfig + // load spoke client config and create spoke clients, + // the work agent may not running in the spoke/managed cluster. + spokeRestConfig, err := o.spokeKubeConfig(controllerContext) + if err != nil { + return err + } + spokeRestConfig.QPS = o.QPS spokeRestConfig.Burst = o.Burst spokeDynamicClient, err := dynamic.NewForConfig(spokeRestConfig) @@ -154,3 +164,16 @@ func (o *WorkloadAgentOptions) RunWorkloadAgent(ctx context.Context, controllerC <-ctx.Done() return nil } + +// spokeKubeConfig builds kubeconfig for the spoke/managed cluster +func (o *WorkloadAgentOptions) spokeKubeConfig(controllerContext *controllercmd.ControllerContext) (*rest.Config, error) { + if o.SpokeKubeconfigFile == "" { + return controllerContext.KubeConfig, nil + } + + spokeRestConfig, err := clientcmd.BuildConfigFromFlags("" /* leave masterurl as empty */, o.SpokeKubeconfigFile) + if err != nil { + return nil, fmt.Errorf("unable to load spoke kubeconfig from file %q: %w", o.SpokeKubeconfigFile, err) + } + return spokeRestConfig, nil +}