valid member cluster's workspaces when joining
Signed-off-by: Roland.Ma <rolandma@yunify.com>
This commit is contained in:
@@ -18,22 +18,29 @@ package cluster
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
kubeclient "k8s.io/client-go/kubernetes"
|
kubeclient "k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/klog"
|
"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"
|
"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"
|
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
|
||||||
genericclient "sigs.k8s.io/kubefed/pkg/client/generic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -53,11 +60,17 @@ var (
|
|||||||
Verbs: []string{"get"},
|
Verbs: []string{"get"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
localSchemeBuilder = runtime.SchemeBuilder{
|
||||||
|
fedapis.AddToScheme,
|
||||||
|
k8sscheme.AddToScheme,
|
||||||
|
v1beta1.AddToScheme,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tokenKey = "token"
|
tokenKey = "token"
|
||||||
serviceAccountSecretTimeout = 30 * time.Second
|
serviceAccountSecretTimeout = 30 * time.Second
|
||||||
|
kubefedManagedSelector = "kubefed.io/managed=true"
|
||||||
)
|
)
|
||||||
|
|
||||||
// joinClusterForNamespace registers a cluster with a KubeFed control
|
// 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)
|
klog.V(2).Infof("Failed to get joining cluster clientset: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
client, err := genericclient.New(hostConfig)
|
localSchemeBuilder.AddToScheme(scheme)
|
||||||
|
client, err := client.New(hostConfig, client.Options{Scheme: scheme})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(2).Infof("Failed to get kubefed clientset: %v", err)
|
klog.V(2).Infof("Failed to get kubefed clientset: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -116,7 +130,7 @@ func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedName
|
|||||||
disabledTLSValidations = append(disabledTLSValidations, fedv1b1.TLSAll)
|
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)
|
secret.Name, kubefedNamespace, clusterConfig.CAData, disabledTLSValidations, labels, dryRun, errorOnExisting)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(2).Infof("Failed to create federated cluster resource: %v", err)
|
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
|
// createKubeFedCluster creates a federated cluster resource that associates
|
||||||
// the cluster and secret.
|
// 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,
|
secretName, kubefedNamespace string, caBundle []byte, disabledTLSValidations []fedv1b1.TLSValidation,
|
||||||
labels map[string]string, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
|
labels map[string]string, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) {
|
||||||
fedCluster := &fedv1b1.KubeFedCluster{
|
fedCluster := &fedv1b1.KubeFedCluster{
|
||||||
@@ -174,7 +188,8 @@ func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
existingFedCluster := &fedv1b1.KubeFedCluster{}
|
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 {
|
switch {
|
||||||
case err != nil && !apierrors.IsNotFound(err):
|
case err != nil && !apierrors.IsNotFound(err):
|
||||||
klog.V(2).Infof("Could not retrieve federated cluster %s due to %v", joiningClusterName, 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
|
return existingFedCluster, nil
|
||||||
default:
|
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)
|
err = client.Create(context.TODO(), fedCluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.V(2).Infof("Could not create federated cluster %s due to %v", fedCluster.Name, err)
|
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)
|
klog.V(2).Infof("Created secret in host cluster named: %s", v1SecretResult.Name)
|
||||||
return v1SecretResult, caBundle, nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user