From 84f5fb7e6939d1756675fab117d3702edc271020 Mon Sep 17 00:00:00 2001 From: "Roland.Ma" Date: Fri, 25 Dec 2020 03:16:03 +0000 Subject: [PATCH] valid member cluster's workspaces when joining Signed-off-by: Roland.Ma --- pkg/controller/cluster/join.go | 87 ++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/pkg/controller/cluster/join.go b/pkg/controller/cluster/join.go index 294e81796..c8331fa7f 100644 --- a/pkg/controller/cluster/join.go +++ b/pkg/controller/cluster/join.go @@ -18,22 +18,29 @@ package cluster import ( "context" + "reflect" + "time" + "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" kubeclient "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/klog" - "reflect" + "kubesphere.io/kubesphere/pkg/apis/types/v1beta1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/kubefed/pkg/kubefedctl/util" - "time" + "k8s.io/apimachinery/pkg/runtime" + k8sscheme "k8s.io/client-go/kubernetes/scheme" + fedapis "sigs.k8s.io/kubefed/pkg/apis" fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" - genericclient "sigs.k8s.io/kubefed/pkg/client/generic" ) var ( @@ -53,11 +60,17 @@ var ( Verbs: []string{"get"}, }, } + localSchemeBuilder = runtime.SchemeBuilder{ + fedapis.AddToScheme, + k8sscheme.AddToScheme, + v1beta1.AddToScheme, + } ) const ( tokenKey = "token" serviceAccountSecretTimeout = 30 * time.Second + kubefedManagedSelector = "kubefed.io/managed=true" ) // joinClusterForNamespace registers a cluster with a KubeFed control @@ -78,8 +91,9 @@ func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedName klog.V(2).Infof("Failed to get joining cluster clientset: %v", err) return nil, err } - - client, err := genericclient.New(hostConfig) + scheme := runtime.NewScheme() + localSchemeBuilder.AddToScheme(scheme) + client, err := client.New(hostConfig, client.Options{Scheme: scheme}) if err != nil { klog.V(2).Infof("Failed to get kubefed clientset: %v", err) return nil, err @@ -116,7 +130,7 @@ func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedName disabledTLSValidations = append(disabledTLSValidations, fedv1b1.TLSAll) } - kubefedCluster, err := createKubeFedCluster(client, joiningClusterName, clusterConfig.Host, + kubefedCluster, err := createKubeFedCluster(clusterConfig, client, joiningClusterName, clusterConfig.Host, secret.Name, kubefedNamespace, clusterConfig.CAData, disabledTLSValidations, labels, dryRun, errorOnExisting) if err != nil { klog.V(2).Infof("Failed to create federated cluster resource: %v", err) @@ -150,7 +164,7 @@ func performPreflightChecks(clusterClientset kubeclient.Interface, name, hostClu // createKubeFedCluster creates a federated cluster resource that associates // the cluster and secret. -func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEndpoint, +func createKubeFedCluster(clusterConfig *rest.Config, client client.Client, joiningClusterName, apiEndpoint, secretName, kubefedNamespace string, caBundle []byte, disabledTLSValidations []fedv1b1.TLSValidation, labels map[string]string, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { fedCluster := &fedv1b1.KubeFedCluster{ @@ -174,7 +188,8 @@ func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEn } existingFedCluster := &fedv1b1.KubeFedCluster{} - err := client.Get(context.TODO(), existingFedCluster, kubefedNamespace, joiningClusterName) + key := types.NamespacedName{Namespace: kubefedNamespace, Name: joiningClusterName} + err := client.Get(context.TODO(), key, existingFedCluster) switch { case err != nil && !apierrors.IsNotFound(err): klog.V(2).Infof("Could not retrieve federated cluster %s due to %v", joiningClusterName, err) @@ -191,6 +206,14 @@ func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEn } return existingFedCluster, nil default: + + err = checkWorkspaces(clusterConfig, client, fedCluster) + + if err != nil { + klog.V(2).Infof("Validate federated cluster %s failed due to %v", fedCluster.Name, err) + return nil, err + } + err = client.Create(context.TODO(), fedCluster) if err != nil { klog.V(2).Infof("Could not create federated cluster %s due to %v", fedCluster.Name, err) @@ -734,3 +757,51 @@ func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Inte klog.V(2).Infof("Created secret in host cluster named: %s", v1SecretResult.Name) return v1SecretResult, caBundle, nil } + +func checkWorkspaces(clusterConfig *rest.Config, hostClient client.Client, cluster *fedv1b1.KubeFedCluster) error { + + tenantclient, err := v1alpha1.NewForConfig(clusterConfig) + if err != nil { + return err + } + + workspaces, err := tenantclient.Workspaces().List(metav1.ListOptions{LabelSelector: kubefedManagedSelector}) + if err != nil { + return err + } + + // Workspaces with the `kubefed.io/managed: true` label will be deleted if the FederatedWorkspace's Clusters don't include the cluster. + // The user needs to remove the label or delete the workspace manually. + for _, ws := range workspaces.Items { + fedWorkspace := &v1beta1.FederatedWorkspace{} + key := types.NamespacedName{Name: ws.Name} + err := hostClient.Get(context.TODO(), key, fedWorkspace) + if err != nil { + // Continue to check next workspace, when it's not exist in the host. + if apierrors.IsNotFound(err) { + continue + } + return err + } + if !containsCluster(fedWorkspace.Spec.Placement, cluster.Name) { + denied := errors.Errorf("The workspace %s is found in the target member cluster %s, which is conflict with the workspace on host", ws.Name, cluster.Name) + return denied + } + } + + return nil +} + +func containsCluster(placement v1beta1.GenericPlacementFields, str string) bool { + // Use selector if clusters are nil. But we ignore selector here. + if placement.Clusters == nil { + return true + } + + for _, s := range placement.Clusters { + if s.Name == str { + return true + } + } + return false +}