fix: policy rules cannot be updated during upgrade
Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
@@ -21,7 +21,6 @@ package namespace
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
@@ -30,6 +29,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
@@ -44,7 +44,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
"time"
|
||||
)
|
||||
@@ -56,13 +55,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
log = logf.Log.WithName("namespace-controller")
|
||||
defaultRoles = []rbac.Role{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "admin", Annotations: map[string]string{constants.DescriptionAnnotationKey: adminDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "operator", Annotations: map[string]string{constants.DescriptionAnnotationKey: operatorDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}},
|
||||
{Verbs: []string{"*"}, APIGroups: []string{"", "apps", "extensions", "batch", "logging.kubesphere.io", "monitoring.kubesphere.io", "iam.kubesphere.io", "resources.kubesphere.io", "autoscaling", "alerting.kubesphere.io", "app.k8s.io", "servicemesh.kubesphere.io", "operations.kubesphere.io"}, Resources: []string{"*"}}}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "viewer", Annotations: map[string]string{constants.DescriptionAnnotationKey: viewerDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}},
|
||||
}
|
||||
admin = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "admin", Annotations: map[string]string{constants.DescriptionAnnotationKey: adminDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}}
|
||||
operator = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "operator", Annotations: map[string]string{constants.DescriptionAnnotationKey: operatorDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}},
|
||||
{Verbs: []string{"*"}, APIGroups: []string{"", "apps", "extensions", "batch", "logging.kubesphere.io", "monitoring.kubesphere.io", "iam.kubesphere.io", "resources.kubesphere.io", "autoscaling", "alerting.kubesphere.io", "app.k8s.io", "servicemesh.kubesphere.io", "operations.kubesphere.io"}, Resources: []string{"*"}}}}
|
||||
viewer = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "viewer", Annotations: map[string]string{constants.DescriptionAnnotationKey: viewerDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}}
|
||||
defaultRoles = []rbac.Role{admin, operator, viewer}
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -162,27 +159,17 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
workspaceName := instance.Labels[constants.WorkspaceLabelKey]
|
||||
controlledByWorkspace, err := r.isControlledByWorkspace(instance)
|
||||
|
||||
// delete default role bindings
|
||||
if workspaceName == "" {
|
||||
adminBinding := &rbac.RoleBinding{}
|
||||
adminBinding.Name = "admin"
|
||||
adminBinding.Namespace = instance.Name
|
||||
log.Info("Deleting default role binding", "namespace", instance.Name, "name", adminBinding.Name)
|
||||
err := r.Delete(context.TODO(), adminBinding)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
viewerBinding := &rbac.RoleBinding{}
|
||||
viewerBinding.Name = "viewer"
|
||||
viewerBinding.Namespace = instance.Name
|
||||
log.Info("Deleting default role binding", "namespace", instance.Name, "name", viewerBinding.Name)
|
||||
err = r.Delete(context.TODO(), viewerBinding)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
if !controlledByWorkspace {
|
||||
|
||||
err = r.deleteRoleBindings(instance)
|
||||
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
if err = r.checkAndBindWorkspace(instance); err != nil {
|
||||
@@ -208,6 +195,18 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileNamespace) isControlledByWorkspace(namespace *corev1.Namespace) (bool, error) {
|
||||
|
||||
workspaceName := namespace.Labels[constants.WorkspaceLabelKey]
|
||||
|
||||
// without workspace label
|
||||
if workspaceName == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Create default roles
|
||||
func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) error {
|
||||
for _, role := range defaultRoles {
|
||||
@@ -217,14 +216,21 @@ func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) er
|
||||
if errors.IsNotFound(err) {
|
||||
role := role.DeepCopy()
|
||||
role.Namespace = namespace.Name
|
||||
log.Info("Creating default role", "namespace", namespace.Name, "role", role.Name)
|
||||
err = r.Create(context.TODO(), role)
|
||||
if err != nil {
|
||||
log.Info("Creating default role failed", "namespace", namespace.Name, "role", role.Name)
|
||||
klog.Errorf("creating role namespace: %s,role: %s,error: %s", namespace.Name, role.Name, err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Info("Get default role failed", "namespace", namespace.Name, "role", role.Name)
|
||||
klog.Errorf("get role namespace: %s,role: %s,error: %s", namespace.Name, role.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(found.Rules, role.Rules) {
|
||||
found.Rules = role.Rules
|
||||
err := r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
klog.Errorf("updating default role namespace: %s, role: %s,error: %s", namespace.Name, role.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -248,9 +254,9 @@ func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namesp
|
||||
}
|
||||
|
||||
adminBinding := &rbac.RoleBinding{}
|
||||
adminBinding.Name = "admin"
|
||||
adminBinding.Name = admin.Name
|
||||
adminBinding.Namespace = namespace.Name
|
||||
adminBinding.RoleRef = rbac.RoleRef{Name: "admin", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
|
||||
adminBinding.RoleRef = rbac.RoleRef{Name: admin.Name, APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
|
||||
adminBinding.Subjects = workspaceAdminBinding.Subjects
|
||||
|
||||
if creator.Name != "" {
|
||||
@@ -267,30 +273,33 @@ func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namesp
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: adminBinding.Name}, found)
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
log.Info("Creating default role binding", "namespace", namespace.Name, "name", adminBinding.Name)
|
||||
err = r.Create(context.TODO(), adminBinding)
|
||||
if err != nil {
|
||||
klog.Errorf("creating role binding namespace: %s,role binding: %s, error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
found = adminBinding
|
||||
} else if err != nil {
|
||||
klog.Errorf("get role binding namespace: %s,role binding: %s, error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(found.RoleRef, adminBinding.RoleRef) {
|
||||
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", adminBinding.Name)
|
||||
err = r.Delete(context.TODO(), found)
|
||||
if err != nil {
|
||||
klog.Errorf("deleting role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, adminBinding.Name)
|
||||
err = fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, adminBinding.Name)
|
||||
klog.Errorf("conflict role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(found.Subjects, adminBinding.Subjects) {
|
||||
found.Subjects = adminBinding.Subjects
|
||||
log.Info("Updating role binding", "namespace", namespace.Name, "name", adminBinding.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
klog.Errorf("updating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -304,17 +313,17 @@ func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namesp
|
||||
}
|
||||
|
||||
viewerBinding := &rbac.RoleBinding{}
|
||||
viewerBinding.Name = "viewer"
|
||||
viewerBinding.Name = viewer.Name
|
||||
viewerBinding.Namespace = namespace.Name
|
||||
viewerBinding.RoleRef = rbac.RoleRef{Name: "viewer", APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
|
||||
viewerBinding.RoleRef = rbac.RoleRef{Name: viewer.Name, APIGroup: "rbac.authorization.k8s.io", Kind: "Role"}
|
||||
viewerBinding.Subjects = workspaceViewerBinding.Subjects
|
||||
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: viewerBinding.Name}, found)
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
log.Info("Creating default role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
|
||||
err = r.Create(context.TODO(), viewerBinding)
|
||||
if err != nil {
|
||||
klog.Errorf("creating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
found = viewerBinding
|
||||
@@ -323,19 +332,21 @@ func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namesp
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(found.RoleRef, viewerBinding.RoleRef) {
|
||||
log.Info("Deleting conflict role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
|
||||
err = r.Delete(context.TODO(), found)
|
||||
if err != nil {
|
||||
klog.Errorf("deleting conflict role binding namespace: %s, role binding: %s, %s", namespace.Name, viewerBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, viewerBinding.Name)
|
||||
err = fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, viewerBinding.Name)
|
||||
klog.Errorf("conflict role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(found.Subjects, viewerBinding.Subjects) {
|
||||
found.Subjects = viewerBinding.Subjects
|
||||
log.Info("Updating role binding", "namespace", namespace.Name, "name", viewerBinding.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
klog.Errorf("updating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -360,8 +371,8 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace)
|
||||
|
||||
runtime := &openpitrix.RunTime{Name: namespace.Name, Zone: namespace.Name, Provider: "kubernetes", RuntimeCredential: cm.Data["config"]}
|
||||
|
||||
log.Info("Creating openpitrix runtime", "namespace", namespace.Name)
|
||||
if err := openpitrix.Client().CreateRuntime(runtime); err != nil {
|
||||
klog.Errorf("creating openpitrix runtime namespace: %s, error: %s", namespace.Name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -375,14 +386,14 @@ func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error {
|
||||
maxRetries := float64(3)
|
||||
for i := float64(0); i < maxRetries; i++ {
|
||||
time.Sleep(time.Duration(i*math.Pow(2, i)) * time.Second)
|
||||
log.Info("Deleting openpitrix runtime", "namespace", namespace.Name, "runtime", runtimeId)
|
||||
|
||||
err := openpitrix.Client().DeleteRuntime(runtimeId)
|
||||
|
||||
if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Error(err, fmt.Sprintf("Deleteing openpitrix runtime failed: %v times left", maxRetries-i-1))
|
||||
klog.Errorf("delete openpitrix runtime: %v times left, error: %s", maxRetries-i-1, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,20 +414,22 @@ func (r *ReconcileNamespace) checkAndBindWorkspace(namespace *corev1.Namespace)
|
||||
err := r.Get(context.TODO(), types.NamespacedName{Name: workspaceName}, workspace)
|
||||
|
||||
if err != nil {
|
||||
// skip if workspace not found
|
||||
if errors.IsNotFound(err) {
|
||||
log.Error(err, fmt.Sprintf("namespace %s bind workspace %s but not found", namespace.Name, workspaceName))
|
||||
return nil
|
||||
}
|
||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !metav1.IsControlledBy(namespace, workspace) {
|
||||
if err := controllerutil.SetControllerReference(workspace, namespace, r.scheme); err != nil {
|
||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
||||
return err
|
||||
}
|
||||
log.Info("Bind workspace", "namespace", namespace.Name, "workspace", workspaceName)
|
||||
err = r.Update(context.TODO(), namespace)
|
||||
if err != nil {
|
||||
klog.Errorf("bind workspace namespace: %s, workspace: %s, error: %s", namespace.Name, workspaceName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -431,23 +444,22 @@ func (r *ReconcileNamespace) checkAndCreateCephSecret(namespace *corev1.Namespac
|
||||
scList := &v1.StorageClassList{}
|
||||
err := r.List(context.TODO(), &client.ListOptions{}, scList)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
for _, sc := range scList.Items {
|
||||
if sc.Provisioner == "kubernetes.io/rbd" {
|
||||
log.Info("would create Ceph user secret in storage class %s at namespace %s", sc.GetName(), newNsName)
|
||||
if secretName, ok := sc.Parameters["userSecretName"]; ok {
|
||||
secret := &corev1.Secret{}
|
||||
r.Get(context.TODO(), types.NamespacedName{Namespace: core.NamespaceSystem, Name: secretName}, secret)
|
||||
err = r.Get(context.TODO(), types.NamespacedName{Namespace: core.NamespaceSystem, Name: secretName}, secret)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
log.Error(err, "cannot find secret in namespace %s, error: %s", core.NamespaceSystem, secretName)
|
||||
klog.Errorf("cannot find secret %s in namespace %s, error: %s", secretName, core.NamespaceSystem, err)
|
||||
continue
|
||||
}
|
||||
log.Error(err, fmt.Sprintf("failed to find secret in namespace %s", core.NamespaceSystem))
|
||||
klog.Errorf("failed to find secret in namespace %s, error: %s", core.NamespaceSystem, err)
|
||||
continue
|
||||
}
|
||||
glog.Infof("succeed to find secret %s in namespace %s", secret.GetName(), secret.GetNamespace())
|
||||
|
||||
newSecret := &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -466,15 +478,14 @@ func (r *ReconcileNamespace) checkAndCreateCephSecret(namespace *corev1.Namespac
|
||||
StringData: secret.StringData,
|
||||
Type: secret.Type,
|
||||
}
|
||||
log.Info(fmt.Sprintf("creating secret %s in namespace %s...", newSecret.GetName(), newSecret.GetNamespace()))
|
||||
|
||||
err = r.Create(context.TODO(), newSecret)
|
||||
if err != nil {
|
||||
log.Error(err, fmt.Sprintf("failed to create secret in namespace %s", newSecret.GetNamespace()))
|
||||
klog.Errorf("failed to create secret in namespace %s,error: %s", newSecret.GetNamespace(), err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Error(err, fmt.Sprintf("failed to find user secret name in storage class %s", sc.GetName()))
|
||||
klog.Errorf("failed to find user secret name in storage class %s,error: %s", sc.GetName(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,12 +503,12 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
log.V(2).Info("get router service failed", err)
|
||||
klog.V(6).Info("get router service failed", err)
|
||||
}
|
||||
|
||||
err = r.Delete(context.TODO(), &found)
|
||||
if err != nil {
|
||||
log.Error(err, "delete router failed")
|
||||
klog.Error(err, "delete router failed")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -508,16 +519,37 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
log.V(2).Info("get router deployment failed", err)
|
||||
klog.V(6).Info("get router deployment failed", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Delete(context.TODO(), &deploy)
|
||||
if err != nil {
|
||||
log.Error(err, "delete router deployment failed")
|
||||
klog.Error(err, "delete router deployment failed")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *ReconcileNamespace) deleteRoleBindings(namespace *corev1.Namespace) error {
|
||||
klog.V(6).Info("deleting role bindings namespace: ", namespace.Name)
|
||||
adminBinding := &rbac.RoleBinding{}
|
||||
adminBinding.Name = admin.Name
|
||||
adminBinding.Namespace = namespace.Name
|
||||
err := r.Delete(context.TODO(), adminBinding)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
klog.Errorf("deleting role binding namespace: %s, role binding: %s,error: %s", namespace.Name, adminBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
viewerBinding := &rbac.RoleBinding{}
|
||||
viewerBinding.Name = viewer.Name
|
||||
viewerBinding.Namespace = namespace.Name
|
||||
err = r.Delete(context.TODO(), viewerBinding)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
klog.Errorf("deleting role binding namespace: %s,role binding: %s,error: %s", namespace.Name, viewerBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user