diff --git a/pkg/apis/v1alpha/iam/iam_handler.go b/pkg/apis/v1alpha/iam/iam_handler.go index 38ca6c0ac..38a9e7aba 100644 --- a/pkg/apis/v1alpha/iam/iam_handler.go +++ b/pkg/apis/v1alpha/iam/iam_handler.go @@ -63,7 +63,7 @@ func userRolesHandler(req *restful.Request, resp *restful.Response) { username := req.PathParameter("username") - roles, err := iam.GetRoles(username) + roles, err := iam.GetRoles("", username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -206,7 +206,7 @@ func clusterRoleRulesHandler(req *restful.Request, resp *restful.Response) { var rules []iam.Rule if name == "" { - rules = iam.ClusterRoleRuleGroup + rules = iam.ClusterRoleRuleMapping } else { var err error rules, err = iam.GetClusterRoleRules(name) @@ -227,7 +227,7 @@ func roleRulesHandler(req *restful.Request, resp *restful.Response) { var rules []iam.Rule if namespace == "" && name == "" { - rules = iam.RoleRuleGroup + rules = iam.RoleRuleMapping } else { var err error rules, err = iam.GetRoleRules(namespace, name) diff --git a/pkg/apis/v1alpha/monitoring/monitor_handler.go b/pkg/apis/v1alpha/monitoring/monitor_handler.go old mode 100644 new mode 100755 index dc39c12b7..59725ee39 --- a/pkg/apis/v1alpha/monitoring/monitor_handler.go +++ b/pkg/apis/v1alpha/monitoring/monitor_handler.go @@ -58,6 +58,18 @@ func (u MonitorResource) monitorWorkload(request *restful.Request, response *res } } +// merge multiple metric: all-devops, all-roles, all-projects...this api is designed for admin +func (u MonitorResource) monitorWorkspaceUserInfo(request *restful.Request, response *restful.Response) { + res := metrics.MonitorWorkspaceUserInfo(request) + response.WriteAsJson(res) +} + +// merge multiple metric: devops, roles, projects... +func (u MonitorResource) monitorWorkspaceResourceLevelMetrics(request *restful.Request, response *restful.Response) { + res := metrics.MonitorWorkspaceResourceLevelMetrics(request) + response.WriteAsJson(res) +} + func (u MonitorResource) monitorWorkspacePodLevelMetrics(request *restful.Request, response *restful.Response) { res := metrics.MonitorAllMetrics(request) response.WriteAsJson(res) @@ -223,4 +235,29 @@ func Register(ws *restful.WebService, subPath string) { Metadata(restfulspec.KeyOpenAPITags, tags)). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON) + + ws.Route(ws.GET(subPath+"/workspaces/{workspace_name}/pods").To(u.monitorWorkspacePodLevelMetrics). + Filter(route.RouteLogging). + Doc("monitor specific workspace level metrics"). + Param(ws.PathParameter("workspace_name", "workspace name").DataType("string").Required(true)). + Param(ws.QueryParameter("namespaces_filter", "namespaces filter").DataType("string").Required(false).DefaultValue("k.*")). + Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false).DefaultValue("tenant_memory_utilisation_wo_cache")). + Metadata(restfulspec.KeyOpenAPITags, tags)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET(subPath+"/workspaces/{workspace_name}").To(u.monitorWorkspaceResourceLevelMetrics). + Filter(route.RouteLogging). + Doc("monitor specific workspace level metrics"). + Param(ws.PathParameter("workspace_name", "workspace name").DataType("string").Required(true)). + Metadata(restfulspec.KeyOpenAPITags, tags)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET(subPath+"/workspaces").To(u.monitorWorkspaceUserInfo). + Filter(route.RouteLogging). + Doc("monitor specific workspace level metrics"). + Metadata(restfulspec.KeyOpenAPITags, tags)). + Consumes(restful.MIME_JSON, restful.MIME_XML). + Produces(restful.MIME_JSON) } diff --git a/pkg/apis/v1alpha/workspaces/workspaces.go b/pkg/apis/v1alpha/workspaces/workspaces.go index 7be20b7e4..24ccec2f7 100644 --- a/pkg/apis/v1alpha/workspaces/workspaces.go +++ b/pkg/apis/v1alpha/workspaces/workspaces.go @@ -16,18 +16,22 @@ import ( "kubesphere.io/kubesphere/pkg/models/workspaces" ) +const UserNameHeader = "X-Token-Username" + func Register(ws *restful.WebService, subPath string) { - ws.Route(ws.GET(subPath).To(WorkspaceListHandler)) + + ws.Route(ws.GET(subPath).To(UserWorkspaceListHandler)) ws.Route(ws.POST(subPath).To(WorkspaceCreateHandler)) ws.Route(ws.DELETE(subPath + "/{name}").To(DeleteWorkspaceHandler)) ws.Route(ws.GET(subPath + "/{name}").To(WorkspaceDetailHandler)) ws.Route(ws.PUT(subPath + "/{name}").To(WorkspaceEditHandler)) - ws.Route(ws.GET(subPath + "/{name}/namespaces").To(NamespaceHandler)) + ws.Route(ws.GET(subPath + "/{workspace}/namespaces").To(UserNamespaceListHandler)) ws.Route(ws.POST(subPath + "/{name}/namespaces").To(NamespaceCreateHandler)) ws.Route(ws.DELETE(subPath + "/{name}/namespaces/{namespace}").To(NamespaceDeleteHandler)) ws.Route(ws.GET(subPath + "/{name}/devops").To(DevOpsProjectHandler)) ws.Route(ws.POST(subPath + "/{name}/devops").To(DevOpsProjectCreateHandler)) ws.Route(ws.DELETE(subPath + "/{name}/devops/{id}").To(DevOpsProjectDeleteHandler)) + ws.Route(ws.GET(subPath + "/{name}/members").To(MembersHandler)) ws.Route(ws.GET(subPath + "/{name}/members/{member}").To(MemberHandler)) ws.Route(ws.GET(subPath + "/{name}/roles").To(RolesHandler)) @@ -107,6 +111,7 @@ func MemberHandler(req *restful.Request, resp *restful.Response) { } namespaces, err := workspaces.Namespaces(workspace) + if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return @@ -169,14 +174,9 @@ func MembersRemoveHandler(req *restful.Request, resp *restful.Response) { func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) { namespace := req.PathParameter("namespace") workspace := req.PathParameter("name") - force := req.QueryParameter("force") - err := workspaces.UnBindNamespace(workspace, namespace) + //force := req.QueryParameter("force") - if err != nil && force != "true" { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) - return - } - err = workspaces.DeleteNamespace(namespace) + err := workspaces.DeleteNamespace(workspace, namespace) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -190,7 +190,7 @@ func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) { devops := req.PathParameter("id") workspace := req.PathParameter("name") force := req.QueryParameter("force") - username := req.HeaderParameter("X-Token-Username") + username := req.HeaderParameter(UserNameHeader) err := workspaces.UnBindDevopsProject(workspace, devops) @@ -212,7 +212,7 @@ func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) { func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) { workspace := req.PathParameter("name") - username := req.HeaderParameter("X-Token-Username") + username := req.HeaderParameter(UserNameHeader) var devops workspaces.DevopsProject @@ -248,7 +248,7 @@ func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) { func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) { workspace := req.PathParameter("name") - username := req.HeaderParameter("X-Token-Username") + username := req.HeaderParameter(UserNameHeader) namespace := &v1.Namespace{} @@ -266,6 +266,12 @@ func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) { namespace.Annotations["creator"] = username namespace.Annotations["workspace"] = workspace + if namespace.Labels == nil { + namespace.Labels = make(map[string]string, 0) + } + + namespace.Labels["kubesphere.io/workspace"] = workspace + namespace, err = workspaces.CreateNamespace(namespace) if err != nil { @@ -273,14 +279,6 @@ func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) { return } - err = workspaces.BindingNamespace(workspace, namespace.Name) - - if err != nil { - workspaces.DeleteNamespace(namespace.Name) - resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()}) - return - } - resp.WriteEntity(namespace) } @@ -298,22 +296,9 @@ func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) { resp.WriteEntity(devOpsProjects) } -func NamespaceHandler(req *restful.Request, resp *restful.Response) { - - workspace := req.PathParameter("name") - - namespaces, err := workspaces.Namespaces(workspace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) - return - } - - resp.WriteEntity(namespaces) -} func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) { var workspace workspaces.Workspace - username := req.HeaderParameter("X-Token-Username") + username := req.HeaderParameter(UserNameHeader) err := req.ReadEntity(&workspace) if err != nil { resp.WriteHeaderAndEntity(http.StatusBadRequest, constants.MessageResponse{Message: err.Error()}) @@ -327,7 +312,11 @@ func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) { workspace.Path = workspace.Name workspace.Members = nil - workspace.Creator = username + if workspace.Admin != "" { + workspace.Creator = workspace.Admin + } else { + workspace.Creator = username + } created, err := workspaces.Create(&workspace) @@ -411,15 +400,12 @@ func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) { resp.WriteEntity(workspace) } -func WorkspaceListHandler(req *restful.Request, resp *restful.Response) { +// List all workspaces for the current user +func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) { - var names []string + username := req.HeaderParameter(UserNameHeader) - if query := req.QueryParameter("name"); query != "" { - names = strings.Split(query, ",") - } - - list, err := workspaces.List(names) + list, err := workspaces.ListByUser(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -428,3 +414,18 @@ func WorkspaceListHandler(req *restful.Request, resp *restful.Response) { resp.WriteEntity(list) } + +func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) { + + username := req.HeaderParameter(UserNameHeader) + workspaceName := req.PathParameter("workspace") + + namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) + return + } + + resp.WriteEntity(namespaces) +} diff --git a/pkg/models/controllers/clusterrole_bindings.go b/pkg/models/controllers/clusterrole_bindings.go new file mode 100644 index 000000000..89bfd789f --- /dev/null +++ b/pkg/models/controllers/clusterrole_bindings.go @@ -0,0 +1,62 @@ +/* +Copyright 2018 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "time" + + "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" +) + +func (ctl *ClusterRoleBindingCtl) Name() string { + return ctl.CommonAttribute.Name +} + +func (ctl *ClusterRoleBindingCtl) sync(stopChan chan struct{}) { + ctl.initListerAndInformer() + ctl.informer.Run(stopChan) +} + +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()) + return 0 + } + return len(list) +} + +func (ctl *ClusterRoleBindingCtl) initListerAndInformer() { + informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle) + ctl.lister = informerFactory.Rbac().V1().ClusterRoleBindings().Lister() + ctl.informer = informerFactory.Rbac().V1().ClusterRoleBindings().Informer() +} + +func (ctl *ClusterRoleBindingCtl) CountWithConditions(conditions string) int { + return 0 +} + +func (ctl *ClusterRoleBindingCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) { + return 0, nil, errors.New("not implement") +} + +func (ctl *ClusterRoleBindingCtl) Lister() interface{} { + return ctl.lister +} diff --git a/pkg/models/controllers/clusterroles.go b/pkg/models/controllers/clusterroles.go index ed5a47e5c..6e742637c 100644 --- a/pkg/models/controllers/clusterroles.go +++ b/pkg/models/controllers/clusterroles.go @@ -74,7 +74,9 @@ func (ctl *ClusterRoleCtl) sync(stopChan chan struct{}) { for _, item := range list { obj := ctl.generateObject(*item) if obj != nil { - db.Create(obj) + if err := db.Create(obj).Error; err != nil { + glog.Error("cluster roles sync error", err) + } } } @@ -111,14 +113,18 @@ func (ctl *ClusterRoleCtl) initListerAndInformer() { object := obj.(*v1.ClusterRole) mysqlObject := ctl.generateObject(*object) if mysqlObject != nil { - db.Create(mysqlObject) + if err := db.Create(mysqlObject).Error; err != nil { + glog.Error("cluster roles sync error", err) + } } }, UpdateFunc: func(old, new interface{}) { object := new.(*v1.ClusterRole) mysqlObject := ctl.generateObject(*object) if mysqlObject != nil { - db.Save(mysqlObject) + if err := db.Save(mysqlObject).Error; err != nil { + glog.Error("cluster roles update error", err) + } } }, DeleteFunc: func(obj interface{}) { diff --git a/pkg/models/controllers/namespaces.go b/pkg/models/controllers/namespaces.go index a3ecf902c..88355264b 100644 --- a/pkg/models/controllers/namespaces.go +++ b/pkg/models/controllers/namespaces.go @@ -147,9 +147,9 @@ func (ctl *NamespaceCtl) createDefaultRoleBinding(ns, user string) error { } func (ctl *NamespaceCtl) createDefaultRole(ns string) error { - adminRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: ns}, Rules: adminRules} - editorRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: editor, Namespace: ns}, Rules: editorRules} - viewerRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: viewer, Namespace: ns}, Rules: viewerRules} + 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} _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(adminRole) diff --git a/pkg/models/controllers/role_bindings.go b/pkg/models/controllers/role_bindings.go new file mode 100644 index 000000000..3de5dafda --- /dev/null +++ b/pkg/models/controllers/role_bindings.go @@ -0,0 +1,64 @@ +/* +Copyright 2018 The KubeSphere Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "time" + + "github.com/golang/glog" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" +) + +func (ctl *RoleBindingCtl) Name() string { + return ctl.CommonAttribute.Name +} + +func (ctl *RoleBindingCtl) sync(stopChan chan struct{}) { + ctl.initListerAndInformer() + ctl.informer.Run(stopChan) +} + +func (ctl *RoleBindingCtl) total() int { + list, err := ctl.lister.List(labels.Everything()) + if err != nil { + glog.Errorf("count %s falied, reason:%s", err, ctl.Name()) + return 0 + } + return len(list) +} + +func (ctl *RoleBindingCtl) initListerAndInformer() { + + informerFactory := informers.NewSharedInformerFactory(ctl.K8sClient, time.Second*resyncCircle) + + ctl.lister = informerFactory.Rbac().V1().RoleBindings().Lister() + ctl.informer = informerFactory.Rbac().V1().RoleBindings().Informer() +} + +func (ctl *RoleBindingCtl) CountWithConditions(conditions string) int { + return 0 +} + +func (ctl *RoleBindingCtl) ListWithConditions(conditions string, paging *Paging, order string) (int, interface{}, error) { + return 0, nil, errors.New("not implement") +} + +func (ctl *RoleBindingCtl) Lister() interface{} { + return ctl.lister +} diff --git a/pkg/models/controllers/run.go b/pkg/models/controllers/run.go index 9659d65af..f65d8e950 100644 --- a/pkg/models/controllers/run.go +++ b/pkg/models/controllers/run.go @@ -37,7 +37,7 @@ type resourceControllers struct { var ResourceControllers resourceControllers -func (rec *resourceControllers) runContoller(name string, stopChan chan struct{}, wg *sync.WaitGroup) { +func (rec *resourceControllers) runController(name string, stopChan chan struct{}, wg *sync.WaitGroup) { var ctl Controller attr := CommonAttribute{DB: client.NewDBClient(), K8sClient: rec.k8sClient, stopChan: stopChan, aliveChan: make(chan struct{}), Name: name} @@ -78,6 +78,10 @@ func (rec *resourceControllers) runContoller(name string, stopChan chan struct{} ctl = &ConfigMapCtl{CommonAttribute: attr} case Secrets: ctl = &SecretCtl{CommonAttribute: attr} + case ClusterRoleBindings: + ctl = &ClusterRoleBindingCtl{CommonAttribute: attr} + case RoleBindings: + ctl = &RoleBindingCtl{CommonAttribute: attr} default: return } @@ -116,9 +120,9 @@ func Run(stopChan chan struct{}, wg *sync.WaitGroup) { ResourceControllers = resourceControllers{k8sClient: k8sClient, Controllers: make(map[string]Controller)} for _, item := range []string{Deployments, Statefulsets, Daemonsets, PersistentVolumeClaim, Pods, Services, - Ingresses, Roles, ClusterRoles, Namespaces, StorageClasses, Jobs, Cronjobs, Nodes, Replicasets, + Ingresses, Roles, RoleBindings, ClusterRoles, ClusterRoleBindings, Namespaces, StorageClasses, Jobs, Cronjobs, Nodes, Replicasets, ControllerRevisions, ConfigMaps, Secrets} { - ResourceControllers.runContoller(item, stopChan, wg) + ResourceControllers.runController(item, stopChan, wg) } go dbHealthCheck(client.NewDBClient()) @@ -131,7 +135,7 @@ func Run(stopChan chan struct{}, wg *sync.WaitGroup) { case _, isClose := <-controller.chanAlive(): if !isClose { glog.Errorf("controller %s have stopped, restart it", ctlName) - ResourceControllers.runContoller(ctlName, stopChan, wg) + ResourceControllers.runController(ctlName, stopChan, wg) } default: time.Sleep(3 * time.Second) diff --git a/pkg/models/controllers/types.go b/pkg/models/controllers/types.go index 7ccd95b11..03f1fd5d5 100644 --- a/pkg/models/controllers/types.go +++ b/pkg/models/controllers/types.go @@ -59,7 +59,9 @@ const ( Ingresses = "ingresses" PersistentVolumeClaim = "persistent-volume-claims" Roles = "roles" + RoleBindings = "role-bindings" ClusterRoles = "cluster-roles" + ClusterRoleBindings = "cluster-role-bindings" Services = "services" StorageClasses = "storage-classes" Applications = "applications" @@ -464,6 +466,17 @@ type ClusterRoleCtl struct { CommonAttribute } +type ClusterRoleBindingCtl struct { + lister rbacV1.ClusterRoleBindingLister + informer cache.SharedIndexInformer + CommonAttribute +} +type RoleBindingCtl struct { + lister rbacV1.RoleBindingLister + informer cache.SharedIndexInformer + CommonAttribute +} + type JobCtl struct { lister batchv1.JobLister informer cache.SharedIndexInformer diff --git a/pkg/models/iam/iam.go b/pkg/models/iam/iam.go index d0c6b7603..e14235374 100644 --- a/pkg/models/iam/iam.go +++ b/pkg/models/iam/iam.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "log" "net/http" "strings" @@ -12,10 +11,12 @@ import ( "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubernetes/pkg/util/slice" + "k8s.io/apimachinery/pkg/labels" + v12 "k8s.io/client-go/listers/rbac/v1" "kubesphere.io/kubesphere/pkg/client" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/controllers" ksErr "kubesphere.io/kubesphere/pkg/util/errors" ) @@ -133,7 +134,7 @@ func WorkspaceRoleRules(workspace string, roleName string) (*v1.ClusterRole, []R rule := Rule{Name: WorkspaceRoleRuleMapping[i].Name} rule.Actions = make([]Action, 0) for j := 0; j < len(WorkspaceRoleRuleMapping[i].Actions); j++ { - if actionValidate(role.Rules, WorkspaceRoleRuleMapping[i].Actions[j]) { + if rulesMatchesAction(role.Rules, WorkspaceRoleRuleMapping[i].Actions[j]) { rule.Actions = append(rule.Actions, WorkspaceRoleRuleMapping[i].Actions[j]) } } @@ -161,18 +162,22 @@ func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespac } if requiredRule.Size() == 0 { - if ruleValidate(clusterRules, v1.PolicyRule{ - Verbs: []string{"get", "list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, + if RulesMatchesRequired(clusterRules, v1.PolicyRule{ + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces/namespaces"}, }) { return true, nil, nil } - } else if ruleValidate(clusterRules, requiredRule) { - return true, nil, nil + } else { + + if RulesMatchesRequired(clusterRules, requiredRule) { + return true, nil, nil + } + } - roles, err := GetRoles(username) + roles, err := GetRoles("", username) if err != nil { return false, nil, err @@ -192,7 +197,7 @@ func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespac namespaces = make([]string, 0) for namespace, rules := range rulesMapping { - if requiredRule.Size() == 0 || ruleValidate(rules, requiredRule) { + if requiredRule.Size() == 0 || RulesMatchesRequired(rules, requiredRule) { namespaces = append(namespaces, namespace) } } @@ -309,22 +314,24 @@ func GetClusterRole(name string) (*v1.ClusterRole, error) { return role, nil } -func GetRoles(username string) ([]v1.Role, error) { - k8s := client.NewK8sClient() +func GetRoles(namespace string, username string) ([]v1.Role, error) { + roleBindingLister := controllers.ResourceControllers.Controllers[controllers.RoleBindings].Lister().(v12.RoleBindingLister) + roleLister := controllers.ResourceControllers.Controllers[controllers.Roles].Lister().(v12.RoleLister) + clusterRoleLister := controllers.ResourceControllers.Controllers[controllers.ClusterRoles].Lister().(v12.ClusterRoleLister) - roleBindings, err := k8s.RbacV1().RoleBindings("").List(meta_v1.ListOptions{}) + roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything()) if err != nil { return nil, err } roles := make([]v1.Role, 0) - for _, roleBinding := range roleBindings.Items { + for _, roleBinding := range roleBindings { for _, subject := range roleBinding.Subjects { if subject.Kind == v1.UserKind && subject.Name == username { if roleBinding.RoleRef.Kind == ClusterRoleKind { - clusterRole, err := k8s.RbacV1().ClusterRoles().Get(roleBinding.RoleRef.Name, meta_v1.GetOptions{}) + clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name) if err == nil { var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules} role.Namespace = roleBinding.Namespace @@ -339,7 +346,7 @@ func GetRoles(username string) ([]v1.Role, error) { } else { if subject.Kind == v1.UserKind && subject.Name == username { - rule, err := k8s.RbacV1().Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name, meta_v1.GetOptions{}) + rule, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name) if err == nil { roles = append(roles, *rule) break @@ -361,10 +368,12 @@ func GetRoles(username string) ([]v1.Role, error) { return roles, nil } +// Get cluster roles by username func GetClusterRoles(username string) ([]v1.ClusterRole, error) { - k8s := client.NewK8sClient() - - clusterRoleBindings, err := k8s.RbacV1().ClusterRoleBindings().List(meta_v1.ListOptions{}) + //TODO fix NPE + clusterRoleBindingLister := controllers.ResourceControllers.Controllers[controllers.ClusterRoleBindings].Lister().(v12.ClusterRoleBindingLister) + clusterRoleLister := controllers.ResourceControllers.Controllers[controllers.ClusterRoles].Lister().(v12.ClusterRoleLister) + clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything()) if err != nil { return nil, err @@ -372,27 +381,24 @@ func GetClusterRoles(username string) ([]v1.ClusterRole, error) { roles := make([]v1.ClusterRole, 0) - for _, roleBinding := range clusterRoleBindings.Items { + for _, roleBinding := range clusterRoleBindings { for _, subject := range roleBinding.Subjects { if subject.Kind == v1.UserKind && subject.Name == username { if roleBinding.RoleRef.Kind == ClusterRoleKind { - role, err := k8s.RbacV1().ClusterRoles().Get(roleBinding.RoleRef.Name, meta_v1.GetOptions{}) + role, err := clusterRoleLister.Get(roleBinding.RoleRef.Name) if err == nil { if role.Annotations == nil { role.Annotations = make(map[string]string, 0) } - role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = roleBinding.Name - if roleBinding.Annotations != nil && roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == roleBinding.RoleRef.Name { role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true" } - roles = append(roles, *role) break } else if apierrors.IsNotFound(err) { - log.Println(err) + glog.Warning(err) break } else { return nil, err @@ -405,80 +411,80 @@ func GetClusterRoles(username string) ([]v1.ClusterRole, error) { return roles, nil } -func ruleValidate(rules []v1.PolicyRule, rule v1.PolicyRule) bool { +//func RuleValidate(rules []v1.PolicyRule, rule v1.PolicyRule) bool { +// +// for _, apiGroup := range rule.APIGroups { +// if len(rule.NonResourceURLs) == 0 { +// for _, resource := range rule.Resources { +// +// //if len(Rule.ResourceNames) == 0 { +// +// for _, verb := range rule.Verbs { +// if !verbValidate(rules, apiGroup, "", resource, "", verb) { +// return false +// } +// } +// +// //} else { +// // for _, resourceName := range Rule.ResourceNames { +// // for _, verb := range Rule.Verbs { +// // if !verbValidate(rules, apiGroup, "", resource, resourceName, verb) { +// // return false +// // } +// // } +// // } +// //} +// } +// } else { +// for _, nonResourceURL := range rule.NonResourceURLs { +// for _, verb := range rule.Verbs { +// if !verbValidate(rules, apiGroup, nonResourceURL, "", "", verb) { +// return false +// } +// } +// } +// } +// } +// return true +//} - for _, apiGroup := range rule.APIGroups { - if len(rule.NonResourceURLs) == 0 { - for _, resource := range rule.Resources { - - //if len(Rule.ResourceNames) == 0 { - - for _, verb := range rule.Verbs { - if !verbValidate(rules, apiGroup, "", resource, "", verb) { - return false - } - } - - //} else { - // for _, resourceName := range Rule.ResourceNames { - // for _, verb := range Rule.Verbs { - // if !verbValidate(rules, apiGroup, "", resource, resourceName, verb) { - // return false - // } - // } - // } - //} - } - } else { - for _, nonResourceURL := range rule.NonResourceURLs { - for _, verb := range rule.Verbs { - if !verbValidate(rules, apiGroup, nonResourceURL, "", "", verb) { - return false - } - } - } - } - } - return true -} - -func verbValidate(rules []v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, resourceName string, verb string) bool { - for _, rule := range rules { - - if nonResourceURL == "" { - if slice.ContainsString(rule.APIGroups, apiGroup, nil) || - slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) { - if slice.ContainsString(rule.Verbs, verb, nil) || - slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { - if slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { - return true - } else if slice.ContainsString(rule.Resources, resource, nil) { - if len(rule.ResourceNames) > 0 { - if slice.ContainsString(rule.ResourceNames, resourceName, nil) { - return true - } - } else if resourceName == "" { - return true - } - } - } - } - - } else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) || - slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) { - if slice.ContainsString(rule.Verbs, verb, nil) || - slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { - return true - } - } - } - return false -} +//func verbValidate(rules []v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, resourceName string, verb string) bool { +// for _, rule := range rules { +// +// if nonResourceURL == "" { +// if slice.ContainsString(rule.APIGroups, apiGroup, nil) || +// slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) { +// if slice.ContainsString(rule.Verbs, verb, nil) || +// slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { +// if slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { +// return true +// } else if slice.ContainsString(rule.Resources, resource, nil) { +// if len(rule.ResourceNames) > 0 { +// if slice.ContainsString(rule.ResourceNames, resourceName, nil) { +// return true +// } +// } else if resourceName == "" { +// return true +// } +// } +// } +// } +// +// } else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) || +// slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) { +// if slice.ContainsString(rule.Verbs, verb, nil) || +// slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { +// return true +// } +// } +// } +// return false +//} func GetUserRules(username string) (map[string][]Rule, error) { items := make(map[string][]Rule, 0) - userRoles, err := GetRoles(username) + userRoles, err := GetRoles("", username) if err != nil { return nil, err @@ -508,12 +514,12 @@ func GetUserRules(username string) (map[string][]Rule, error) { func convertToRules(policyRules []v1.PolicyRule) []Rule { rules := make([]Rule, 0) - for i := 0; i < (len(RoleRuleGroup)); i++ { - rule := Rule{Name: RoleRuleGroup[i].Name} + for i := 0; i < (len(RoleRuleMapping)); i++ { + rule := Rule{Name: RoleRuleMapping[i].Name} rule.Actions = make([]Action, 0) - for j := 0; j < (len(RoleRuleGroup[i].Actions)); j++ { - if actionValidate(policyRules, RoleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j]) + for j := 0; j < (len(RoleRuleMapping[i].Actions)); j++ { + if rulesMatchesAction(policyRules, RoleRuleMapping[i].Actions[j]) { + rule.Actions = append(rule.Actions, RoleRuleMapping[i].Actions[j]) } } @@ -541,12 +547,12 @@ func GetUserClusterRules(username string) ([]Rule, error) { clusterRules = append(clusterRules, role.Rules...) } - for i := 0; i < (len(ClusterRoleRuleGroup)); i++ { - rule := Rule{Name: ClusterRoleRuleGroup[i].Name} + for i := 0; i < (len(ClusterRoleRuleMapping)); i++ { + rule := Rule{Name: ClusterRoleRuleMapping[i].Name} rule.Actions = make([]Action, 0) - for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ { - if actionValidate(clusterRules, ClusterRoleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j]) + for j := 0; j < (len(ClusterRoleRuleMapping[i].Actions)); j++ { + if rulesMatchesAction(clusterRules, ClusterRoleRuleMapping[i].Actions[j]) { + rule.Actions = append(rule.Actions, ClusterRoleRuleMapping[i].Actions[j]) } } if len(rule.Actions) > 0 { @@ -567,12 +573,12 @@ func GetClusterRoleRules(name string) ([]Rule, error) { rules := make([]Rule, 0) - for i := 0; i < len(ClusterRoleRuleGroup); i++ { - rule := Rule{Name: ClusterRoleRuleGroup[i].Name} + for i := 0; i < len(ClusterRoleRuleMapping); i++ { + rule := Rule{Name: ClusterRoleRuleMapping[i].Name} rule.Actions = make([]Action, 0) - for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ { - if actionValidate(clusterRole.Rules, ClusterRoleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j]) + for j := 0; j < (len(ClusterRoleRuleMapping[i].Actions)); j++ { + if rulesMatchesAction(clusterRole.Rules, ClusterRoleRuleMapping[i].Actions[j]) { + rule.Actions = append(rule.Actions, ClusterRoleRuleMapping[i].Actions[j]) } } if len(rule.Actions) > 0 { @@ -590,12 +596,12 @@ func GetRoleRules(namespace string, name string) ([]Rule, error) { } rules := make([]Rule, 0) - for i := 0; i < len(RoleRuleGroup); i++ { - rule := Rule{Name: RoleRuleGroup[i].Name} + for i := 0; i < len(RoleRuleMapping); i++ { + rule := Rule{Name: RoleRuleMapping[i].Name} rule.Actions = make([]Action, 0) - for j := 0; j < len(RoleRuleGroup[i].Actions); j++ { - if actionValidate(role.Rules, RoleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j]) + for j := 0; j < len(RoleRuleMapping[i].Actions); j++ { + if rulesMatchesAction(role.Rules, RoleRuleMapping[i].Actions[j]) { + rule.Actions = append(rule.Actions, RoleRuleMapping[i].Actions[j]) } } if len(rule.Actions) > 0 { @@ -605,11 +611,157 @@ func GetRoleRules(namespace string, name string) ([]Rule, error) { return rules, nil } -func actionValidate(rules []v1.PolicyRule, action Action) bool { +func rulesMatchesAction(rules []v1.PolicyRule, action Action) bool { + for _, rule := range action.Rules { - if !ruleValidate(rules, rule) { + if !RulesMatchesRequired(rules, rule) { return false } } return true } + +func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool { + for _, rule := range rules { + if ruleMatchesRequired(rule, required) { + return true + } + } + return false +} + +func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool { + + if len(required.NonResourceURLs) == 0 { + for _, apiGroup := range required.APIGroups { + for _, resource := range required.Resources { + resources := strings.Split(resource, "/") + resource = resources[0] + var subsource string + if len(resources) > 1 { + subsource = resources[1] + } + + if len(required.ResourceNames) == 0 { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) { + return false + } + } + } else { + for _, resourceName := range required.ResourceNames { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) { + return false + } + } + } + } + } + } + } else { + for _, apiGroup := range required.APIGroups { + for _, nonResourceURL := range required.NonResourceURLs { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) { + return false + } + } + } + } + } + return true +} + +func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool { + + if resource == "" { + return false + } + + if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) { + return false + } + + if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) { + return false + } + + combinedResource := resource + + if subresource != "" { + combinedResource = combinedResource + "/" + subresource + } + + for _, res := range rule.Resources { + + // match "*" + if res == v1.ResourceAll || res == combinedResource { + return true + } + + // match "*/subresource" + if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") { + return true + } + // match "resource/*" + if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") { + return true + } + } + + return false +} + +func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool { + + if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) { + return false + } + + if nonResourceURL == "" { + return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName) + } else { + return ruleMatchesNonResource(rule, nonResourceURL) + } +} + +func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool { + + if nonResourceURL == "" { + return false + } + + for _, spec := range rule.NonResourceURLs { + if pathMatches(nonResourceURL, spec) { + return true + } + } + + return false +} + +func pathMatches(path, spec string) bool { + // Allow wildcard match + if spec == "*" { + return true + } + // Allow exact match + if spec == path { + return true + } + // Allow a trailing * subpath match + if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) { + return true + } + return false +} + +func hasString(slice []string, value string) bool { + for _, s := range slice { + if s == value { + return true + } + } + return false +} diff --git a/pkg/models/iam/policy.go b/pkg/models/iam/policy.go index 8ab352664..7557134c4 100644 --- a/pkg/models/iam/policy.go +++ b/pkg/models/iam/policy.go @@ -31,11 +31,12 @@ const ( func init() { rulesConfig, err := ioutil.ReadFile(rulesConfigPath) + if err == nil { config := &[]Rule{} json.Unmarshal(rulesConfig, config) if len(*config) > 0 { - RoleRuleGroup = *config + RoleRuleMapping = *config } } @@ -45,7 +46,7 @@ func init() { config := &[]Rule{} json.Unmarshal(clusterRulesConfig, config) if len(*config) > 0 { - ClusterRoleRuleGroup = *config + ClusterRoleRuleMapping = *config } } } @@ -55,6 +56,7 @@ var ( { Name: "workspaces", Actions: []Action{ + {Name: "edit", Rules: []v1.PolicyRule{ { @@ -110,6 +112,24 @@ var ( }, }, }, + {Name: "edit", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces/devops"}, + }, + }, + }, + {Name: "delete", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces/devops"}, + }, + }, + }, }, }, { @@ -133,6 +153,24 @@ var ( }, }, }, + {Name: "edit", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces/namespaces"}, + }, + }, + }, + {Name: "delete", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces/namespaces"}, + }, + }, + }, }, }, { @@ -144,240 +182,144 @@ var ( {Name: "delete"}, }, }, + { + Name: "organizations", + Actions: []Action{ + {Name: "view"}, + {Name: "create"}, + {Name: "edit"}, + {Name: "delete"}, + }, + }, + { + Name: "roles", + Actions: []Action{ + {Name: "view"}, + {Name: "create"}, + {Name: "edit"}, + {Name: "delete"}, + }, + }, } - ClusterRoleRuleGroup = []Rule{{ - Name: "projects", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "members", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list", "create", "delete"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"rolebindings"}, - }, - }, - }, - {Name: "member_roles", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list", "create", "delete", "patch", "update"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles"}, - }, - }, - }, - }, - }, { - Name: "users", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"create", "delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"create", "delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"users"}, - }, - }, - }, - }, - }, { - Name: "roles", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - }, - }, { - - Name: "images", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{ - "secrets", - }, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{ - "secrets", + ClusterRoleRuleMapping = []Rule{ + {Name: "workspaces", + Actions: []Action{ + { + Name: "view", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"get", "watch", "list"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces"}, }, }, }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{ - "secrets", + { + Name: "create", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces"}, }, }, }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{""}, - Resources: []string{ - "secrets", + {Name: "edit", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"get", "watch", "list", "create", "delete", "patch", "update"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"workspaces", "workspaces/namespaces", "workspaces/roles", "workspaces/devops", "workspaces/members"}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{"workspaces"}, + Resources: []string{"monitoring/*"}, }, }, }, }, }, - }, + //{ + // Name: "projects", + // Actions: []Action{ + // {Name: "view", + // Rules: []v1.PolicyRule{ + // { + // Verbs: []string{"get", "watch", "list"}, + // APIGroups: []string{""}, + // Resources: []string{"namespaces"}, + // }, + // }, + // }, + // {Name: "create", + // Rules: []v1.PolicyRule{ + // { + // Verbs: []string{"create"}, + // APIGroups: []string{""}, + // Resources: []string{"namespaces"}, + // }, + // }, + // }, + // {Name: "edit", + // Rules: []v1.PolicyRule{ + // { + // Verbs: []string{"update", "patch"}, + // APIGroups: []string{""}, + // Resources: []string{"namespaces"}, + // }, + // }, + // }, + // {Name: "delete", + // Rules: []v1.PolicyRule{ + // { + // Verbs: []string{"delete", "deletecollection"}, + // APIGroups: []string{""}, + // Resources: []string{"namespaces"}, + // }, + // }, + // }, + // {Name: "members", + // Rules: []v1.PolicyRule{ + // { + // Verbs: []string{"get", "watch", "list", "create", "delete", "patch", "update"}, + // APIGroups: []string{"rbac.authorization.k8s.io"}, + // Resources: []string{"rolebindings", "roles"}, + // }, + // }, + // }, + // }, + //}, { - Name: "volumes", + Name: "accounts", Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumes"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"accounts"}, }, { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, + Verbs: []string{"get", "watch", "list"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterrolebindings"}, }, }, }, {Name: "create", Rules: []v1.PolicyRule{ { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumes"}, + Verbs: []string{"create"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"accounts"}, + }, + { + Verbs: []string{"create", "delete", "deletecollection"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterrolebindings"}, }, }, }, @@ -385,17 +327,63 @@ var ( Rules: []v1.PolicyRule{ { Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumes"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"accounts"}, + }, + { + Verbs: []string{"create", "delete", "deletecollection"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterrolebindings"}, }, }, }, {Name: "delete", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"accounts"}, + }, + }, + }, + }, + }, { + Name: "roles", + Actions: []Action{ + {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumes"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles"}, + }, + }, + }, + + {Name: "create", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles"}, + }, + }, + }, + {Name: "edit", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles"}, + }, + }, + }, + {Name: "delete", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles"}, }, }, }, @@ -447,7 +435,7 @@ var ( Rules: []v1.PolicyRule{ { Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, + APIGroups: []string{"kubesphere.io"}, Resources: []string{"nodes"}, }, }, @@ -461,25 +449,31 @@ var ( }, }, }, - {Name: "drain", + {Name: "cordon", Rules: []v1.PolicyRule{ { - Verbs: []string{"*"}, - APIGroups: []string{"kubesphere.io"}, + Verbs: []string{"update", "patch"}, + APIGroups: []string{""}, Resources: []string{"nodes"}, }, + }}, + {Name: "taint", Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{""}, + Resources: []string{"nodes"}, }, - }, + }}, }, }, { - Name: "app_catalog", + Name: "repos", Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "watch", "list"}, APIGroups: []string{"openpitrix.io"}, - Resources: []string{"appcatalog"}, + Resources: []string{"repos"}, }, }, }, @@ -488,7 +482,7 @@ var ( { Verbs: []string{"create"}, APIGroups: []string{"openpitrix.io"}, - Resources: []string{"appcatalog"}, + Resources: []string{"repos"}, }, }, }, @@ -497,7 +491,7 @@ var ( { Verbs: []string{"update", "patch"}, APIGroups: []string{"openpitrix.io"}, - Resources: []string{"appcatalog"}, + Resources: []string{"repos"}, }, }, }, @@ -506,7 +500,7 @@ var ( { Verbs: []string{"delete", "deletecollection"}, APIGroups: []string{"openpitrix.io"}, - Resources: []string{"appcatalog"}, + Resources: []string{"repos"}, }, }, }, @@ -519,7 +513,7 @@ var ( { Verbs: []string{"get", "watch", "list"}, APIGroups: []string{"openpitrix.io"}, - Resources: []string{"apps"}, + Resources: []string{"apps", "repos"}, }, }, }, @@ -537,7 +531,96 @@ var ( }, }, }, - }, { + }} + + RoleRuleMapping = []Rule{{ + Name: "projects", + Actions: []Action{ + // limit range + router + {Name: "edit", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch", "get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, + }, + }, + {Name: "delete", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, + }, + }, + }, + }, + { + Name: "members", + Actions: []Action{ + {Name: "view", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"get", "watch", "list", "create", "delete"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, + }, + }, + }, + }, + { + Name: "roles", + Actions: []Action{ + {Name: "view", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"get", "watch", "list", "create", "delete", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, + }, + }, + }, + }, + { Name: "deployments", Actions: []Action{ {Name: "view", @@ -548,10 +631,15 @@ var ( Resources: []string{"deployments", "deployments/scale"}, }, { - Verbs: []string{"list"}, + Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"namespaces"}, }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, { Verbs: []string{"get", "watch", "list"}, APIGroups: []string{""}, @@ -609,10 +697,15 @@ var ( Resources: []string{"statefulsets"}, }, { - Verbs: []string{"list"}, + Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"namespaces"}, }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, { Verbs: []string{"get", "watch", "list"}, APIGroups: []string{""}, @@ -668,10 +761,15 @@ var ( Resources: []string{"daemonsets"}, }, { - Verbs: []string{"list"}, + Verbs: []string{"get"}, APIGroups: []string{""}, Resources: []string{"namespaces"}, }, + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"events"}, + }, { Verbs: []string{"get", "watch", "list"}, APIGroups: []string{""}, @@ -720,373 +818,7 @@ var ( }, }, }, - }, { - Name: "services", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - }, - }, { - Name: "routes", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - }, - }} - - RoleRuleGroup = []Rule{{ - Name: "projects", - Actions: []Action{ - {Name: "members", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list", "create", "delete"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"rolebindings"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - }, - }, - {Name: "member_roles", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list", "create", "delete", "patch", "update"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch", "get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - }, - }, }, - }, { - Name: "deployments", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments", "deployments/scale"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/log", "pods/status"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments"}, - }, - }, - }, - - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments", "deployments/rollback"}, - }, - }, - }, - - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments"}, - }, - }, - }, - {Name: "scale", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create", "update", "patch", "delete"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments/scale"}, - }, - }, - }, - }, - }, { - Name: "statefulsets", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/log", "pods/status"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "scale", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"patch"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - }, - }, { - Name: "daemonsets", - Actions: []Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/log", "pods/status"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - }, - }, { - Name: "pods", - Actions: []Action{ - {Name: "terminal", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"*"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"terminal"}, - }, - }, - }, - }, - }, { Name: "services", Actions: []Action{ @@ -1238,5 +970,151 @@ var ( }, }, }, - }} + }, { + Name: "applications", + Actions: []Action{ + {Name: "view", + Rules: []v1.PolicyRule{ + { + Verbs: []string{"list"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"applications"}, + }, + }, + }, + }, + }, + { + Name: "jobs", + Actions: []Action{ + {Name: "view", Rules: []v1.PolicyRule{ + { + Verbs: []string{"view", "list"}, + APIGroups: []string{"batch"}, + Resources: []string{"jobs"}, + }, + }}, + {Name: "create", Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"batch"}, + Resources: []string{"jobs"}, + }, + }}, + {Name: "edit", Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{"batch"}, + Resources: []string{"jobs"}, + }, + }}, + {Name: "delete", Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"batch"}, + Resources: []string{"jobs"}, + }, + }}, + }, + }, + { + Name: "cronjobs", + Actions: []Action{ + {Name: "view", Rules: []v1.PolicyRule{ + { + Verbs: []string{"view", "list"}, + APIGroups: []string{"batch"}, + Resources: []string{"cronjobs"}, + }, + }}, + {Name: "create", Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"batch"}, + Resources: []string{"cronjobs"}, + }, + }}, + {Name: "edit", Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{"batch"}, + Resources: []string{"cronjobs"}, + }, + }}, + {Name: "delete", Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{"batch"}, + Resources: []string{"cronjobs"}, + }, + }}, + }, + }, + { + Name: "secrets", + Actions: []Action{ + {Name: "view", Rules: []v1.PolicyRule{ + { + Verbs: []string{"view", "list"}, + APIGroups: []string{""}, + Resources: []string{"secrets"}, + }, + }}, + {Name: "create", Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{""}, + Resources: []string{"secrets"}, + }, + }}, + {Name: "edit", Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{""}, + Resources: []string{"secrets"}, + }, + }}, + {Name: "delete", Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{""}, + Resources: []string{"secrets"}, + }, + }}, + }, + }, + { + Name: "configmaps", + Actions: []Action{ + {Name: "view", Rules: []v1.PolicyRule{ + { + Verbs: []string{"view", "list"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }}, + {Name: "create", Rules: []v1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }}, + {Name: "edit", Rules: []v1.PolicyRule{ + { + Verbs: []string{"update", "patch"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }}, + {Name: "delete", Rules: []v1.PolicyRule{ + { + Verbs: []string{"delete", "deletecollection"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + }, + }}, + }, + }, + } ) diff --git a/pkg/models/metrics/containers.go b/pkg/models/metrics/containers.go old mode 100644 new mode 100755 diff --git a/pkg/models/metrics/metricscollector.go b/pkg/models/metrics/metricscollector.go old mode 100644 new mode 100755 index 06b1e369f..d534135dc --- a/pkg/models/metrics/metricscollector.go +++ b/pkg/models/metrics/metricscollector.go @@ -26,11 +26,19 @@ import ( "time" + "github.com/pkg/errors" "k8s.io/api/core/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sync" + + "k8s.io/apimachinery/pkg/labels" + v12 "k8s.io/client-go/listers/core/v1" + "kubesphere.io/kubesphere/pkg/client" "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/controllers" + "kubesphere.io/kubesphere/pkg/models/workspaces" ) func getPodNameRegexInWorkload(request *restful.Request) string { @@ -186,6 +194,15 @@ func MonitorAllMetrics(request *restful.Request) FormatedLevelMetric { go collectPodMetrics(request, metricName, ch) } else if sourceType == MetricLevelWorkload { go collectWorkloadMetrics(request, metricName, ch) + } else if sourceType == MetricLevelWorkspace { + name := request.PathParameter("workspace_name") + namespaces, err := workspaces.WorkspaceNamespaces(name) + + if err != nil { + glog.Errorln(err) + } + namespaces = filterNamespace(request, namespaces) + go collectWorkspaceMetrics(request, metricName, namespaces, ch) } } } @@ -213,6 +230,106 @@ func MonitorAllMetrics(request *restful.Request) FormatedLevelMetric { } } +func MonitorWorkspaceUserInfo(req *restful.Request) FormatedLevelMetric { + + var metricsArray []FormatedMetric + timestamp := time.Now().Unix() + + wg := sync.WaitGroup{} + var orgResultItem FormatedMetric + var dvpResultItem FormatedMetric + var projResultItem FormatedMetric + var actResultItem FormatedMetric + + wg.Add(4) + + go func() { + orgNums, errOrg := workspaces.GetAllOrgNums() + orgResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllOrganizationCount, WorkspaceResourceKindOrganization, orgNums, errOrg) + wg.Done() + }() + go func() { + devOpsProjectNums, errDevops := workspaces.GetAllDevOpsProjectsNums() + dvpResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllDevopsCount, WorkspaceResourceKindDevops, devOpsProjectNums, errDevops) + wg.Done() + }() + go func() { + projNums, errProj := workspaces.GetAllProjectNums() + projResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllProjectCount, WorkspaceResourceKindNamespace, projNums, errProj) + wg.Done() + }() + go func() { + actNums, errAct := workspaces.GetAllAccountNums() + actResultItem = getSpecificMetricItem(timestamp, MetricNameWorkspaceAllAccountCount, WorkspaceResourceKindAccount, actNums, errAct) + wg.Done() + }() + + wg.Wait() + metricsArray = append(metricsArray, orgResultItem, dvpResultItem, projResultItem, actResultItem) + + return FormatedLevelMetric{ + MetricsLevel: MetricLevelWorkspace, + Results: metricsArray, + } +} + +//func getWorkspaceMetricItem(timestamp int64, namespaceNums int64, resourceName string, err error) FormatedMetric { +// var fMetric FormatedMetric +// fMetric.Data.ResultType = ResultTypeVector +// fMetric.MetricName = MetricNameWorkspaceInfoCount +// fMetric.Status = MetricStatusSuccess +// if err != nil { +// fMetric.Status = MetricStatusError +// } +// resultItem := make(map[string]interface{}) +// tmp := make(map[string]string) +// tmp[ResultItemMetricResource] = resourceName +// resultItem[ResultItemMetric] = tmp +// resultItem[ResultItemValue] = []interface{}{timestamp, strconv.FormatInt(namespaceNums, 10)} +// return fMetric +//} + +func MonitorWorkspaceResourceLevelMetrics(request *restful.Request) FormatedLevelMetric { + wsName := request.PathParameter("workspace_name") + namspaces, errNs := workspaces.WorkspaceNamespaces(wsName) + + devOpsProjects, errDevOps := workspaces.GetDevOpsProjects(wsName) + members, errMemb := workspaces.GetOrgMembers(wsName) + roles, errRole := workspaces.GetOrgRoles(wsName) + + var fMetricsArray []FormatedMetric + timestamp := int64(time.Now().Unix()) + namespaces, noneExistentNs := getExistingNamespace(namspaces) + if len(noneExistentNs) != 0 { + nsStr := strings.Join(noneExistentNs, "|") + errStr := "the namespaces " + nsStr + " do not exist" + if errNs == nil { + errNs = errors.New(errStr) + } else { + errNs = errors.New(errNs.Error() + "\t" + errStr) + } + } + + // add namespaces(project) metric + nsMetrics := getSpecificMetricItem(timestamp, MetricNameWorkspaceNamespaceCount, WorkspaceResourceKindNamespace, len(namespaces), errNs) + // add devops metric + devopsMetrics := getSpecificMetricItem(timestamp, MetricNameWorkspaceDevopsCount, WorkspaceResourceKindDevops, len(devOpsProjects), errDevOps) + // add member metric + memberMetrics := getSpecificMetricItem(timestamp, MetricNameWorkspaceMemberCount, WorkspaceResourceKindMember, len(members), errMemb) + // add role metric + roleMetrics := getSpecificMetricItem(timestamp, MetricNameWorkspaceRoleCount, WorkspaceResourceKindRole, len(roles), errRole) + // add workloads count metric + wlMetrics := getWorkspaceWorkloadCountMetrics(namespaces) + // add pods count metric + podsCountMetrics := getWorkspacePodsCountMetrics(request, namespaces) + fMetricsArray = append(fMetricsArray, nsMetrics, devopsMetrics, memberMetrics, roleMetrics, wlMetrics, *podsCountMetrics) + + return FormatedLevelMetric{ + MetricsLevel: MetricLevelWorkspace, + Results: fMetricsArray, + } +} + func getWorkspacePodsCountMetrics(request *restful.Request, namespaces []string) *FormatedMetric { metricName := MetricNameNamespacePodCount var recordingRule = RulePromQLTmplMap[metricName] @@ -289,13 +406,13 @@ func MonitorNodeorClusterSingleMetric(request *restful.Request, metricsName stri var fMetric FormatedMetric timestamp := int64(time.Now().Unix()) - if metricsName == MetricNameClusterHealthyNodeCount { + if metricsName == "cluster_node_online" { onlineNodes, _ := getNodeHealthyConditionMetric() fMetric = getSpecificMetricItem(timestamp, MetricNameClusterHealthyNodeCount, "node_count", len(onlineNodes), nil) - } else if metricsName == MetricNameClusterUnhealthyNodeCount { + } else if metricsName == "cluster_node_offline" { _, offlineNodes := getNodeHealthyConditionMetric() fMetric = getSpecificMetricItem(timestamp, MetricNameClusterUnhealthyNodeCount, "node_count", len(offlineNodes), nil) - } else if metricsName == MetricNameClusterNodeCount { + } else if metricsName == "cluster_node_total" { onlineNodes, offlineNodes := getNodeHealthyConditionMetric() fMetric = getSpecificMetricItem(timestamp, MetricNameClusterNodeCount, "node_count", len(onlineNodes)+len(offlineNodes), nil) } else { @@ -355,14 +472,14 @@ func getExistingNamespace(namespaces []string) ([]string, []string) { } func getAllNamespace() (map[string]int, error) { - k8sClient := client.NewK8sClient() - nsList, err := k8sClient.CoreV1().Namespaces().List(metaV1.ListOptions{}) + lister := controllers.ResourceControllers.Controllers[controllers.Namespaces].Lister().(v12.NamespaceLister) + nsList, err := lister.List(labels.Everything()) if err != nil { glog.Errorln(err) return nil, err } namespaceMap := make(map[string]int) - for _, item := range nsList.Items { + for _, item := range nsList { namespaceMap[item.Name] = 0 } return namespaceMap, nil diff --git a/pkg/models/metrics/metricsconst.go b/pkg/models/metrics/metricsconst.go old mode 100644 new mode 100755 index a44d302a5..a43cd52b5 --- a/pkg/models/metrics/metricsconst.go +++ b/pkg/models/metrics/metricsconst.go @@ -148,7 +148,7 @@ var RulePromQLTmplMap = MetricMap{ "cluster_memory_bytes_available": "sum(node:node_memory_bytes_available:sum)", "cluster_memory_bytes_total": "sum(node:node_memory_bytes_total:sum)", "cluster_memory_bytes_usage": "sum(node:node_memory_bytes_total:sum) - sum(node:node_memory_bytes_available:sum)", - "cluster_net_utilisation": "sum(node:node_net_utilisation:sum_irate)", + "cluster_net_utilisation": ":node_net_utilisation:sum_irate", "cluster_net_bytes_transmitted": "sum(node:node_net_bytes_transmitted:sum_irate)", "cluster_net_bytes_received": "sum(node:node_net_bytes_received:sum_irate)", "cluster_disk_read_iops": "sum(node:data_volume_iops_reads:sum)", diff --git a/pkg/models/metrics/metricsrule.go b/pkg/models/metrics/metricsrule.go old mode 100644 new mode 100755 diff --git a/pkg/models/metrics/metricsstruct.go b/pkg/models/metrics/metricsstruct.go old mode 100644 new mode 100755 diff --git a/pkg/models/metrics/nodes.go b/pkg/models/metrics/nodes.go old mode 100644 new mode 100755 diff --git a/pkg/models/metrics/pods.go b/pkg/models/metrics/pods.go old mode 100644 new mode 100755 diff --git a/pkg/models/workspaces/types.go b/pkg/models/workspaces/types.go index 3147dbd18..edc8176c4 100644 --- a/pkg/models/workspaces/types.go +++ b/pkg/models/workspaces/types.go @@ -4,8 +4,9 @@ import "time" type Workspace struct { Group `json:",inline"` - Namespaces []string `json:"namespaces,omitempty"` - DevopsProjects []string `json:"devops_projects,omitempty"` + Admin string `json:"admin,omitempty"` + Namespaces []string `json:"namespaces"` + DevopsProjects []string `json:"devops_projects"` } type UserInvite struct { diff --git a/pkg/models/workspaces/workspaces.go b/pkg/models/workspaces/workspaces.go index 6547517f1..a840c3fc8 100644 --- a/pkg/models/workspaces/workspaces.go +++ b/pkg/models/workspaces/workspaces.go @@ -7,19 +7,21 @@ import ( "io/ioutil" "net/http" - "github.com/jinzhu/gorm" - core "k8s.io/api/core/v1" - k8sErr "k8s.io/apimachinery/pkg/api/errors" - "log" "strings" + "github.com/jinzhu/gorm" + core "k8s.io/api/core/v1" + + "errors" + "regexp" + "github.com/emicklei/go-restful" "k8s.io/api/rbac/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - v13 "k8s.io/client-go/listers/rbac/v1" + clientV1 "k8s.io/client-go/listers/core/v1" "k8s.io/kubernetes/pkg/util/slice" "kubesphere.io/kubesphere/pkg/client" @@ -31,12 +33,6 @@ import ( var WorkSpaceRoles = []string{"admin", "operator", "viewer"} -func UnBindNamespace(workspace string, namespace string) error { - db := client.NewSharedDBClient() - defer db.Close() - return db.Delete(&WorkspaceNSBinding{Workspace: workspace, Namespace: namespace}).Error -} - func UnBindDevopsProject(workspace string, devops string) error { db := client.NewSharedDBClient() defer db.Close() @@ -100,44 +96,85 @@ func CreateDevopsProject(username string, devops DevopsProject) (*DevopsProject, return &project, nil } -func Namespaces(workspace string) ([]*core.Namespace, error) { - db := client.NewSharedDBClient() - defer db.Close() +func ListNamespaceByUser(workspaceName string, username string) ([]*core.Namespace, error) { - var workspaceNSBindings []WorkspaceNSBinding + namespaces, err := Namespaces(workspaceName) - if err := db.Where("workspace = ?", workspace).Find(&workspaceNSBindings).Error; err != nil { + if err != nil { return nil, err } - namespaces := make([]*core.Namespace, 0) + clusterRoles, err := iam.GetClusterRoles(username) - for _, workspaceNSBinding := range workspaceNSBindings { - namespace, err := client.NewK8sClient().CoreV1().Namespaces().Get(workspaceNSBinding.Namespace, meta_v1.GetOptions{}) - if err != nil { - if k8sErr.IsNotFound(err) { - db.Delete(&WorkspaceNSBinding{Workspace: workspace, Namespace: workspaceNSBinding.Namespace}) - } else { + if err != nil { + return nil, err + } + + rules := make([]v1.PolicyRule, 0) + + for _, clusterRole := range clusterRoles { + rules = append(rules, clusterRole.Rules...) + } + + namespacesManager := v1.PolicyRule{APIGroups: []string{"kubesphere.io"}, ResourceNames: []string{workspaceName}, Verbs: []string{"get"}, Resources: []string{"workspaces/namespaces"}} + + if iam.RulesMatchesRequired(rules, namespacesManager) { + return namespaces, nil + } else { + for i := 0; i < len(namespaces); i++ { + roles, err := iam.GetRoles(namespaces[i].Name, username) + if err != nil { return nil, err } - } else { - namespaces = append(namespaces, namespace) + rules := make([]v1.PolicyRule, 0) + for _, role := range roles { + rules = append(rules, role.Rules...) + } + if !iam.RulesMatchesRequired(rules, v1.PolicyRule{APIGroups: []string{""}, ResourceNames: []string{namespaces[i].Name}, Verbs: []string{"get"}, Resources: []string{"namespaces"}}) { + namespaces = append(namespaces[:i], namespaces[i+1:]...) + i-- + } } } return namespaces, nil } +func Namespaces(workspaceName string) ([]*core.Namespace, error) { + + lister := controllers.ResourceControllers.Controllers[controllers.Namespaces].Lister().(clientV1.NamespaceLister) + + namespaces, err := lister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspaceName})) + + if err != nil { + return nil, err + } + + if namespaces == nil { + return make([]*core.Namespace, 0), nil + } + + return namespaces, nil +} + func BindingDevopsProject(workspace string, devops string) error { db := client.NewSharedDBClient() defer db.Close() return db.Create(&WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error } -func DeleteNamespace(namespace string) error { - deletePolicy := meta_v1.DeletePropagationBackground - err := client.NewK8sClient().CoreV1().Namespaces().Delete(namespace, &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy}) - return err +func DeleteNamespace(workspace string, namespaceName string) error { + namespace, err := client.NewK8sClient().CoreV1().Namespaces().Get(namespaceName, meta_v1.GetOptions{}) + if err != nil { + return err + } + if namespace.Labels != nil && namespace.Labels["kubesphere.io/workspace"] == workspace { + deletePolicy := meta_v1.DeletePropagationBackground + return client.NewK8sClient().CoreV1().Namespaces().Delete(namespaceName, &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy}) + } else { + return errors.New("resource not found") + } + } func Delete(workspace *Workspace) error { @@ -174,7 +211,7 @@ func Delete(workspace *Workspace) error { func release(workspace *Workspace) error { for _, namespace := range workspace.Namespaces { - err := DeleteNamespace(namespace) + err := DeleteNamespace(workspace.Name, namespace) if err != nil && !apierrors.IsNotFound(err) { return err } @@ -335,7 +372,44 @@ func Detail(name string) (*Workspace, error) { return workspace, nil } -func List(names []string) ([]*Workspace, error) { +// List all workspaces for the current user +func ListByUser(username string) ([]*Workspace, error) { + + clusterRoles, err := iam.GetClusterRoles(username) + + if err != nil { + return nil, err + } + + rules := make([]v1.PolicyRule, 0) + + for _, clusterRole := range clusterRoles { + rules = append(rules, clusterRole.Rules...) + } + + workspacesManager := v1.PolicyRule{APIGroups: []string{"kubesphere.io"}, Verbs: []string{"list", "get"}, Resources: []string{"workspaces"}} + + if iam.RulesMatchesRequired(rules, workspacesManager) { + return fetch(nil) + } else { + workspaceNames := make([]string, 0) + + for _, clusterRole := range clusterRoles { + if regexp.MustCompile("^system:\\w+:(admin|operator|viewer)$").MatchString(clusterRole.Name) { + arr := strings.Split(clusterRole.Name, ":") + workspaceNames = append(workspaceNames, arr[1]) + } + } + + if len(workspaceNames) == 0 { + return make([]*Workspace, 0), nil + } + + return fetch(workspaceNames) + } +} + +func fetch(names []string) ([]*Workspace, error) { url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer) @@ -379,6 +453,7 @@ func List(names []string) ([]*Workspace, error) { } workspaces = append(workspaces, workspace) } + return workspaces, nil } @@ -434,16 +509,16 @@ func DevopsProjects(workspace string) ([]DevopsProject, error) { } func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) { - var workspaceNSBindings []WorkspaceNSBinding + namespaces, err := Namespaces(group.Name) - if err := db.Where("workspace = ?", group.Name).Find(&workspaceNSBindings).Error; err != nil { + if err != nil { return nil, err } - namespaces := make([]string, 0) + namespacesNames := make([]string, 0) - for _, workspaceNSBinding := range workspaceNSBindings { - namespaces = append(namespaces, workspaceNSBinding.Namespace) + for _, namespace := range namespaces { + namespacesNames = append(namespacesNames, namespace.Name) } var workspaceDOPBindings []WorkspaceDPBinding @@ -459,7 +534,7 @@ func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) { } workspace := Workspace{Group: group} - workspace.Namespaces = namespaces + workspace.Namespaces = namespacesNames workspace.DevopsProjects = devOpsProjects return &workspace, nil } @@ -468,12 +543,6 @@ func CreateNamespace(namespace *core.Namespace) (*core.Namespace, error) { return client.NewK8sClient().CoreV1().Namespaces().Create(namespace) } -func BindingNamespace(workspace string, namespace string) error { - db := client.NewSharedDBClient() - defer db.Close() - return db.Create(&WorkspaceNSBinding{Workspace: workspace, Namespace: namespace}).Error -} - func Invite(workspaceName string, users []UserInvite) error { for _, user := range users { if !slice.ContainsString(WorkSpaceRoles, user.Role, nil) { @@ -523,38 +592,22 @@ func RemoveMembers(workspaceName string, users []string) error { return err } + for i := 0; i < len(workspace.Members); i++ { + if slice.ContainsString(users, workspace.Members[i], nil) { + workspace.Members = append(workspace.Members[:i], workspace.Members[i+1:]...) + i-- + } + } + + workspace, err = Edit(workspace) + + if err != nil { + return err + } + return nil } -//func checkUserExist(username string) (bool, error) { -// result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?check=%s", constants.AccountAPIServer, username)) -// -// if err != nil { -// return false, err -// } -// -// data, err := ioutil.ReadAll(result.Body) -// -// if err != nil { -// return false, err -// } -// -// if result.StatusCode > 200 { -// return false, ksErr.Wrap(data) -// } -// -// var r map[string]bool -// -// err = json.Unmarshal(data, &r) -// -// if err != nil { -// return false, err -// } -// -// return r["exist"], nil -// -//} - func Roles(workspace *Workspace) ([]*v1.ClusterRole, error) { roles := make([]*v1.ClusterRole, 0) @@ -614,11 +667,138 @@ func WorkspaceRoleInit(workspace *Workspace) error { admin.Name = fmt.Sprintf("system:%s:admin", workspace.Name) admin.Kind = iam.ClusterRoleKind admin.Rules = []v1.PolicyRule{ + // apis/kubesphere.io/v1alpha1/workspaces/sample + // apis/kubesphere.io/v1alpha1/workspaces/sample/namespaces + // apis/kubesphere.io/v1alpha1/workspaces/sample/devops + // apis/kubesphere.io/v1alpha1/workspaces/sample/roles + // apis/kubesphere.io/v1alpha1/workspaces/sample/members + // apis/kubesphere.io/v1alpha1/workspaces/sample/members/admin + { Verbs: []string{"*"}, + APIGroups: []string{"kubesphere.io", "account.kubesphere.io"}, + ResourceNames: []string{workspace.Name}, + Resources: []string{"workspaces", "workspaces/*"}, + }, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/namespaces + + { + Verbs: []string{"create"}, APIGroups: []string{"kubesphere.io"}, ResourceNames: []string{workspace.Name}, - Resources: []string{"workspaces", "workspaces/namespaces", "workspaces/members", "workspaces/devops", "workspaces/registries"}, + Resources: []string{"workspaces/namespaces"}, + }, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/members + + { + Verbs: []string{"create"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{workspace.Name}, + Resources: []string{"workspaces/members"}, + }, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/devops + { + Verbs: []string{"create"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{workspace.Name}, + Resources: []string{"workspaces/devops"}, + }, + // TODO have risks + // get apis/apps/v1/namespaces/proj1/deployments/?labelSelector + // post api/v1/namespaces/project-0vya57/limitranges + { + Verbs: []string{"*"}, + APIGroups: []string{"", "apps", "extensions", "batch"}, + Resources: []string{"limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumes", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"}, + }, + // get apis/kubesphere.io/v1alpha1/quota/namespaces/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"quota/*"}, + }, + // get api/v1/namespaces/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces", "serviceaccounts", "configmaps"}, + }, + // get api/v1/namespaces/proj1/serviceaccounts + // get api/v1/namespaces/proj1/configmaps + // get api/v1/namespaces/proj1/secrets + + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"serviceaccounts", "configmaps", "secrets"}, + }, + + // get apis/kubesphere.io/v1alpha1/status/namespaces/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{"namespaces"}, + Resources: []string{"status/*"}, + }, + // apis/kubesphere.io/v1alpha1/namespaces/proj1/router + { + Verbs: []string{"list"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"router"}, + }, + // get apis/kubesphere.io/v1alpha1/registries/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"registries"}, + }, + + // get apis/kubesphere.io/v1alpha1/monitoring/namespaces/proj1 + + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{"namespaces"}, + Resources: []string{"monitoring/*"}, + }, + + // get apis/kubesphere.io/v1alpha1/resources/persistent-volume-claims + // get apis/kubesphere.io/v1alpha1/resources/deployments + // get apis/kubesphere.io/v1alpha1/resources/statefulsets + // get apis/kubesphere.io/v1alpha1/resources/daemonsets + // get apis/kubesphere.io/v1alpha1/resources/jobs + // get apis/kubesphere.io/v1alpha1/resources/cronjobs + // get apis/kubesphere.io/v1alpha1/resources/persistent-volume-claims + // get apis/kubesphere.io/v1alpha1/resources/services + // get apis/kubesphere.io/v1alpha1/resources/ingresses + // get apis/kubesphere.io/v1alpha1/resources/secrets + // get apis/kubesphere.io/v1alpha1/resources/configmaps + // get apis/kubesphere.io/v1alpha1/resources/roles + + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"resources"}, + }, + + // apis/account.kubesphere.io/v1alpha1/users + // apis/account.kubesphere.io/v1alpha1/namespaces/proj1/users + { + Verbs: []string{"list"}, + APIGroups: []string{"account.kubesphere.io"}, + Resources: []string{"users"}, + }, + + // apis/kubesphere.io/v1alpha1/monitoring/workspaces/sample?metrics_filter= + // apis/kubesphere.io/v1alpha1/monitoring/workspaces/sample/pods?step=30m + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{"workspaces"}, + Resources: []string{"monitoring/" + workspace.Name}, }, } @@ -634,35 +814,159 @@ func WorkspaceRoleInit(workspace *Workspace) error { Resources: []string{"workspaces"}, ResourceNames: []string{workspace.Name}, }, { - Verbs: []string{"list", "create"}, + Verbs: []string{"create", "get"}, APIGroups: []string{"kubesphere.io"}, Resources: []string{"workspaces/namespaces", "workspaces/devops"}, ResourceNames: []string{workspace.Name}, - }, { - Verbs: []string{"list"}, - APIGroups: []string{"kubesphere.io"}, - Resources: []string{"workspaces/members", "workspaces/registries"}, - ResourceNames: []string{workspace.Name}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"registries"}, }, } + operator.Labels = map[string]string{"creator": "system"} viewer := new(v1.ClusterRole) viewer.Name = fmt.Sprintf("system:%s:viewer", workspace.Name) viewer.Kind = iam.ClusterRoleKind viewer.Rules = []v1.PolicyRule{ + // apis/kubesphere.io/v1alpha1/workspaces/sample + // apis/kubesphere.io/v1alpha1/workspaces/sample/namespaces + // apis/kubesphere.io/v1alpha1/workspaces/sample/devops + // apis/kubesphere.io/v1alpha1/workspaces/sample/roles + // apis/kubesphere.io/v1alpha1/workspaces/sample/members + // apis/kubesphere.io/v1alpha1/workspaces/sample/members/admin + + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io", "account.kubesphere.io"}, + ResourceNames: []string{workspace.Name}, + Resources: []string{"workspaces", "workspaces/*"}, + }, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/namespaces + + //{ + // Verbs: []string{"create"}, + // APIGroups: []string{"kubesphere.io"}, + // ResourceNames: []string{workspace.Name}, + // Resources: []string{"workspaces/namespaces"}, + //}, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/members + + //{ + // Verbs: []string{"create"}, + // APIGroups: []string{"kubesphere.io"}, + // ResourceNames: []string{workspace.Name}, + // Resources: []string{"workspaces/members"}, + //}, + + // post apis/kubesphere.io/v1alpha1/workspaces/sample/devops + //{ + // Verbs: []string{"create"}, + // APIGroups: []string{"kubesphere.io"}, + // ResourceNames: []string{workspace.Name}, + // Resources: []string{"workspaces/devops"}, + //}, + // TODO have risks + // get apis/apps/v1/namespaces/proj1/deployments/?labelSelector + // post api/v1/namespaces/project-0vya57/limitranges + { + Verbs: []string{"get", "list"}, + APIGroups: []string{"", "apps", "extensions", "batch"}, + Resources: []string{"limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumes", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"}, + }, + // get apis/kubesphere.io/v1alpha1/quota/namespaces/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"quota/*"}, + }, + // get api/v1/namespaces/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"namespaces", "serviceaccounts", "configmaps"}, + }, + // get api/v1/namespaces/proj1/serviceaccounts + // get api/v1/namespaces/proj1/configmaps + // get api/v1/namespaces/proj1/secrets + + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"serviceaccounts", "configmaps", "secrets"}, + }, + + // get apis/kubesphere.io/v1alpha1/status/namespaces/proj1 { Verbs: []string{"get"}, APIGroups: []string{"kubesphere.io"}, - Resources: []string{"workspaces"}, - ResourceNames: []string{workspace.Name}, - }, { - Verbs: []string{"list"}, + ResourceNames: []string{"namespaces"}, + Resources: []string{"status/*"}, + }, + // apis/kubesphere.io/v1alpha1/namespaces/proj1/router + { + Verbs: []string{"list"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"router"}, + }, + // get apis/kubesphere.io/v1alpha1/registries/proj1 + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"registries"}, + }, + + // get apis/kubesphere.io/v1alpha1/monitoring/namespaces/proj1 + + { + Verbs: []string{"get"}, APIGroups: []string{"kubesphere.io"}, - Resources: []string{"workspaces/namespaces", "workspaces/members", "workspaces/devops", "workspaces/registries"}, - ResourceNames: []string{workspace.Name}, + ResourceNames: []string{"namespaces"}, + Resources: []string{"monitoring/*"}, + }, + + // get apis/kubesphere.io/v1alpha1/resources/persistent-volume-claims + // get apis/kubesphere.io/v1alpha1/resources/deployments + // get apis/kubesphere.io/v1alpha1/resources/statefulsets + // get apis/kubesphere.io/v1alpha1/resources/daemonsets + // get apis/kubesphere.io/v1alpha1/resources/jobs + // get apis/kubesphere.io/v1alpha1/resources/cronjobs + // get apis/kubesphere.io/v1alpha1/resources/persistent-volume-claims + // get apis/kubesphere.io/v1alpha1/resources/services + // get apis/kubesphere.io/v1alpha1/resources/ingresses + // get apis/kubesphere.io/v1alpha1/resources/secrets + // get apis/kubesphere.io/v1alpha1/resources/configmaps + // get apis/kubesphere.io/v1alpha1/resources/roles + + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + Resources: []string{"resources"}, + }, + + // apis/account.kubesphere.io/v1alpha1/users + // apis/account.kubesphere.io/v1alpha1/namespaces/proj1/users + { + Verbs: []string{"list"}, + APIGroups: []string{"account.kubesphere.io"}, + Resources: []string{"users"}, + }, + + // apis/kubesphere.io/v1alpha1/monitoring/workspaces/sample?metrics_filter= + // apis/kubesphere.io/v1alpha1/monitoring/workspaces/sample/pods?step=30m + { + Verbs: []string{"get"}, + APIGroups: []string{"kubesphere.io"}, + ResourceNames: []string{"workspaces"}, + Resources: []string{"monitoring/" + workspace.Name}, }, } + viewer.Labels = map[string]string{"creator": "system"} _, err := k8sClient.RbacV1().ClusterRoles().Create(admin) @@ -677,6 +981,7 @@ func WorkspaceRoleInit(workspace *Workspace) error { adminRoleBinding.Name = admin.Name adminRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: admin.Name} adminRoleBinding.Subjects = []v1.Subject{{Kind: v1.UserKind, Name: workspace.Creator}} + _, err = k8sClient.RbacV1().ClusterRoleBindings().Create(adminRoleBinding) if err != nil { if !apierrors.IsAlreadyExists(err) { @@ -761,17 +1066,15 @@ func unbindWorkspaceRole(workspace string, users []string) error { func unbindNamespacesRole(namespaces []string, users []string) error { - lister := controllers.ResourceControllers.Controllers[controllers.Namespaces].Lister().(v13.RoleBindingLister) - k8sClient := client.NewK8sClient() for _, namespace := range namespaces { - roleBindings, err := lister.RoleBindings(namespace).List(labels.Everything()) + roleBindings, err := k8sClient.RbacV1().RoleBindings(namespace).List(meta_v1.ListOptions{}) if err != nil { return err } - for _, roleBinding := range roleBindings { + for _, roleBinding := range roleBindings.Items { modify := false for i := 0; i < len(roleBinding.Subjects); i++ { @@ -781,7 +1084,7 @@ func unbindNamespacesRole(namespaces []string, users []string) error { } } if modify { - _, err := k8sClient.RbacV1().RoleBindings(namespace).Update(roleBinding) + _, err := k8sClient.RbacV1().RoleBindings(namespace).Update(&roleBinding) if err != nil { return err } @@ -857,3 +1160,150 @@ func CreateWorkspaceRoleBinding(workspace *Workspace, username string, role stri return nil } + +func GetDevOpsProjects(name string) ([]string, error) { + + db := client.NewSharedDBClient() + defer db.Close() + + var workspaceDOPBindings []WorkspaceDPBinding + + if err := db.Where("workspace = ?", name).Find(&workspaceDOPBindings).Error; err != nil { + return nil, err + } + + devOpsProjects := make([]string, 0) + + for _, workspaceDOPBinding := range workspaceDOPBindings { + devOpsProjects = append(devOpsProjects, workspaceDOPBinding.DevOpsProject) + } + return devOpsProjects, nil +} + +func GetOrgMembers(workspace string) ([]string, error) { + ws, err := Detail(workspace) + if err != nil { + return nil, err + } + return ws.Members, nil +} + +func GetOrgRoles(name string) ([]string, error) { + return []string{"admin", "operator", "user"}, nil +} + +func WorkspaceNamespaces(workspaceName string) ([]string, error) { + ns, err := Namespaces(workspaceName) + + namespaces := make([]string, 0) + + if err != nil { + return namespaces, err + } + + for i := 0; i < len(ns); i++ { + namespaces = append(namespaces, ns[i].Name) + } + + return namespaces, nil +} + +func CountAll() (int, error) { + result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/count", constants.AccountAPIServer)) + + if err != nil { + return 0, err + } + + data, err := ioutil.ReadAll(result.Body) + if err != nil { + return 0, err + } + if result.StatusCode > 200 { + return 0, ksErr.Wrap(data) + } + var count map[string]interface{} + + err = json.Unmarshal(data, &count) + + if err != nil { + return 0, err + } + val, ok := count["total"] + + if !ok { + return 0, errors.New("not found") + } + + switch val.(type) { + case int: + return val.(int), nil + case float32: + return int(val.(float32)), nil + case float64: + return int(val.(float64)), nil + } + + return 0, errors.New("not found") +} + +func GetAllOrgNums() (int, error) { + count, err := CountAll() + if err != nil { + return 0, err + } + return count, nil +} + +func GetAllProjectNums() (int, error) { + return controllers.ResourceControllers.Controllers[controllers.Namespaces].CountWithConditions(""), nil +} + +func GetAllDevOpsProjectsNums() (int, error) { + db := client.NewSharedDBClient() + defer db.Close() + + var count int + if err := db.Find(&WorkspaceDPBinding{}).Count(&count).Error; err != nil { + return 0, err + } + return count, nil +} + +func GetAllAccountNums() (int, error) { + result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users", constants.AccountAPIServer)) + + if err != nil { + return 0, err + } + + data, err := ioutil.ReadAll(result.Body) + if err != nil { + return 0, err + } + if result.StatusCode > 200 { + return 0, ksErr.Wrap(data) + } + var count map[string]interface{} + + err = json.Unmarshal(data, &count) + + if err != nil { + return 0, err + } + val, ok := count["total"] + + if !ok { + return 0, errors.New("not found") + } + + switch val.(type) { + case int: + return val.(int), nil + case float32: + return int(val.(float32)), nil + case float64: + return int(val.(float64)), nil + } + return 0, errors.New("not found") +}