fix: resource lifecycle

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-07-09 15:12:20 +08:00
parent 7516019be5
commit ed8bb437e8
9 changed files with 335 additions and 123 deletions

View File

@@ -160,7 +160,6 @@ func addControllers(
fedWorkspaceCacheController, fedWorkspaceRoleCacheController, fedWorkspaceRoleBindingCacheController cache.Controller fedWorkspaceCacheController, fedWorkspaceRoleCacheController, fedWorkspaceRoleBindingCacheController cache.Controller
if multiClusterEnabled { if multiClusterEnabled {
fedUserClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedUserResource) fedUserClient, err := util.NewResourceClient(client.Config(), &iamv1alpha2.FedUserResource)
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
@@ -208,31 +207,42 @@ func addControllers(
} }
userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(), userController := user.NewController(client.Kubernetes(), client.KubeSphere(), client.Config(),
kubesphereInformer.Iam().V1alpha2().Users(), fedUserCache, fedUserCacheController, kubesphereInformer.Iam().V1alpha2().Users(),
fedUserCache, fedUserCacheController,
kubernetesInformer.Core().V1().ConfigMaps(), ldapClient, multiClusterEnabled) kubernetesInformer.Core().V1().ConfigMaps(), ldapClient, multiClusterEnabled)
csrController := certificatesigningrequest.NewController(client.Kubernetes(), kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(), csrController := certificatesigningrequest.NewController(client.Kubernetes(),
kubernetesInformer.Certificates().V1beta1().CertificateSigningRequests(),
kubernetesInformer.Core().V1().ConfigMaps(), client.Config()) kubernetesInformer.Core().V1().ConfigMaps(), client.Config())
clusterRoleBindingController := clusterrolebinding.NewController(client.Kubernetes(), clusterRoleBindingController := clusterrolebinding.NewController(client.Kubernetes(),
kubernetesInformer.Rbac().V1().ClusterRoleBindings(), kubernetesInformer.Apps().V1().Deployments(), kubernetesInformer.Rbac().V1().ClusterRoleBindings(),
kubernetesInformer.Core().V1().Pods(), kubesphereInformer.Iam().V1alpha2().Users()) kubernetesInformer.Apps().V1().Deployments(),
kubernetesInformer.Core().V1().Pods(),
kubesphereInformer.Iam().V1alpha2().Users())
globalRoleController := globalrole.NewController(client.Kubernetes(), client.KubeSphere(), globalRoleController := globalrole.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Iam().V1alpha2().GlobalRoles(), fedGlobalRoleCache, fedGlobalRoleCacheController) kubesphereInformer.Iam().V1alpha2().GlobalRoles(), fedGlobalRoleCache, fedGlobalRoleCacheController)
workspaceRoleController := workspacerole.NewController(client.Kubernetes(), client.KubeSphere(), workspaceRoleController := workspacerole.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(), fedWorkspaceRoleCache, fedWorkspaceRoleCacheController) kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(),
fedWorkspaceRoleCache, fedWorkspaceRoleCacheController,
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), multiClusterEnabled)
globalRoleBindingController := globalrolebinding.NewController(client.Kubernetes(), client.KubeSphere(), globalRoleBindingController := globalrolebinding.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Iam().V1alpha2().GlobalRoleBindings(), fedGlobalRoleBindingCache, fedGlobalRoleBindingCacheController, multiClusterEnabled) kubesphereInformer.Iam().V1alpha2().GlobalRoleBindings(),
fedGlobalRoleBindingCache, fedGlobalRoleBindingCacheController, multiClusterEnabled)
workspaceRoleBindingController := workspacerolebinding.NewController(client.Kubernetes(), client.KubeSphere(), workspaceRoleBindingController := workspacerolebinding.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Iam().V1alpha2().WorkspaceRoleBindings(), fedWorkspaceRoleBindingCache, fedWorkspaceRoleBindingCacheController) kubesphereInformer.Iam().V1alpha2().WorkspaceRoleBindings(),
fedWorkspaceRoleBindingCache, fedWorkspaceRoleBindingCacheController,
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), multiClusterEnabled)
workspaceTemplateController := workspacetemplate.NewController(client.Kubernetes(), client.KubeSphere(), workspaceTemplateController := workspacetemplate.NewController(client.Kubernetes(), client.KubeSphere(),
kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(), kubesphereInformer.Tenant().V1alpha1().Workspaces(), kubesphereInformer.Tenant().V1alpha2().WorkspaceTemplates(),
kubesphereInformer.Iam().V1alpha2().RoleBases(), kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(), kubesphereInformer.Tenant().V1alpha1().Workspaces(),
kubesphereInformer.Iam().V1alpha2().RoleBases(),
kubesphereInformer.Iam().V1alpha2().WorkspaceRoles(),
fedWorkspaceCache, fedWorkspaceCacheController, multiClusterEnabled) fedWorkspaceCache, fedWorkspaceCacheController, multiClusterEnabled)
var clusterController manager.Runnable var clusterController manager.Runnable
@@ -253,28 +263,32 @@ func addControllers(
} }
nsnpController = nsnetworkpolicy.NewNSNetworkPolicyController(client.Kubernetes(), nsnpController = nsnetworkpolicy.NewNSNetworkPolicyController(client.Kubernetes(),
client.KubeSphere().NetworkV1alpha1(), kubesphereInformer.Network().V1alpha1().NamespaceNetworkPolicies(), client.KubeSphere().NetworkV1alpha1(),
kubernetesInformer.Core().V1().Services(), kubernetesInformer.Core().V1().Nodes(), kubesphereInformer.Network().V1alpha1().NamespaceNetworkPolicies(),
kubernetesInformer.Core().V1().Services(),
kubernetesInformer.Core().V1().Nodes(),
kubesphereInformer.Tenant().V1alpha1().Workspaces(), kubesphereInformer.Tenant().V1alpha1().Workspaces(),
kubernetesInformer.Core().V1().Namespaces(), nsnpProvider) kubernetesInformer.Core().V1().Namespaces(), nsnpProvider)
} }
controllers := map[string]manager.Runnable{ controllers := map[string]manager.Runnable{
"virtualservice-controller": vsController, "virtualservice-controller": vsController,
"destinationrule-controller": drController, "destinationrule-controller": drController,
"application-controller": apController, "application-controller": apController,
"job-controller": jobController, "job-controller": jobController,
"s2ibinary-controller": s2iBinaryController, "s2ibinary-controller": s2iBinaryController,
"s2irun-controller": s2iRunController, "s2irun-controller": s2iRunController,
"storagecapability-controller": storageCapabilityController, "storagecapability-controller": storageCapabilityController,
"volumeexpansion-controller": volumeExpansionController, "volumeexpansion-controller": volumeExpansionController,
"user-controller": userController, "user-controller": userController,
"cluster-controller": clusterController, "cluster-controller": clusterController,
"nsnp-controller": nsnpController, "nsnp-controller": nsnpController,
"csr-controller": csrController, "csr-controller": csrController,
"clusterrolebinding-controller": clusterRoleBindingController, "clusterrolebinding-controller": clusterRoleBindingController,
"globalrolebinding-controller": globalRoleBindingController, "globalrolebinding-controller": globalRoleBindingController,
"workspacetemplate-controller": workspaceTemplateController, "workspacetemplate-controller": workspaceTemplateController,
"workspacerole-controller": workspaceRoleController,
"workspacerolebinding-controller": workspaceRoleBindingController,
} }
if devopsClient != nil { if devopsClient != nil {
@@ -285,8 +299,6 @@ func addControllers(
if multiClusterEnabled { if multiClusterEnabled {
controllers["globalrole-controller"] = globalRoleController controllers["globalrole-controller"] = globalRoleController
controllers["workspacerole-controller"] = workspaceRoleController
controllers["workspacerolebinding-controller"] = workspaceRoleBindingController
} }
for name, ctrl := range controllers { for name, ctrl := range controllers {

View File

@@ -97,11 +97,11 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
} }
klog.Info("Setting up event handlers") klog.Info("Setting up event handlers")
globalRoleBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ globalRoleBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueClusterRoleBinding, AddFunc: ctl.enqueueGlobalRoleBinding,
UpdateFunc: func(old, new interface{}) { UpdateFunc: func(old, new interface{}) {
ctl.enqueueClusterRoleBinding(new) ctl.enqueueGlobalRoleBinding(new)
}, },
DeleteFunc: ctl.enqueueClusterRoleBinding, DeleteFunc: ctl.enqueueGlobalRoleBinding,
}) })
return ctl return ctl
} }
@@ -138,7 +138,7 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
return nil return nil
} }
func (c *Controller) enqueueClusterRoleBinding(obj interface{}) { func (c *Controller) enqueueGlobalRoleBinding(obj interface{}) {
var key string var key string
var err error var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {

View File

@@ -189,7 +189,7 @@ func (r *ReconcileNamespace) bindWorkspace(namespace *corev1.Namespace) error {
// federated namespace not controlled by workspace // federated namespace not controlled by workspace
if namespace.Labels[constants.KubefedManagedLabel] != "true" && !metav1.IsControlledBy(namespace, workspace) { if namespace.Labels[constants.KubefedManagedLabel] != "true" && !metav1.IsControlledBy(namespace, workspace) {
namespace.OwnerReferences = removeWorkspaceOwnerReferences(namespace.OwnerReferences) namespace.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil { if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
klog.Error(err) klog.Error(err)
return err return err
@@ -204,17 +204,6 @@ func (r *ReconcileNamespace) bindWorkspace(namespace *corev1.Namespace) error {
return nil return nil
} }
func removeWorkspaceOwnerReferences(ownerReferences []metav1.OwnerReference) []metav1.OwnerReference {
for i := 0; i < len(ownerReferences); i++ {
owner := ownerReferences[i]
if owner.Kind == tenantv1alpha1.ResourceKindWorkspace {
ownerReferences = append(ownerReferences[:i], ownerReferences[i+1:]...)
i--
}
}
return ownerReferences
}
func (r *ReconcileNamespace) deleteRouter(namespace string) error { func (r *ReconcileNamespace) deleteRouter(namespace string) error {
routerName := constants.IngressControllerPrefix + namespace routerName := constants.IngressControllerPrefix + namespace
@@ -296,34 +285,62 @@ func (r *ReconcileNamespace) initRoles(namespace *corev1.Namespace) error {
return nil return nil
} }
func (r *ReconcileNamespace) resetNamespaceOwner(namespace *corev1.Namespace) error {
namespace = namespace.DeepCopy()
delete(namespace.Annotations, constants.CreatorAnnotationKey)
err := r.Update(context.Background(), namespace)
klog.V(4).Infof("update namespace after creator has been deleted")
return err
}
func (r *ReconcileNamespace) initCreatorRoleBinding(namespace *corev1.Namespace) error { func (r *ReconcileNamespace) initCreatorRoleBinding(namespace *corev1.Namespace) error {
if creator := namespace.Annotations[constants.CreatorAnnotationKey]; creator != "" { creator := namespace.Annotations[constants.CreatorAnnotationKey]
creatorRoleBinding := &rbacv1.RoleBinding{ if creator == "" {
ObjectMeta: metav1.ObjectMeta{ return nil
Name: fmt.Sprintf("%s-%s", creator, iamv1alpha2.NamespaceAdmin), }
Namespace: namespace.Name,
}, var user iamv1alpha2.User
RoleRef: rbacv1.RoleRef{ err := r.Get(context.Background(), types.NamespacedName{Name: creator}, &user)
if err != nil {
// skip if user has been deleted
if errors.IsNotFound(err) {
return r.resetNamespaceOwner(namespace)
}
klog.Error(err)
return err
}
// skip if user has been deleted
if !user.DeletionTimestamp.IsZero() {
return r.resetNamespaceOwner(namespace)
}
creatorRoleBinding := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", creator, iamv1alpha2.NamespaceAdmin),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: creator},
Namespace: namespace.Name,
},
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.GroupName,
Kind: iamv1alpha2.ResourceKindRole,
Name: iamv1alpha2.NamespaceAdmin,
},
Subjects: []rbacv1.Subject{
{
Name: creator,
Kind: iamv1alpha2.ResourceKindUser,
APIGroup: rbacv1.GroupName, APIGroup: rbacv1.GroupName,
Kind: iamv1alpha2.ResourceKindRole,
Name: iamv1alpha2.NamespaceAdmin,
},
Subjects: []rbacv1.Subject{
{
Name: creator,
Kind: iamv1alpha2.ResourceKindUser,
APIGroup: rbacv1.GroupName,
},
}, },
},
}
err = r.Client.Create(context.Background(), creatorRoleBinding)
if err != nil {
if errors.IsAlreadyExists(err) {
return nil
} }
err := r.Client.Create(context.Background(), creatorRoleBinding) klog.Error(err)
if err != nil { return err
if errors.IsAlreadyExists(err) {
return nil
}
klog.Error(err)
return err
}
} }
return nil return nil

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "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/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@@ -39,8 +40,8 @@ import (
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
userinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/kubeconfig" "kubesphere.io/kubesphere/pkg/models/kubeconfig"
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap" ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
@@ -63,8 +64,8 @@ type Controller struct {
k8sClient kubernetes.Interface k8sClient kubernetes.Interface
ksClient kubesphere.Interface ksClient kubesphere.Interface
kubeconfig kubeconfig.Interface kubeconfig kubeconfig.Interface
userInformer userinformer.UserInformer userInformer iamv1alpha2informers.UserInformer
userLister userlister.UserLister userLister iamv1alpha2listers.UserLister
userSynced cache.InformerSynced userSynced cache.InformerSynced
cmSynced cache.InformerSynced cmSynced cache.InformerSynced
fedUserCache cache.Store fedUserCache cache.Store
@@ -83,8 +84,10 @@ type Controller struct {
} }
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface,
config *rest.Config, userInformer userinformer.UserInformer, fedUserCache cache.Store, fedUserController cache.Controller, config *rest.Config, userInformer iamv1alpha2informers.UserInformer,
configMapInformer corev1informers.ConfigMapInformer, ldapClient ldapclient.Interface, multiClusterEnabled bool) *Controller { fedUserCache cache.Store, fedUserController cache.Controller,
configMapInformer corev1informers.ConfigMapInformer,
ldapClient ldapclient.Interface, multiClusterEnabled bool) *Controller {
// Create event broadcaster // Create event broadcaster
// Add sample-controller types to the default Kubernetes Scheme so Events can be // Add sample-controller types to the default Kubernetes Scheme so Events can be
// logged for sample-controller types. // logged for sample-controller types.
@@ -266,6 +269,11 @@ func (c *Controller) reconcile(key string) error {
return err return err
} }
if err = c.deleteRoleBindings(user); err != nil {
klog.Error(err)
return err
}
// remove our finalizer from the list and update it. // remove our finalizer from the list and update it.
user.Finalizers = sliceutil.RemoveString(user.ObjectMeta.Finalizers, func(item string) bool { user.Finalizers = sliceutil.RemoveString(user.ObjectMeta.Finalizers, func(item string) bool {
return item == finalizer return item == finalizer
@@ -483,6 +491,46 @@ func (c *Controller) ldapSync(user *iamv1alpha2.User) error {
} }
} }
func (c *Controller) deleteRoleBindings(user *iamv1alpha2.User) error {
listOptions := metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{iamv1alpha2.UserReferenceLabel: user.Name}).String(),
}
deleteOptions := metav1.NewDeleteOptions(0)
if err := c.ksClient.IamV1alpha2().GlobalRoleBindings().
DeleteCollection(deleteOptions, listOptions); err != nil {
klog.Error(err)
return err
}
if err := c.ksClient.IamV1alpha2().WorkspaceRoleBindings().
DeleteCollection(deleteOptions, listOptions); err != nil {
klog.Error(err)
return err
}
if err := c.k8sClient.RbacV1().ClusterRoleBindings().
DeleteCollection(deleteOptions, listOptions); err != nil {
klog.Error(err)
return err
}
if result, err := c.k8sClient.CoreV1().Namespaces().List(metav1.ListOptions{}); err != nil {
klog.Error(err)
return err
} else {
for _, namespace := range result.Items {
if err := c.k8sClient.RbacV1().RoleBindings(namespace.Name).
DeleteCollection(deleteOptions, listOptions); err != nil {
klog.Error(err)
return err
}
}
}
return nil
}
func encrypt(password string) (string, error) { func encrypt(password string) (string, error) {
// when user is already mapped to another identity, password is empty by default // when user is already mapped to another identity, password is empty by default
// unable to log in directly until password reset // unable to log in directly until password reset

View File

@@ -94,7 +94,11 @@ func (f *fixture) newController() (*Controller, ksinformers.SharedInformerFactor
} }
} }
c := NewController(f.k8sclient, f.ksclient, nil, ksinformers.Iam().V1alpha2().Users(), nil, nil, k8sinformers.Core().V1().ConfigMaps(), ldapClient, false) c := NewController(f.k8sclient, f.ksclient, nil,
ksinformers.Iam().V1alpha2().Users(),
nil, nil,
k8sinformers.Core().V1().ConfigMaps(),
ldapClient, false)
c.userSynced = alwaysReady c.userSynced = alwaysReady
c.recorder = &record.FakeRecorder{} c.recorder = &record.FakeRecorder{}
@@ -209,9 +213,7 @@ func checkAction(expected, actual core.Action, t *testing.T) {
func filterInformerActions(actions []core.Action) []core.Action { func filterInformerActions(actions []core.Action) []core.Action {
var ret []core.Action var ret []core.Action
for _, action := range actions { for _, action := range actions {
if action.Matches("list", "users") || if !action.Matches("update", "users") {
action.Matches("list", "configmaps") ||
action.Matches("watch", "users") {
continue continue
} }
ret = append(ret, action) ret = append(ret, action)

View File

@@ -36,7 +36,9 @@ import (
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
tenantv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha2"
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
tenantv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"reflect" "reflect"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -52,13 +54,18 @@ const (
) )
type Controller struct { type Controller struct {
scheme *runtime.Scheme
k8sClient kubernetes.Interface k8sClient kubernetes.Interface
ksClient kubesphere.Interface ksClient kubesphere.Interface
workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer
workspaceRoleLister iamv1alpha2listers.WorkspaceRoleLister workspaceRoleLister iamv1alpha2listers.WorkspaceRoleLister
workspaceRoleSynced cache.InformerSynced workspaceRoleSynced cache.InformerSynced
workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer
workspaceTemplateLister tenantv1alpha2listers.WorkspaceTemplateLister
workspaceTemplateSynced cache.InformerSynced
fedWorkspaceRoleCache cache.Store fedWorkspaceRoleCache cache.Store
fedWorkspaceRoleCacheController cache.Controller fedWorkspaceRoleCacheController cache.Controller
multiClusterEnabled bool
// workqueue is a rate limited work queue. This is used to queue work to be // workqueue is a rate limited work queue. This is used to queue work to be
// processed instead of performing it as soon as a change happens. This // processed instead of performing it as soon as a change happens. This
// means we can ensure we only process a fixed amount of resources at a // means we can ensure we only process a fixed amount of resources at a
@@ -71,7 +78,7 @@ type Controller struct {
} }
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer, func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleInformer iamv1alpha2informers.WorkspaceRoleInformer,
fedWorkspaceRoleCache cache.Store, fedWorkspaceRoleCacheController cache.Controller) *Controller { fedWorkspaceRoleCache cache.Store, fedWorkspaceRoleCacheController cache.Controller, workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer, multiClusterEnabled bool) *Controller {
// Create event broadcaster // Create event broadcaster
// Add sample-controller types to the default Kubernetes Scheme so Events can be // Add sample-controller types to the default Kubernetes Scheme so Events can be
// logged for sample-controller types. // logged for sample-controller types.
@@ -89,6 +96,10 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
workspaceRoleSynced: workspaceRoleInformer.Informer().HasSynced, workspaceRoleSynced: workspaceRoleInformer.Informer().HasSynced,
fedWorkspaceRoleCache: fedWorkspaceRoleCache, fedWorkspaceRoleCache: fedWorkspaceRoleCache,
fedWorkspaceRoleCacheController: fedWorkspaceRoleCacheController, fedWorkspaceRoleCacheController: fedWorkspaceRoleCacheController,
workspaceTemplateInformer: workspaceTemplateInformer,
workspaceTemplateLister: workspaceTemplateInformer.Lister(),
workspaceTemplateSynced: workspaceTemplateInformer.Informer().HasSynced,
multiClusterEnabled: multiClusterEnabled,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRole"), workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRole"),
recorder: recorder, recorder: recorder,
} }
@@ -113,7 +124,13 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
// Wait for the caches to be synced before starting workers // Wait for the caches to be synced before starting workers
klog.Info("Waiting for informer caches to sync") klog.Info("Waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.workspaceRoleSynced, c.fedWorkspaceRoleCacheController.HasSynced); !ok { synced := make([]cache.InformerSynced, 0)
synced = append(synced, c.workspaceRoleSynced, c.workspaceTemplateSynced)
if c.multiClusterEnabled {
synced = append(synced, c.fedWorkspaceRoleCacheController.HasSynced)
}
if ok := cache.WaitForCacheSync(stopCh); !ok {
return fmt.Errorf("failed to wait for caches to sync") return fmt.Errorf("failed to wait for caches to sync")
} }
@@ -214,11 +231,18 @@ func (c *Controller) reconcile(key string) error {
return err return err
} }
if err = c.multiClusterSync(workspaceRole); err != nil { if err = c.bindWorkspace(workspaceRole); err != nil {
klog.Error(err) klog.Error(err)
return err return err
} }
if c.multiClusterEnabled {
if err = c.multiClusterSync(workspaceRole); err != nil {
klog.Error(err)
return err
}
}
c.recorder.Event(workspaceRole, corev1.EventTypeNormal, successSynced, messageResourceSynced) c.recorder.Event(workspaceRole, corev1.EventTypeNormal, successSynced, messageResourceSynced)
return nil return nil
} }
@@ -227,6 +251,40 @@ func (c *Controller) Start(stopCh <-chan struct{}) error {
return c.Run(4, stopCh) return c.Run(4, stopCh)
} }
func (c *Controller) bindWorkspace(workspaceRole *iamv1alpha2.WorkspaceRole) error {
workspaceName := workspaceRole.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return nil
}
workspace, err := c.workspaceTemplateLister.Get(workspaceName)
if err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
return err
}
if !metav1.IsControlledBy(workspaceRole, workspace) {
workspaceRole.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, workspaceRole, scheme.Scheme); err != nil {
klog.Error(err)
return err
}
_, err = c.ksClient.IamV1alpha2().WorkspaceRoles().Update(workspaceRole)
if err != nil {
klog.Error(err)
return err
}
}
return nil
}
func (c *Controller) multiClusterSync(workspaceRole *iamv1alpha2.WorkspaceRole) error { func (c *Controller) multiClusterSync(workspaceRole *iamv1alpha2.WorkspaceRole) error {
if err := c.ensureNotControlledByKubefed(workspaceRole); err != nil { if err := c.ensureNotControlledByKubefed(workspaceRole); err != nil {

View File

@@ -37,7 +37,9 @@ import (
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" iamv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2"
tenantv1alpha2informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha2"
iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" iamv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2"
tenantv1alpha2listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"reflect" "reflect"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -60,6 +62,10 @@ type Controller struct {
workspaceRoleBindingSynced cache.InformerSynced workspaceRoleBindingSynced cache.InformerSynced
fedWorkspaceRoleBindingCache cache.Store fedWorkspaceRoleBindingCache cache.Store
fedWorkspaceRoleBindingCacheController cache.Controller fedWorkspaceRoleBindingCacheController cache.Controller
workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer
workspaceTemplateLister tenantv1alpha2listers.WorkspaceTemplateLister
workspaceTemplateSynced cache.InformerSynced
multiClusterEnabled bool
// workqueue is a rate limited work queue. This is used to queue work to be // workqueue is a rate limited work queue. This is used to queue work to be
// processed instead of performing it as soon as a change happens. This // processed instead of performing it as soon as a change happens. This
// means we can ensure we only process a fixed amount of resources at a // means we can ensure we only process a fixed amount of resources at a
@@ -72,7 +78,7 @@ type Controller struct {
} }
func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleBindingInformer iamv1alpha2informers.WorkspaceRoleBindingInformer, func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface, workspaceRoleBindingInformer iamv1alpha2informers.WorkspaceRoleBindingInformer,
fedWorkspaceRoleBindingCache cache.Store, fedWorkspaceRoleBindingCacheController cache.Controller) *Controller { fedWorkspaceRoleBindingCache cache.Store, fedWorkspaceRoleBindingCacheController cache.Controller, workspaceTemplateInformer tenantv1alpha2informers.WorkspaceTemplateInformer, multiClusterEnabled bool) *Controller {
// Create event broadcaster // Create event broadcaster
// Add sample-controller types to the default Kubernetes Scheme so Events can be // Add sample-controller types to the default Kubernetes Scheme so Events can be
// logged for sample-controller types. // logged for sample-controller types.
@@ -90,16 +96,20 @@ func NewController(k8sClient kubernetes.Interface, ksClient kubesphere.Interface
workspaceRoleBindingSynced: workspaceRoleBindingInformer.Informer().HasSynced, workspaceRoleBindingSynced: workspaceRoleBindingInformer.Informer().HasSynced,
fedWorkspaceRoleBindingCache: fedWorkspaceRoleBindingCache, fedWorkspaceRoleBindingCache: fedWorkspaceRoleBindingCache,
fedWorkspaceRoleBindingCacheController: fedWorkspaceRoleBindingCacheController, fedWorkspaceRoleBindingCacheController: fedWorkspaceRoleBindingCacheController,
workspaceTemplateInformer: workspaceTemplateInformer,
workspaceTemplateLister: workspaceTemplateInformer.Lister(),
workspaceTemplateSynced: workspaceTemplateInformer.Informer().HasSynced,
multiClusterEnabled: multiClusterEnabled,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRoleBinding"), workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceRoleBinding"),
recorder: recorder, recorder: recorder,
} }
klog.Info("Setting up event handlers") klog.Info("Setting up event handlers")
workspaceRoleBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ workspaceRoleBindingInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: ctl.enqueueClusterRoleBinding, AddFunc: ctl.enqueueWorkspaceRoleBinding,
UpdateFunc: func(old, new interface{}) { UpdateFunc: func(old, new interface{}) {
ctl.enqueueClusterRoleBinding(new) ctl.enqueueWorkspaceRoleBinding(new)
}, },
DeleteFunc: ctl.enqueueClusterRoleBinding, DeleteFunc: ctl.enqueueWorkspaceRoleBinding,
}) })
return ctl return ctl
} }
@@ -114,7 +124,13 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
// Wait for the caches to be synced before starting workers // Wait for the caches to be synced before starting workers
klog.Info("Waiting for informer caches to sync") klog.Info("Waiting for informer caches to sync")
if ok := cache.WaitForCacheSync(stopCh, c.workspaceRoleBindingSynced, c.fedWorkspaceRoleBindingCacheController.HasSynced); !ok { synced := make([]cache.InformerSynced, 0)
synced = append(synced, c.workspaceRoleBindingSynced, c.workspaceTemplateSynced)
if c.multiClusterEnabled {
synced = append(synced, c.fedWorkspaceRoleBindingCacheController.HasSynced)
}
if ok := cache.WaitForCacheSync(stopCh, synced...); !ok {
return fmt.Errorf("failed to wait for caches to sync") return fmt.Errorf("failed to wait for caches to sync")
} }
@@ -130,7 +146,7 @@ func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
return nil return nil
} }
func (c *Controller) enqueueClusterRoleBinding(obj interface{}) { func (c *Controller) enqueueWorkspaceRoleBinding(obj interface{}) {
var key string var key string
var err error var err error
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
@@ -215,11 +231,18 @@ func (c *Controller) reconcile(key string) error {
return err return err
} }
if err = c.multiClusterSync(workspaceRoleBinding); err != nil { if err = c.bindWorkspace(workspaceRoleBinding); err != nil {
klog.Error(err) klog.Error(err)
return err return err
} }
if c.multiClusterEnabled {
if err = c.multiClusterSync(workspaceRoleBinding); err != nil {
klog.Error(err)
return err
}
}
c.recorder.Event(workspaceRoleBinding, corev1.EventTypeNormal, successSynced, messageResourceSynced) c.recorder.Event(workspaceRoleBinding, corev1.EventTypeNormal, successSynced, messageResourceSynced)
return nil return nil
} }
@@ -228,6 +251,40 @@ func (c *Controller) Start(stopCh <-chan struct{}) error {
return c.Run(4, stopCh) return c.Run(4, stopCh)
} }
func (c *Controller) bindWorkspace(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
workspaceName := workspaceRoleBinding.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return nil
}
workspace, err := c.workspaceTemplateLister.Get(workspaceName)
if err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
return err
}
if !metav1.IsControlledBy(workspaceRoleBinding, workspace) {
workspaceRoleBinding.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, workspaceRoleBinding, scheme.Scheme); err != nil {
klog.Error(err)
return err
}
_, err = c.ksClient.IamV1alpha2().WorkspaceRoleBindings().Update(workspaceRoleBinding)
if err != nil {
klog.Error(err)
return err
}
}
return nil
}
func (c *Controller) multiClusterSync(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error { func (c *Controller) multiClusterSync(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
if err := c.ensureNotControlledByKubefed(workspaceRoleBinding); err != nil { if err := c.ensureNotControlledByKubefed(workspaceRoleBinding); err != nil {

View File

@@ -477,37 +477,64 @@ func (r *Controller) initRoles(workspace *tenantv1alpha2.WorkspaceTemplate) erro
return nil return nil
} }
func (r *Controller) resetWorkspaceOwner(workspace *tenantv1alpha2.WorkspaceTemplate) error {
workspace = workspace.DeepCopy()
workspace.Spec.Template.Spec.Manager = ""
_, err := r.ksClient.TenantV1alpha2().WorkspaceTemplates().Update(workspace)
klog.V(4).Infof("update workspace after manager has been deleted")
return err
}
func (r *Controller) initManagerRoleBinding(workspace *tenantv1alpha2.WorkspaceTemplate) error { func (r *Controller) initManagerRoleBinding(workspace *tenantv1alpha2.WorkspaceTemplate) error {
if manager := workspace.Spec.Template.Spec.Manager; manager != "" { manager := workspace.Spec.Template.Spec.Manager
if manager == "" {
return nil
}
workspaceAdminRoleName := fmt.Sprintf(iamv1alpha2.WorkspaceAdminFormat, workspace.Name) user, err := r.ksClient.IamV1alpha2().Users().Get(manager, metav1.GetOptions{})
if err != nil {
// skip if user has been deleted
if errors.IsNotFound(err) {
return r.resetWorkspaceOwner(workspace)
}
klog.Error(err)
return err
}
managerRoleBinding := &iamv1alpha2.WorkspaceRoleBinding{ // skip if user has been deleted
ObjectMeta: metav1.ObjectMeta{ if !user.DeletionTimestamp.IsZero() {
Name: fmt.Sprintf("%s-%s", manager, workspaceAdminRoleName), return r.resetWorkspaceOwner(workspace)
Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: workspace.Name}, }
workspaceAdminRoleName := fmt.Sprintf(iamv1alpha2.WorkspaceAdminFormat, workspace.Name)
managerRoleBinding := &iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", manager, workspaceAdminRoleName),
Labels: map[string]string{
tenantv1alpha1.WorkspaceLabel: workspace.Name,
iamv1alpha2.UserReferenceLabel: manager,
}, },
RoleRef: rbacv1.RoleRef{ },
APIGroup: iamv1alpha2.SchemeGroupVersion.Group, RoleRef: rbacv1.RoleRef{
Kind: iamv1alpha2.ResourceKindWorkspaceRole, APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
Name: workspaceAdminRoleName, Kind: iamv1alpha2.ResourceKindWorkspaceRole,
}, Name: workspaceAdminRoleName,
Subjects: []rbacv1.Subject{ },
{ Subjects: []rbacv1.Subject{
Name: manager, {
Kind: iamv1alpha2.ResourceKindUser, Name: manager,
APIGroup: rbacv1.GroupName, Kind: iamv1alpha2.ResourceKindUser,
}, APIGroup: rbacv1.GroupName,
}, },
},
}
_, err = r.ksClient.IamV1alpha2().WorkspaceRoleBindings().Create(managerRoleBinding)
if err != nil {
if errors.IsAlreadyExists(err) {
return nil
} }
_, err := r.ksClient.IamV1alpha2().WorkspaceRoleBindings().Create(managerRoleBinding) klog.Error(err)
if err != nil { return err
if errors.IsAlreadyExists(err) {
return nil
}
klog.Error(err)
return err
}
} }
return nil return nil

View File

@@ -28,12 +28,3 @@ func IsControlledBy(reference []metav1.OwnerReference, kind string, name string)
} }
return false return false
} }
func GetControlledWorkspace(reference []metav1.OwnerReference) string {
for _, ref := range reference {
if ref.Kind == "Workspace" {
return ref.Name
}
}
return ""
}