From a908757cfb5f469162e974683b569b9cf56a3357 Mon Sep 17 00:00:00 2001 From: hongming Date: Thu, 22 Nov 2018 16:53:57 +0800 Subject: [PATCH] fix bug: rolebinding cannot delete --- pkg/apis/v1alpha/workspaces/workspaces.go | 2 +- pkg/constants/common.go | 5 + .../controllers/clusterrole_bindings.go | 8 +- pkg/models/controllers/namespaces.go | 92 ++++++++++++------- pkg/models/workspaces/workspaces.go | 60 ++++++------ 5 files changed, 102 insertions(+), 65 deletions(-) diff --git a/pkg/apis/v1alpha/workspaces/workspaces.go b/pkg/apis/v1alpha/workspaces/workspaces.go index 19ece09a8..99b257b26 100644 --- a/pkg/apis/v1alpha/workspaces/workspaces.go +++ b/pkg/apis/v1alpha/workspaces/workspaces.go @@ -55,7 +55,7 @@ func RoleHandler(req *restful.Request, resp *restful.Response) { workspaceName := req.PathParameter("name") roleName := req.PathParameter("role") - if !slice.ContainsString(workspaces.WorkSpaceRoles, roleName, nil) { + if !slice.ContainsString(constants.WorkSpaceRoles, roleName, nil) { resp.WriteHeaderAndEntity(http.StatusNotFound, constants.MessageResponse{Message: fmt.Sprintf("role %s not found", roleName)}) return } diff --git a/pkg/constants/common.go b/pkg/constants/common.go index 9e8b2be02..211be117c 100644 --- a/pkg/constants/common.go +++ b/pkg/constants/common.go @@ -44,6 +44,10 @@ const ( AccountAPIServerEnv = "ACCOUNT_API_SERVER" DevopsProxyTokenEnv = "DEVOPS_PROXY_TOKEN" OpenPitrixProxyTokenEnv = "OPENPITRIX_PROXY_TOKEN" + WorkspaceLabelKey = "kubesphere.io/workspace" + WorkspaceAdmin = "workspace-admin" + WorkspaceRegular = "workspace-regular" + WorkspaceViewer = "workspace-viewer" ) var ( @@ -51,6 +55,7 @@ var ( AccountAPIServer = "ks-account.kubesphere-system.svc" DevopsProxyToken = "" OpenPitrixProxyToken = "" + WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer} ) func init() { diff --git a/pkg/models/controllers/clusterrole_bindings.go b/pkg/models/controllers/clusterrole_bindings.go index c02857240..29784d2df 100644 --- a/pkg/models/controllers/clusterrole_bindings.go +++ b/pkg/models/controllers/clusterrole_bindings.go @@ -22,6 +22,8 @@ import ( "fmt" "regexp" + "strings" + "github.com/golang/glog" "github.com/pkg/errors" rbac "k8s.io/api/rbac/v1" @@ -29,6 +31,8 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" + + "kubesphere.io/kubesphere/pkg/constants" ) func (ctl *ClusterRoleBindingCtl) Name() string { @@ -43,14 +47,14 @@ func (ctl *ClusterRoleBindingCtl) sync(stopChan chan struct{}) { func (ctl *ClusterRoleBindingCtl) total() int { list, err := ctl.lister.List(labels.Everything()) if err != nil { - glog.Errorf("count %s falied, reason:%s", err, ctl.Name()) + glog.Errorf("count %s failed, reason:%s", ctl.Name(), err) return 0 } return len(list) } func (ctl *ClusterRoleBindingCtl) handleWorkspaceRoleChange(clusterRole *rbac.ClusterRoleBinding) { - if groups := regexp.MustCompile(`^system:(\S+):(admin|operator|viewer)$`).FindStringSubmatch(clusterRole.Name); len(groups) == 3 { + if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(clusterRole.Name); len(groups) == 3 { workspace := groups[1] go ctl.restNamespaceRoleBinding(workspace) } diff --git a/pkg/models/controllers/namespaces.go b/pkg/models/controllers/namespaces.go index bed8faabf..5863539d6 100644 --- a/pkg/models/controllers/namespaces.go +++ b/pkg/models/controllers/namespaces.go @@ -46,7 +46,7 @@ import ( const ( provider = "kubernetes" admin = "admin" - editor = "operator" + operator = "operator" viewer = "viewer" kubectlNamespace = constants.KubeSphereControlNamespace kubectlConfigKey = "config" @@ -133,13 +133,10 @@ func (ctl *NamespaceCtl) createOpRuntime(namespace string) ([]byte, error) { return makeHttpRequest("POST", url, string(body)) } -func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error { +func (ctl *NamespaceCtl) updateSystemRoleBindings(namespace *v1.Namespace) error { workspace := "" - creator := "" - if namespace.Annotations != nil { - creator = namespace.Annotations[creatorAnnotateKey] - } + if namespace.Labels != nil { workspace = namespace.Labels[workspaceLabelKey] } @@ -159,12 +156,8 @@ func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error adminBinding.Subjects = make([]rbac.Subject, 0) - if creator != "" { - adminBinding.Subjects = append(adminBinding.Subjects, rbac.Subject{Name: creator, Kind: rbac.UserKind}) - } - if workspace != "" { - workspaceAdmin, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:admin", workspace), metaV1.GetOptions{}) + workspaceAdmin, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, constants.WorkspaceAdmin), metaV1.GetOptions{}) if err != nil { return err } @@ -197,7 +190,7 @@ func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error viewerBinding.Subjects = make([]rbac.Subject, 0) if workspace != "" { - workspaceViewer, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:viewer", workspace), metaV1.GetOptions{}) + workspaceViewer, err := ctl.K8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, constants.WorkspaceViewer), metaV1.GetOptions{}) if err != nil { return err } @@ -217,24 +210,64 @@ func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error return nil } -func (ctl *NamespaceCtl) createDefaultRole(ns string) error { - adminRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: ns, Annotations: map[string]string{"creator": "system"}}, Rules: adminRules} - editorRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: editor, Namespace: ns, Annotations: map[string]string{"creator": "system"}}, Rules: editorRules} - viewerRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: viewer, Namespace: ns, Annotations: map[string]string{"creator": "system"}}, Rules: viewerRules} +func (ctl *NamespaceCtl) createDefaultRoleBinding(namespace *v1.Namespace) error { + creator := "" + if namespace.Annotations != nil { + creator = namespace.Annotations[creatorAnnotateKey] + } + // create once + if creator != "" { + creatorBindingName := fmt.Sprintf("%s-admin", creator) + creatorBinding, err := ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Get(creatorBindingName, metaV1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + creatorBinding = new(rbac.RoleBinding) + creatorBinding.Name = creatorBindingName + creatorBinding.Namespace = namespace.Name + creatorBinding.RoleRef = rbac.RoleRef{Kind: "Role", Name: admin} + } else { + return err + } + } - _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(adminRole) + creatorBinding.Subjects = []rbac.Subject{{Kind: rbac.UserKind, Name: creator}} + + if creatorBinding.ResourceVersion == "" { + _, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Create(creatorBinding) + } else { + _, err = ctl.K8sClient.RbacV1().RoleBindings(namespace.Name).Update(creatorBinding) + } + + if err != nil { + return err + } + } + + return nil +} + +func (ctl *NamespaceCtl) CreateDefaultRoleAndRoleBinding(namespace *v1.Namespace) error { + adminRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: adminRules} + operatorRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: operator, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: editorRules} + viewerRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: viewer, Namespace: namespace.Name, Annotations: map[string]string{creatorAnnotateKey: "system"}}, Rules: viewerRules} + + _, err := ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(adminRole) + + if err != nil && !errors.IsAlreadyExists(err) { + return err + } else if err == nil { + if err := ctl.createDefaultRoleBinding(namespace); err != nil { + glog.Warning("default role binding create failed", namespace.Name) + } + } + + _, err = ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(operatorRole) if err != nil && !errors.IsAlreadyExists(err) { return err } - _, err = ctl.K8sClient.RbacV1().Roles(ns).Create(editorRole) - - if err != nil && !errors.IsAlreadyExists(err) { - return err - } - - _, err = ctl.K8sClient.RbacV1().Roles(ns).Create(viewerRole) + _, err = ctl.K8sClient.RbacV1().Roles(namespace.Name).Create(viewerRole) if err != nil && !errors.IsAlreadyExists(err) { return err @@ -254,27 +287,22 @@ func (ctl *NamespaceCtl) createRoleAndRuntime(namespace *v1.Namespace) { componentsNamespaces := []string{constants.KubeSystemNamespace, constants.OpenPitrixNamespace, constants.IstioNamespace, constants.KubeSphereNamespace} if runtime == "" && !slice.ContainsString(componentsNamespaces, namespace.Name, nil) { - glog.Infoln("create runtime:", namespace.Name) _, runtimeCreateError := ctl.createOpRuntime(namespace.Name) - if runtimeCreateError != nil { glog.Error("runtime create error:", runtimeCreateError) } } if initTime == "" { - err := ctl.createDefaultRole(namespace.Name) - glog.Infoln("create default role:", namespace.Name) + err := ctl.CreateDefaultRoleAndRoleBinding(namespace) if err == nil { - err = ctl.createDefaultRoleBinding(namespace) - glog.Infoln("create default role binding:", namespace.Name) + err = ctl.updateSystemRoleBindings(namespace) if err != nil { - glog.Error("default role binding create error:", err) + glog.Error("role binding update error:", err) } } else { glog.Error("default role create error:", err) } - if err == nil { pathJson := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, initTimeAnnotateKey, time.Now().UTC().Format("2006-01-02T15:04:05Z")) _, err = ctl.K8sClient.CoreV1().Namespaces().Patch(namespace.Name, "application/strategic-merge-patch+json", []byte(pathJson)) diff --git a/pkg/models/workspaces/workspaces.go b/pkg/models/workspaces/workspaces.go index 8dd341242..5ab72e945 100644 --- a/pkg/models/workspaces/workspaces.go +++ b/pkg/models/workspaces/workspaces.go @@ -35,12 +35,6 @@ import ( ksErr "kubesphere.io/kubesphere/pkg/util/errors" ) -const ( - WorkspaceKey = "kubesphere.io/workspace" -) - -var WorkSpaceRoles = []string{"admin", "operator", "viewer"} - func UnBindDevopsProject(workspace string, devops string) error { db := client.NewSharedDBClient() defer db.Close() @@ -283,7 +277,7 @@ func DeleteNamespace(workspace string, namespaceName string) error { return err } if namespace.Labels != nil && namespace.Labels["kubesphere.io/workspace"] == workspace { - deletePolicy := meta_v1.DeletePropagationBackground + deletePolicy := meta_v1.DeletePropagationForeground return client.NewK8sClient().CoreV1().Namespaces().Delete(namespaceName, &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy}) } else { return errors.New("resource not found") @@ -347,7 +341,7 @@ func workspaceRoleRelease(workspace string) error { k8sClient := client.NewK8sClient() deletePolicy := meta_v1.DeletePropagationForeground - for _, role := range WorkSpaceRoles { + for _, role := range constants.WorkSpaceRoles { err := k8sClient.RbacV1().ClusterRoles().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy}) if err != nil && !apierrors.IsNotFound(err) { @@ -355,7 +349,7 @@ func workspaceRoleRelease(workspace string) error { } } - for _, role := range WorkSpaceRoles { + for _, role := range constants.WorkSpaceRoles { err := k8sClient.RbacV1().ClusterRoleBindings().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy}) if err != nil && !apierrors.IsNotFound(err) { @@ -516,7 +510,7 @@ func ListWorkspaceByUser(username string, keyword string) ([]*Workspace, error) } else { workspaceNames := make([]string, 0) for _, clusterRole := range clusterRoles { - if groups := regexp.MustCompile(`^system:(\S+):(admin|operator|viewer)$`).FindStringSubmatch(clusterRole.Name); len(groups) == 3 { + if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(clusterRole.Name); len(groups) == 3 { if !slice.ContainsString(workspaceNames, groups[1], nil) { workspaceNames = append(workspaceNames, groups[1]) } @@ -721,12 +715,18 @@ func CreateNamespace(namespace *core.Namespace) (*core.Namespace, error) { return nil, err } + if ctl, ok := controllers.ResourceControllers.Controllers[controllers.Namespaces]; ok { + if nsCtl, ok := ctl.(*controllers.NamespaceCtl); ok { + nsCtl.CreateDefaultRoleAndRoleBinding(ns) + } + } + return ns, nil } func Invite(workspaceName string, users []UserInvite) error { for _, user := range users { - if !slice.ContainsString(WorkSpaceRoles, user.Role, nil) { + if !slice.ContainsString(constants.WorkSpaceRoles, user.Role, nil) { return fmt.Errorf("role %s not exist", user.Role) } } @@ -807,7 +807,7 @@ func Roles(workspace *Workspace) ([]*v1.ClusterRole, error) { k8sClient := client.NewK8sClient() - for _, name := range WorkSpaceRoles { + for _, name := range constants.WorkSpaceRoles { role, err := k8sClient.RbacV1().ClusterRoles().Get(fmt.Sprintf("system:%s:%s", workspace.Name, name), meta_v1.GetOptions{}) if err != nil { @@ -859,7 +859,7 @@ func WorkspaceRoleInit(workspace *Workspace) error { k8sClient := client.NewK8sClient() admin := new(v1.ClusterRole) - admin.Name = fmt.Sprintf("system:%s:admin", workspace.Name) + admin.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceAdmin) admin.Kind = iam.ClusterRoleKind admin.Rules = []v1.PolicyRule{ { @@ -899,10 +899,10 @@ func WorkspaceRoleInit(workspace *Workspace) error { admin.Labels = map[string]string{"creator": "system"} - operator := new(v1.ClusterRole) - operator.Name = fmt.Sprintf("system:%s:operator", workspace.Name) - operator.Kind = iam.ClusterRoleKind - operator.Rules = []v1.PolicyRule{ + regular := new(v1.ClusterRole) + regular.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceRegular) + regular.Kind = iam.ClusterRoleKind + regular.Rules = []v1.PolicyRule{ { Verbs: []string{"get"}, APIGroups: []string{"kubesphere.io"}, @@ -948,10 +948,10 @@ func WorkspaceRoleInit(workspace *Workspace) error { }, } - operator.Labels = map[string]string{"creator": "system"} + regular.Labels = map[string]string{"creator": "system"} viewer := new(v1.ClusterRole) - viewer.Name = fmt.Sprintf("system:%s:viewer", workspace.Name) + viewer.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceViewer) viewer.Kind = iam.ClusterRoleKind viewer.Rules = []v1.PolicyRule{ { @@ -1011,7 +1011,7 @@ func WorkspaceRoleInit(workspace *Workspace) error { } } - _, err = k8sClient.RbacV1().ClusterRoles().Create(operator) + _, err = k8sClient.RbacV1().ClusterRoles().Create(regular) if err != nil { if !apierrors.IsAlreadyExists(err) { log.Println("cluster role create failed", viewer.Name, err) @@ -1019,14 +1019,14 @@ func WorkspaceRoleInit(workspace *Workspace) error { } } - operatorRoleBinding := new(v1.ClusterRoleBinding) - operatorRoleBinding.Name = operator.Name - operatorRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: operator.Name} - operatorRoleBinding.Subjects = make([]v1.Subject, 0) - _, err = k8sClient.RbacV1().ClusterRoleBindings().Create(operatorRoleBinding) + regularRoleBinding := new(v1.ClusterRoleBinding) + regularRoleBinding.Name = regular.Name + regularRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: regular.Name} + regularRoleBinding.Subjects = make([]v1.Subject, 0) + _, err = k8sClient.RbacV1().ClusterRoleBindings().Create(regularRoleBinding) if err != nil { if !apierrors.IsAlreadyExists(err) { - log.Println("cluster rolebinding create failed", operatorRoleBinding.Name, err) + log.Println("cluster rolebinding create failed", regularRoleBinding.Name, err) return err } } @@ -1057,7 +1057,7 @@ func WorkspaceRoleInit(workspace *Workspace) error { func unbindWorkspaceRole(workspace string, users []string) error { k8sClient := client.NewK8sClient() - for _, name := range WorkSpaceRoles { + for _, name := range constants.WorkSpaceRoles { roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, name), meta_v1.GetOptions{}) if err != nil { @@ -1137,7 +1137,7 @@ func CreateWorkspaceRoleBinding(workspace *Workspace, username string, role stri k8sClient := client.NewK8sClient() - for _, roleName := range WorkSpaceRoles { + for _, roleName := range constants.WorkSpaceRoles { roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace.Name, roleName), meta_v1.GetOptions{}) if err != nil { @@ -1215,7 +1215,7 @@ func GetOrgMembers(workspace string) ([]string, error) { } func GetOrgRoles(name string) ([]string, error) { - return []string{"admin", "operator", "user"}, nil + return constants.WorkSpaceRoles, nil } func WorkspaceNamespaces(workspaceName string) ([]string, error) { @@ -1348,7 +1348,7 @@ func GetAllOrgAndProjList() (map[string][]string, map[string]string, error) { var namespaceWorkspaceMap = make(map[string]string) for _, item := range nsList.Items { - ws, exist := item.Labels[WorkspaceKey] + ws, exist := item.Labels[constants.WorkspaceLabelKey] ns := item.Name if exist { if nsArray, exist := workspaceNamespaceMap[ws]; exist {