refactor workspace api

This commit is contained in:
hongming
2018-10-25 14:55:16 +08:00
parent a8d5f552a0
commit 70065d430d
30 changed files with 2805 additions and 1249 deletions

View File

@@ -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)

60
pkg/apis/v1alpha/monitoring/monitor_handler.go Normal file → Executable file
View File

@@ -46,6 +46,31 @@ func (u MonitorResource) monitorContainer(request *restful.Request, response *re
}
func (u MonitorResource) monitorWorkload(request *restful.Request, response *restful.Response) {
wlKind := request.PathParameter("workload_kind")
if strings.Trim(wlKind, " ") == "" {
// count all workloads figure
//metricName := "workload_count"
res := metrics.MonitorWorkloadCount(request)
response.WriteAsJson(res)
} else {
res := metrics.MonitorAllMetrics(request)
response.WriteAsJson(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)
}
@@ -196,10 +221,43 @@ func Register(ws *restful.WebService, subPath string) {
Doc("monitor specific workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.PathParameter("workload_kind", "workload kind").DataType("string").Required(true).DefaultValue("daemonset")).
Param(ws.PathParameter("workload_kind", "workload kind").DataType("string").Required(false).DefaultValue("daemonset")).
Param(ws.QueryParameter("workload_name", "workload name").DataType("string").Required(true).DefaultValue("")).
Metadata(restfulspec.KeyOpenAPITags, tags)).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET(subPath+"/namespaces/{ns_name}/workloads").To(u.monitorWorkload).
Filter(route.RouteLogging).
Doc("monitor all workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
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)
}

View File

@@ -55,6 +55,23 @@ func Register(ws *restful.WebService, subPath string) {
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
ws.Route(ws.GET(subPath + "/{name}/namespaces/{namespace}/searchwords/{searchWord}").
Param(ws.PathParameter("namespace", "registry secret's namespace")).
Param(ws.PathParameter("name", "registry secret's name")).
Param(ws.PathParameter("searchWord", "keyword use to search image")).
To(handlerImageSearch).
Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
ws.Route(ws.GET(subPath + "/{name}/namespaces/{namespace}/tags").
Param(ws.QueryParameter("image", "imageName")).
Param(ws.PathParameter("namespace", "registry secret's namespace")).
Param(ws.PathParameter("name", "registry secret's name")).
To(handlerGetImageTags).
Filter(route.RouteLogging)).
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
}
func handlerRegistryValidation(request *restful.Request, response *restful.Response) {
@@ -77,6 +94,30 @@ func handlerRegistryValidation(request *restful.Request, response *restful.Respo
}
func handlerImageSearch(request *restful.Request, response *restful.Response) {
registry := request.PathParameter("name")
searchWord := request.PathParameter("searchWord")
namespace := request.PathParameter("namespace")
res := models.ImageSearch(namespace, registry, searchWord)
response.WriteEntity(res)
}
func handlerGetImageTags(request *restful.Request, response *restful.Response) {
registry := request.PathParameter("name")
image := request.QueryParameter("image")
namespace := request.PathParameter("namespace")
res := models.GetImageTags(namespace, registry, image)
response.WriteEntity(res)
}
func handleCreateRegistries(request *restful.Request, response *restful.Response) {
registries := models.Registries{}

View File

@@ -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)
}

View File

@@ -17,6 +17,9 @@ import (
"net/http"
"net/url"
"strconv"
"time"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"github.com/pkg/errors"
@@ -28,6 +31,8 @@ const (
DefaultPrometheusPort = "9090"
PrometheusApiPath = "/api/v1/"
PrometheusEndpointUrl = DefaultScheme + "://" + DefaultPrometheusService + ":" + DefaultPrometheusPort + PrometheusApiPath
DefaultQueryStep = "10m"
DefaultQueryTimeout = "30s"
)
var client = &http.Client{}
@@ -79,14 +84,18 @@ func ParseRequestHeader(request *restful.Request) (url.Values, bool, error) {
end := request.QueryParameter("end")
step := request.QueryParameter("step")
timeout := request.QueryParameter("timeout")
if timeout == "" {
timeout = "30s"
timeout = DefaultQueryTimeout
}
if step == "" {
step = DefaultQueryStep
}
// Whether query or query_range request
u := url.Values{}
if start != "" && end != "" && step != "" {
u.Set("start", start)
u.Set("end", end)
if start != "" && end != "" {
u.Set("start", convertTimeGranularity(start))
u.Set("end", convertTimeGranularity(end))
u.Set("step", step)
u.Set("timeout", timeout)
return u, true, nil
@@ -101,6 +110,18 @@ func ParseRequestHeader(request *restful.Request) (url.Values, bool, error) {
return u, false, nil
}
glog.Error("Parse request failed", u)
return u, false, errors.Errorf("Parse request failed")
glog.Errorln("Parse request %s failed", u)
return u, false, errors.Errorf("Parse request time range %s failed", u)
}
func convertTimeGranularity(ts string) string {
timeFloat, err := strconv.ParseFloat(ts, 64)
if err != nil {
glog.Errorf("convert second timestamp %s to minute timestamp failed", ts)
return strconv.FormatInt(int64(time.Now().Unix()), 10)
}
timeInt := int64(timeFloat)
// convert second timestamp to minute timestamp
secondTime := time.Unix(timeInt, 0).Truncate(time.Minute).Unix()
return strconv.FormatInt(secondTime, 10)
}

View File

@@ -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
}

View File

@@ -36,7 +36,7 @@ func (ctl *ClusterRoleCtl) generateObject(item v1.ClusterRole) *ClusterRole {
}
name := item.Name
if strings.HasPrefix(name, systemPrefix) {
if strings.HasPrefix(name, systemPrefix) || item.Annotations == nil || len(item.Annotations[creator]) == 0 {
return nil
}
@@ -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{}) {

View File

@@ -29,11 +29,16 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"reflect"
"strings"
"kubesphere.io/kubesphere/pkg/client"
)
var k8sClient *kubernetes.Clientset
const retryTimes = 3
func (ctl *JobCtl) generateObject(item v1.Job) *Job {
var status, displayName string
@@ -134,11 +139,13 @@ func (ctl *JobCtl) initListerAndInformer() {
object := obj.(*v1.Job)
mysqlObject := ctl.generateObject(*object)
ctl.makeRevision(object)
db.Create(mysqlObject)
},
UpdateFunc: func(old, new interface{}) {
object := new.(*v1.Job)
mysqlObject := ctl.generateObject(*object)
ctl.makeRevision(object)
db.Save(mysqlObject)
},
DeleteFunc: func(obj interface{}) {
@@ -186,41 +193,41 @@ func getRevisions(job v1.Job) (JobRevisions, error) {
err := json.Unmarshal([]byte(revisionsStr), &revisions)
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", err, err)
return nil, fmt.Errorf("failed to rerun job %s", job.Name)
return nil, fmt.Errorf("failed to get job %s's revisions, reason: %s", job.Name, err)
}
}
return revisions, nil
}
func getStatus(item *v1.Job) JobStatus {
var status JobStatus
func getCurrentRevision(item *v1.Job) JobRevision {
var revision JobRevision
for _, condition := range item.Status.Conditions {
if condition.Type == "Failed" && condition.Status == "True" {
status.Status = Failed
status.Reasons = append(status.Reasons, condition.Reason)
status.Messages = append(status.Messages, condition.Message)
revision.Status = Failed
revision.Reasons = append(revision.Reasons, condition.Reason)
revision.Messages = append(revision.Messages, condition.Message)
}
if condition.Type == "Complete" && condition.Status == "True" {
status.Status = Completed
revision.Status = Completed
}
}
if len(status.Status) == 0 {
status.Status = Unfinished
if len(revision.Status) == 0 {
revision.Status = Running
}
status.DesirePodNum = *item.Spec.Completions
status.Succeed = item.Status.Succeeded
status.Failed = item.Status.Failed
status.StartTime = item.Status.StartTime.Time
revision.DesirePodNum = *item.Spec.Completions
revision.Succeed = item.Status.Succeeded
revision.Failed = item.Status.Failed
revision.StartTime = item.CreationTimestamp.Time
revision.Uid = string(item.UID)
if item.Status.CompletionTime != nil {
status.CompletionTime = item.Status.CompletionTime.Time
revision.CompletionTime = item.Status.CompletionTime.Time
}
return status
return revision
}
func deleteJob(namespace, job string) error {
@@ -229,46 +236,81 @@ func deleteJob(namespace, job string) error {
return err
}
func (ctl *JobCtl) makeRevision(job *v1.Job) {
revisionIndex := -1
revisions, err := getRevisions(*job)
if err != nil {
glog.Error(err)
return
}
uid := job.UID
for index, revision := range revisions {
if revision.Uid == string(uid) {
currentRevision := getCurrentRevision(job)
if reflect.DeepEqual(currentRevision, revision) {
return
} else {
revisionIndex = index
break
}
}
}
if revisionIndex == -1 {
revisionIndex = len(revisions) + 1
}
revisions[revisionIndex] = getCurrentRevision(job)
revisionsByte, err := json.Marshal(revisions)
if err != nil {
glog.Error(err)
}
if job.Annotations == nil {
job.Annotations = make(map[string]string)
}
job.Annotations["revisions"] = string(revisionsByte)
ctl.K8sClient.BatchV1().Jobs(job.Namespace).Update(job)
}
func JobReRun(namespace, jobName string) (string, error) {
k8sClient = client.NewK8sClient()
job, err := k8sClient.BatchV1().Jobs(namespace).Get(jobName, metav1.GetOptions{})
if err != nil {
return "", err
}
newJob := *job
newJob.ResourceVersion = ""
newJob.Status = v1.JobStatus{}
newJob.ObjectMeta.UID = ""
newJob.Annotations["revisions"] = strings.Replace(job.Annotations["revisions"], Running, Unfinished, -1)
delete(newJob.Spec.Selector.MatchLabels, "controller-uid")
delete(newJob.Spec.Template.ObjectMeta.Labels, "controller-uid")
revisions, err := getRevisions(*job)
err = deleteJob(namespace, jobName)
if err != nil {
return "", err
}
index := len(revisions) + 1
value := getStatus(job)
revisions[index] = value
revisionsByte, err := json.Marshal(revisions)
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", err, err)
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
return "", fmt.Errorf("failed to rerun job %s", jobName)
}
newJob.Annotations["revisions"] = string(revisionsByte)
err = deleteJob(job.Namespace, job.Name)
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", err, err)
return "", fmt.Errorf("failed to rerun job %s", jobName)
for i := 0; i < retryTimes; i++ {
_, err = k8sClient.BatchV1().Jobs(namespace).Create(&newJob)
if err != nil {
time.Sleep(time.Second)
continue
}
break
}
_, err = k8sClient.BatchV1().Jobs(namespace).Create(&newJob)
if err != nil {
glog.Errorf("failed to rerun job %s, reason: %s", err, err)
glog.Errorf("failed to rerun job %s, reason: %s", jobName, err)
return "", fmt.Errorf("failed to rerun job %s", jobName)
}

View File

@@ -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)

View File

@@ -38,8 +38,12 @@ func (ctl *PvcCtl) generateObject(item *v1.PersistentVolumeClaim) *Pvc {
name := item.Name
namespace := item.Namespace
status := fmt.Sprintf("%s", item.Status.Phase)
createTime := item.CreationTimestamp.Time
status := fmt.Sprintf("%s", item.Status.Phase)
if item.DeletionTimestamp != nil {
status = "Terminating"
}
var capacity, storageClass, accessModeStr string
if createTime.IsZero() {

View File

@@ -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
}

View File

@@ -30,12 +30,12 @@ import (
func (ctl *RoleCtl) generateObject(item v1.Role) *Role {
var displayName string
if item.Annotations != nil && len(item.Annotations[DisplayName]) > 0 {
if item.Annotations != nil && len(item.Annotations[DisplayName]) == 0 {
displayName = item.Annotations[DisplayName]
}
name := item.Name
if strings.HasPrefix(name, systemPrefix) {
if strings.HasPrefix(name, systemPrefix) || item.Annotations == nil || len(item.Annotations[creator]) == 0 {
return nil
}
namespace := item.Namespace

View File

@@ -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)

View File

@@ -95,7 +95,7 @@ func generateSvcObject(item v1.Service) *Service {
createTime = time.Now()
}
if len(item.Spec.ClusterIP) == 0 {
if len(item.Spec.ClusterIP) == 0 || item.Spec.ClusterIP == "None" {
if len(item.Spec.Selector) == 0 {
serviceType = "Headless(Selector)"
}

View File

@@ -49,6 +49,7 @@ const (
Warning = "warning"
Error = "error"
DisplayName = "displayName"
creator = "creator"
Pods = "pods"
Deployments = "deployments"
@@ -58,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"
@@ -284,15 +287,16 @@ type StorageClass struct {
Provisioner string `json:"provisioner"`
}
type JobRevisions map[int]JobStatus
type JobRevisions map[int]JobRevision
type JobStatus struct {
type JobRevision struct {
Status string `json:"status"`
Reasons []string `json:"reasons"`
Messages []string `json:"messages"`
Succeed int32 `json:"succeed"`
DesirePodNum int32 `json:"desire"`
Failed int32 `json:"failed"`
Uid string `json:"uid"`
StartTime time.Time `json:"start-time"`
CompletionTime time.Time `json:"completion-time"`
}
@@ -462,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

View File

@@ -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
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,15 +29,23 @@ import (
"k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"crypto/tls"
"io/ioutil"
"net/http"
"time"
kubeclient "kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
)
const TYPE = "kubernetes.io/dockerconfigjson"
const SECRET = "Secret"
const APIVERSION = "v1"
const (
TYPE = "kubernetes.io/dockerconfigjson"
SECRET = "Secret"
APIVERSION = "v1"
TYPEHARBOR = "harbor"
TYPEDOCKERHUB = "dockerhub"
TYPEDOCKERREGISTRY = "docker-registry"
)
type AuthInfo struct {
Username string `json:"username"`
@@ -45,6 +53,50 @@ type AuthInfo struct {
ServerHost string `json:"serverhost"`
}
type DockerConfigEntry struct {
Username string `json:"username"`
Password string `json:"password"`
Auth string `json:"auth"`
}
type RegistryInfo struct {
user, password, registryType, url string
}
type dockerConfig map[string]map[string]DockerConfigEntry
type harborRepo struct {
RepoName string `json:"repository_name"`
}
type harborRepos struct {
Repos []harborRepo `json:"repository"`
}
type registryRepos struct {
Repositories []string
}
type registryTags struct {
Name string `json:"name"`
Tags []string `json:"tags"`
}
type dockerhubRepo struct {
RepoName string `json:"repo_name"`
}
type dockerhubRepos struct {
Repositories []dockerhubRepo `json:"results"`
}
type dockerhubTag struct {
TagName string `json:"name"`
}
type dockerhubTags struct {
Tags []dockerhubTag `json:"results"`
}
func NewAuthInfo(para Registries) *AuthInfo {
return &AuthInfo{
@@ -440,3 +492,237 @@ func GetReisgtries(name string) (Registries, error) {
return reg, nil
}
// by image secret to get registry'info, like username, password, registry url ...
func getRegistryInfo(namespace, registryName string) *RegistryInfo {
var registry RegistryInfo
k8sClient := kubeclient.NewK8sClient()
secret, err := k8sClient.CoreV1().Secrets(namespace).Get(registryName, meta_v1.GetOptions{})
if err != nil {
glog.Error(err)
return nil
}
registry.registryType = secret.Annotations["type"]
data := secret.Data[v1.DockerConfigJsonKey]
authsMap := make(dockerConfig)
err = json.Unmarshal(data, &authsMap)
if err != nil {
glog.Error(err)
return nil
}
for url, config := range authsMap["auths"] {
registry.url = url
registry.user = config.Username
registry.password = config.Password
break
}
return &registry
}
func ImageSearch(namespace, registryName, searchWord string) []string {
registry := getRegistryInfo(namespace, registryName)
if registry == nil {
return nil
}
switch registry.registryType {
case TYPEDOCKERHUB:
return searchDockerHub(registry.url, searchWord)
case TYPEDOCKERREGISTRY:
return searchDockerRegistry(registry.url, searchWord)
case TYPEHARBOR:
return searchHarbor(registry.url, registry.user, registry.password, searchWord)
}
return nil
}
func GetImageTags(namespace, registryName, imageName string) []string {
registry := getRegistryInfo(namespace, registryName)
if registry == nil {
return nil
}
switch registry.registryType {
case TYPEDOCKERHUB:
return getTagInDockerHub(registry.url, imageName)
case TYPEDOCKERREGISTRY:
return getTagInDockerRegistry(registry.url, imageName)
case TYPEHARBOR:
return getTagInHarbor(registry.url, registry.user, registry.password, imageName)
}
return nil
}
func httpGet(url, username, password string, insecure bool) ([]byte, error) {
var httpClient *http.Client
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if insecure {
httpClient = &http.Client{}
} else {
req.SetBasicAuth(username, password)
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
httpClient = &http.Client{Timeout: 20 * time.Second, Transport: tr}
}
resp, err := httpClient.Do(req)
if err != nil {
err := fmt.Errorf("Request to %s failed reason: %s ", url, err)
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest || err != nil {
return nil, err
}
return body, nil
}
func searchHarbor(url, username, password, searchWord string) []string {
url = strings.TrimSuffix(url, "/") + fmt.Sprintf("/api/search?q=%s", searchWord)
body, err := httpGet(url, username, password, false)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var repos harborRepos
repoList := make([]string, 0, 100)
err = json.Unmarshal(body, &repos)
if err != nil {
glog.Error(err)
return nil
}
for _, repo := range repos.Repos {
repoList = append(repoList, repo.RepoName)
}
return repoList
}
func searchDockerRegistry(url, searchword string) []string {
url = strings.TrimSuffix(url, "/") + "/v2/_catalog"
body, err := httpGet(url, "", "", true)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var repos registryRepos
err = json.Unmarshal(body, &repos)
if err != nil {
glog.Error(err)
return nil
}
repoList := make([]string, 0, 100)
for _, repo := range repos.Repositories {
if strings.HasPrefix(repo, searchword) {
repoList = append(repoList, repo)
}
}
return repoList
}
func searchDockerHub(url, searchWord string) []string {
url = fmt.Sprintf("https://hub.docker.com/v2/search/repositories/?page=1&query=%s&page_size=50", searchWord)
body, err := httpGet(url, "", "", true)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var repos dockerhubRepos
err = json.Unmarshal(body, &repos)
if err != nil {
glog.Error(err)
return nil
}
repoList := make([]string, 0, 50)
for _, repo := range repos.Repositories {
repoList = append(repoList, repo.RepoName)
}
return repoList
}
func getTagInHarbor(url, username, password, imageName string) []string {
url = strings.TrimSuffix(url, "/") + fmt.Sprintf("/api/repositories/%s/tags", imageName)
body, err := httpGet(url, username, password, false)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var tagList []string
err = json.Unmarshal(body, &tagList)
if err != nil {
glog.Error(err)
return nil
}
return tagList
}
func getTagInDockerRegistry(url, imageName string) []string {
url = strings.TrimSuffix(url, "/") + fmt.Sprintf("/v2/%s/tags/list", imageName)
body, err := httpGet(url, "", "", true)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var tags registryTags
err = json.Unmarshal(body, &tags)
if err != nil {
glog.Error(err)
return nil
}
return tags.Tags
}
func getTagInDockerHub(url, imageName string) []string {
if !strings.Contains(imageName, "/") {
imageName = fmt.Sprintf("library/%s", imageName)
}
url = fmt.Sprintf("https://hub.docker.com/v2/repositories/%s/tags/?page=1&page_size=200", imageName)
body, err := httpGet(url, "", "", true)
if err != nil || len(body) == 0 {
glog.Error(err)
return nil
}
var tags dockerhubTags
err = json.Unmarshal(body, &tags)
if err != nil {
glog.Error(err)
return nil
}
tagList := make([]string, 0, 200)
for _, tag := range tags.Tags {
tagList = append(tagList, tag.TagName)
}
return tagList
}

0
pkg/models/metrics/containers.go Normal file → Executable file
View File

View File

@@ -1,184 +0,0 @@
/*
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 metrics
import (
"encoding/json"
"regexp"
"strings"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/client"
)
func getPodNameRegexInWorkload(request *restful.Request) string {
promql := MakeWorkloadRule(request)
res := client.SendPrometheusRequest(request, promql)
data := []byte(res)
var dat CommonMetricsResult
jsonErr := json.Unmarshal(data, &dat)
if jsonErr != nil {
glog.Errorln("json parse failed", jsonErr)
}
var podNames []string
for _, x := range dat.Data.Result {
podName := x.KubePodMetric.Pod
podNames = append(podNames, podName)
}
podNamesFilter := "^(" + strings.Join(podNames, "|") + ")$"
return podNamesFilter
}
func MonitorWorkloadSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
podNamesFilter := getPodNameRegexInWorkload(request)
newPromql := MakePodPromQL(request, []string{metricsName, nsName, "", "", podNamesFilter})
podMetrics := client.SendPrometheusRequest(request, newPromql)
cleanedJson := ReformatJson(podMetrics, metricsName)
return cleanedJson
}
func MonitorPodSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
nodeID := strings.Trim(request.PathParameter("node_id"), " ")
podName := strings.Trim(request.PathParameter("pod_name"), " ")
podFilter := strings.Trim(request.QueryParameter("pods_filter"), " ")
params := []string{metricsName, nsName, nodeID, podName, podFilter}
promql := MakePodPromQL(request, params)
if promql != "" {
res := client.SendPrometheusRequest(request, promql)
cleanedJson := ReformatJson(res, metricsName)
return cleanedJson
}
return nil
}
func MonitorNamespaceSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
recordingRule := MakeNamespacePromQL(request, metricsName)
res := client.SendPrometheusRequest(request, recordingRule)
cleanedJson := ReformatJson(res, metricsName)
return cleanedJson
}
func ReformatJson(metric string, metricsName string) *FormatedMetric {
var formatMetric FormatedMetric
err := json.Unmarshal([]byte(metric), &formatMetric)
if err != nil {
glog.Errorln("Unmarshal metric json failed", err)
}
if formatMetric.MetricName == "" {
formatMetric.MetricName = metricsName
}
// retrive metrics success
if formatMetric.Status == "success" {
result := formatMetric.Data.Result
for _, res := range result {
metric, ok := res["metric"]
me := metric.(map[string]interface{})
if ok {
delete(me, "__name__")
}
}
}
return &formatMetric
}
func collectNodeorClusterMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorNodeorClusterSingleMetric(request, metricsName)
ch <- metric
}
func collectNamespaceMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorNamespaceSingleMetric(request, metricsName)
ch <- metric
}
func collectWorkloadMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metricsName = strings.TrimLeft(metricsName, "workload_")
metric := MonitorWorkloadSingleMetric(request, metricsName)
ch <- metric
}
func collectPodMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorPodSingleMetric(request, metricsName)
ch <- metric
}
func MonitorAllMetrics(request *restful.Request) FormatedLevelMetric {
metricsName := strings.Trim(request.QueryParameter("metrics_filter"), " ")
if metricsName == "" {
metricsName = ".*"
}
path := request.SelectedRoutePath()
sourceType := path[strings.LastIndex(path, "/")+1 : len(path)-1]
if strings.Contains(path, "workload") {
sourceType = "workload"
}
var ch = make(chan *FormatedMetric, 10)
for _, k := range MetricsNames {
bol, err := regexp.MatchString(metricsName, k)
if !bol {
continue
}
if err != nil {
glog.Errorln("regex match failed", err)
continue
}
if strings.HasPrefix(k, sourceType) {
if sourceType == "node" || sourceType == "cluster" {
go collectNodeorClusterMetrics(request, k, ch)
} else if sourceType == "namespace" {
go collectNamespaceMetrics(request, k, ch)
} else if sourceType == "pod" {
go collectPodMetrics(request, k, ch)
} else if sourceType == "workload" {
go collectWorkloadMetrics(request, k, ch)
}
}
}
var metricsArray []FormatedMetric
var tempJson *FormatedMetric
for _, k := range MetricsNames {
bol, err := regexp.MatchString(metricsName, k)
if !bol {
continue
}
if err != nil {
glog.Errorln("regex match failed")
continue
}
if strings.HasPrefix(k, sourceType) {
tempJson = <-ch
if tempJson != nil {
metricsArray = append(metricsArray, *tempJson)
}
}
}
return FormatedLevelMetric{
MetricsLevel: sourceType,
Results: metricsArray,
}
}
func MonitorNodeorClusterSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
recordingRule := MakeNodeorClusterRule(request, metricsName)
res := client.SendPrometheusRequest(request, recordingRule)
cleanedJson := ReformatJson(res, metricsName)
return cleanedJson
}

View File

@@ -1,147 +0,0 @@
/*
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 metrics
type MetricMap map[string]string
var MetricsNames = []string{
"cluster_cpu_utilisation",
"cluster_memory_utilisation",
"cluster_net_utilisation",
"cluster_pod_count",
"node_cpu_utilisation",
"node_memory_utilisation",
"node_memory_available",
"node_memory_total",
"node_net_utilisation",
"node_net_bytes_transmitted",
"node_net_bytes_received",
"node_disk_read_iops",
"node_disk_write_iops",
"node_disk_read_throughput",
"node_disk_write_throughput",
"node_disk_capacity",
"node_disk_available",
"node_disk_utilization",
"namespace_cpu_utilisation",
"namespace_memory_utilisation",
"namespace_memory_utilisation_wo_cache",
"namespace_net_bytes_transmitted",
"namespace_net_bytes_received",
"namespace_pod_count",
"pod_cpu_utilisation",
"pod_memory_utilisation",
"pod_memory_utilisation_wo_cache",
"pod_net_bytes_transmitted",
"pod_net_bytes_received",
"workload_pod_cpu_utilisation",
"workload_pod_memory_utilisation",
"workload_pod_memory_utilisation_wo_cache",
"workload_pod_net_bytes_transmitted",
"workload_pod_net_bytes_received",
//"container_cpu_utilisation",
//"container_memory_utilisation_wo_cache",
//"container_memory_utilisation",
"tenant_cpu_utilisation",
"tenant_memory_utilisation",
"tenant_memory_utilisation_wo_cache",
"tenant_net_bytes_transmitted",
"tenant_net_bytes_received",
"tenant_pod_count",
}
var RulePromQLTmplMap = MetricMap{
//cluster
"cluster_cpu_utilisation": ":node_cpu_utilisation:avg1m",
"cluster_memory_utilisation": ":node_memory_utilisation:",
// Cluster network utilisation (bytes received + bytes transmitted per second)
"cluster_net_utilisation": ":node_net_utilisation:sum_irate",
"cluster_pod_count": `count(kube_pod_info{job="kube-state-metrics"})`,
//node
"node_cpu_utilisation": "node:node_cpu_utilisation:avg1m",
"node_memory_utilisation": "node:node_memory_utilisation:",
"node_memory_available": "node:node_memory_bytes_available:sum",
"node_memory_total": "node:node_memory_bytes_total:sum",
// Node network utilisation (bytes received + bytes transmitted per second)
"node_net_utilisation": "node:node_net_utilisation:sum_irate",
// Node network bytes transmitted per second
"node_net_bytes_transmitted": "node:node_net_bytes_transmitted:sum_irate",
// Node network bytes received per second
"node_net_bytes_received": "node:node_net_bytes_received:sum_irate",
// node:data_volume_iops_reads:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_read_iops": "node:data_volume_iops_reads:sum",
// node:data_volume_iops_writes:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_write_iops": "node:data_volume_iops_writes:sum",
// node:data_volume_throughput_bytes_read:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_read_throughput": "node:data_volume_throughput_bytes_read:sum",
// node:data_volume_throughput_bytes_written:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_write_throughput": "node:data_volume_throughput_bytes_written:sum",
"node_disk_capacity": `sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_disk_available": `sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_disk_utilization": `sum by (node) (((node_filesystem_size{mountpoint="/", job="node-exporter"} - node_filesystem_avail{mountpoint="/", job="node-exporter"}) / node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
//namespace
"namespace_cpu_utilisation": `namespace:container_cpu_usage_seconds_total:sum_rate{namespace=~"$1"}`,
"namespace_memory_utilisation": `namespace:container_memory_usage_bytes:sum{namespace=~"$1"}`,
"namespace_memory_utilisation_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace=~"$1"}`,
"namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[2m]))`,
"namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[2m]))`,
// count(kube_pod_info) by (namespace) namespace=~"monitoring|default|kube-system"
"namespace_pod_count": `count(kube_pod_info{job="kube-state-metrics", namespace=~"$1"}) by (namespace)`,
// pod
"pod_cpu_utilisation": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name="$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_utilisation": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name="$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_utilisation_wo_cache": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name="$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name="$2",image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name="$2", interface="eth0", job="kubelet"}[2m]))`,
"pod_net_bytes_received": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name="$2", interface="eth0", job="kubelet"}[2m]))`,
"pod_cpu_utilisation_all": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_utilisation_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_utilisation_wo_cache_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name=~"$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted_all": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", interface="eth0", job="kubelet"}[2m]))`,
"pod_net_bytes_received_all": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", interface="eth0", job="kubelet"}[2m]))`,
//"pod_cpu_utilisation_node": `sum by (node, pod) (label_join(irate(container_cpu_usage_seconds_total{job="kubelet", image!=""}[5m]), "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
"pod_cpu_utilisation_node": `sum by (node, pod) (label_join(irate(container_cpu_usage_seconds_total{job="kubelet",pod_name=~"$2", image!=""}[5m]), "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
"pod_memory_utilisation_node": `sum by (node, pod) (label_join(container_memory_usage_bytes{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
"pod_memory_utilisation_wo_cache_node": `sum by (node, pod) ((label_join(container_memory_usage_bytes{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name") - label_join(container_memory_cache{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name")) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
// container
"container_cpu_utilisation": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", pod_name="$2", container_name="$3"}[5m])) by (namespace, pod_name, container_name)`,
//"container_cpu_utilisation_wo_podname": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", container_name=~"$3"}[5m])) by (namespace, pod_name, container_name)`,
"container_cpu_utilisation_all": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}[5m])) by (namespace, pod_name, container_name)`,
//"container_cpu_utilisation_all_wo_podname": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", container_name!="POD"}[5m])) by (namespace, pod_name, container_name)`,
"container_memory_utilisation_wo_cache": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name="$3"} - ignoring(id, image, endpoint, instance, job, name, service) container_memory_cache{namespace="$1", pod_name="$2", container_name="$3"}`,
"container_memory_utilisation_wo_cache_all": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"} - ignoring(id, image, endpoint, instance, job, name, service) container_memory_cache{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}`,
"container_memory_utilisation": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name="$3"}`,
"container_memory_utilisation_all": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}`,
// tenant
"tenant_cpu_utilisation": `sum(namespace:container_cpu_usage_seconds_total:sum_rate{namespace =~"$1"})`,
"tenant_memory_utilisation": `sum(namespace:container_memory_usage_bytes:sum{namespace =~"$1"})`,
"tenant_memory_utilisation_wo_cache": `sum(namespace:container_memory_usage_bytes_wo_cache:sum{namespace =~"$1"})`,
"tenant_net_bytes_transmitted": `sum(sum by (namespace) (irate(container_network_transmit_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[2m])))`,
"tenant_net_bytes_received": `sum(sum by (namespace) (irate(container_network_receive_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[2m])))`,
"tenant_pod_count": `sum(count(kube_pod_info{job="kube-state-metrics", namespace=~"$1"}) by (namespace))`,
}

View File

@@ -0,0 +1,539 @@
/*
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 metrics
import (
"encoding/json"
"regexp"
"strings"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"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 {
promql := MakeWorkloadRule(request)
res := client.SendPrometheusRequest(request, promql)
data := []byte(res)
var dat CommonMetricsResult
jsonErr := json.Unmarshal(data, &dat)
if jsonErr != nil {
glog.Errorln("json parse failed", jsonErr)
}
var podNames []string
for _, x := range dat.Data.Result {
podName := x.KubePodMetric.Pod
podNames = append(podNames, podName)
}
podNamesFilter := "^(" + strings.Join(podNames, "|") + ")$"
return podNamesFilter
}
func MonitorWorkloadSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
podNamesFilter := getPodNameRegexInWorkload(request)
newPromql := MakePodPromQL(request, []string{metricsName, nsName, "", "", podNamesFilter})
podMetrics := client.SendPrometheusRequest(request, newPromql)
cleanedJson := ReformatJson(podMetrics, metricsName)
return cleanedJson
}
func MonitorPodSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
nodeID := strings.Trim(request.PathParameter("node_id"), " ")
podName := strings.Trim(request.PathParameter("pod_name"), " ")
podFilter := strings.Trim(request.QueryParameter("pods_filter"), " ")
params := []string{metricsName, nsName, nodeID, podName, podFilter}
promql := MakePodPromQL(request, params)
if promql != "" {
res := client.SendPrometheusRequest(request, promql)
cleanedJson := ReformatJson(res, metricsName)
return cleanedJson
}
return nil
}
func MonitorNamespaceSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
recordingRule := MakeNamespacePromQL(request, metricsName)
res := client.SendPrometheusRequest(request, recordingRule)
cleanedJson := ReformatJson(res, metricsName)
return cleanedJson
}
// maybe this function is time consuming
func ReformatJson(metric string, metricsName string) *FormatedMetric {
var formatMetric FormatedMetric
err := json.Unmarshal([]byte(metric), &formatMetric)
if err != nil {
glog.Errorln("Unmarshal metric json failed", err)
}
if formatMetric.MetricName == "" {
formatMetric.MetricName = metricsName
}
// retrive metrics success
if formatMetric.Status == MetricStatusSuccess {
result := formatMetric.Data.Result
for _, res := range result {
metric, ok := res[ResultItemMetric]
me := metric.(map[string]interface{})
if ok {
delete(me, "__name__")
}
}
}
return &formatMetric
}
func collectNodeorClusterMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorNodeorClusterSingleMetric(request, metricsName)
ch <- metric
}
func collectNamespaceMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorNamespaceSingleMetric(request, metricsName)
ch <- metric
}
func collectWorkloadMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metricsName = strings.TrimLeft(metricsName, "workload_")
metric := MonitorWorkloadSingleMetric(request, metricsName)
ch <- metric
}
func collectWorkspaceMetrics(request *restful.Request, metricsName string, namespaceList []string, ch chan<- *FormatedMetric) {
mertic := monitorWorkspaceSingleMertic(request, metricsName, namespaceList)
ch <- mertic
}
func collectPodMetrics(request *restful.Request, metricsName string, ch chan<- *FormatedMetric) {
metric := MonitorPodSingleMetric(request, metricsName)
ch <- metric
}
func monitorWorkspaceSingleMertic(request *restful.Request, metricsName string, namespaceList []string) *FormatedMetric {
namespaceRe2 := "^(" + strings.Join(namespaceList, "|") + ")$"
newpromql := MakeWorkspacePromQL(metricsName, namespaceRe2)
podMetrics := client.SendPrometheusRequest(request, newpromql)
cleanedJson := ReformatJson(podMetrics, metricsName)
return cleanedJson
}
func filterNamespace(request *restful.Request, namespaceList []string) []string {
var newNSlist []string
nsFilter := strings.Trim(request.QueryParameter("namespaces_filter"), " ")
if nsFilter == "" {
nsFilter = ".*"
}
for _, ns := range namespaceList {
bol, _ := regexp.MatchString(nsFilter, ns)
if bol {
newNSlist = append(newNSlist, ns)
}
}
return newNSlist
}
func MonitorAllMetrics(request *restful.Request) FormatedLevelMetric {
metricsName := strings.Trim(request.QueryParameter("metrics_filter"), " ")
if metricsName == "" {
metricsName = ".*"
}
path := request.SelectedRoutePath()
sourceType := path[strings.LastIndex(path, "/")+1 : len(path)-1]
if strings.Contains(path, MetricLevelWorkload) {
sourceType = MetricLevelWorkload
} else if strings.Contains(path, MetricLevelWorkspace) {
sourceType = MetricLevelWorkspace
}
var ch = make(chan *FormatedMetric, 10)
for _, metricName := range MetricsNames {
bol, err := regexp.MatchString(metricsName, metricName)
if !bol {
continue
}
if err != nil {
glog.Errorln("regex match failed", err)
continue
}
if strings.HasPrefix(metricName, sourceType) {
if sourceType == MetricLevelCluster || sourceType == MetricLevelNode {
go collectNodeorClusterMetrics(request, metricName, ch)
} else if sourceType == MetricLevelNamespace {
go collectNamespaceMetrics(request, metricName, ch)
} else if sourceType == MetricLevelPod {
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)
}
}
}
var metricsArray []FormatedMetric
var tempJson *FormatedMetric
for _, k := range MetricsNames {
bol, err := regexp.MatchString(metricsName, k)
if !bol {
continue
}
if err != nil {
glog.Errorln("regex match failed")
continue
}
if strings.HasPrefix(k, sourceType) {
tempJson = <-ch
if tempJson != nil {
metricsArray = append(metricsArray, *tempJson)
}
}
}
return FormatedLevelMetric{
MetricsLevel: sourceType,
Results: metricsArray,
}
}
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]
nsFilter := "^(" + strings.Join(namespaces, "|") + ")$"
recordingRule = strings.Replace(recordingRule, "$1", nsFilter, -1)
res := client.SendPrometheusRequest(request, recordingRule)
cleanedJson := ReformatJson(res, metricName)
return cleanedJson
}
func getWorkspaceWorkloadCountMetrics(namespaces []string) FormatedMetric {
var wlQuotaMetrics models.ResourceQuota
wlQuotaMetrics.NameSpace = strings.Join(namespaces, "|")
wlQuotaMetrics.Data.Used = make(v1.ResourceList, 1)
wlQuotaMetrics.Data.Hard = make(v1.ResourceList, 1)
for _, ns := range namespaces {
quotaMetric, err := models.GetNamespaceQuota(ns)
if err != nil {
glog.Errorln(err)
continue
}
// sum all resources used along namespaces
quotaUsed := quotaMetric.Data.Used
for resourceName, quantity := range quotaUsed {
if _, ok := wlQuotaMetrics.Data.Used[resourceName]; ok {
tmpQuantity := wlQuotaMetrics.Data.Used[v1.ResourceName(resourceName)]
tmpQuantity.Add(quantity)
wlQuotaMetrics.Data.Used[v1.ResourceName(resourceName)] = tmpQuantity
} else {
wlQuotaMetrics.Data.Used[v1.ResourceName(resourceName)] = quantity.DeepCopy()
}
}
// sum all resources hard along namespaces
quotaHard := quotaMetric.Data.Hard
for resourceName, quantity := range quotaHard {
if _, ok := wlQuotaMetrics.Data.Hard[resourceName]; ok {
tmpQuantity := wlQuotaMetrics.Data.Hard[v1.ResourceName(resourceName)]
tmpQuantity.Add(quantity)
wlQuotaMetrics.Data.Hard[v1.ResourceName(resourceName)] = tmpQuantity
} else {
wlQuotaMetrics.Data.Hard[v1.ResourceName(resourceName)] = quantity.DeepCopy()
}
}
}
wlMetrics := convertQuota2MetricStruct(&wlQuotaMetrics)
return wlMetrics
}
func getSpecificMetricItem(timestamp int64, metricName string, kind string, count int, err error) FormatedMetric {
var nsMetrics FormatedMetric
nsMetrics.MetricName = metricName
nsMetrics.Data.ResultType = ResultTypeVector
resultItem := make(map[string]interface{})
tmp := make(map[string]string)
tmp[ResultItemMetricResource] = kind
if err == nil {
nsMetrics.Status = MetricStatusSuccess
} else {
nsMetrics.Status = MetricStatusError
resultItem["errorinfo"] = err.Error()
}
resultItem[ResultItemMetric] = tmp
resultItem[ResultItemValue] = []interface{}{timestamp, count}
nsMetrics.Data.Result = make([]map[string]interface{}, 1)
nsMetrics.Data.Result[0] = resultItem
return nsMetrics
}
func MonitorNodeorClusterSingleMetric(request *restful.Request, metricsName string) *FormatedMetric {
// support cluster node statistic, include healthy nodes and unhealthy nodes
var res string
var fMetric FormatedMetric
timestamp := int64(time.Now().Unix())
if metricsName == "cluster_node_online" {
onlineNodes, _ := getNodeHealthyConditionMetric()
fMetric = getSpecificMetricItem(timestamp, MetricNameClusterHealthyNodeCount, "node_count", len(onlineNodes), nil)
} else if metricsName == "cluster_node_offline" {
_, offlineNodes := getNodeHealthyConditionMetric()
fMetric = getSpecificMetricItem(timestamp, MetricNameClusterUnhealthyNodeCount, "node_count", len(offlineNodes), nil)
} else if metricsName == "cluster_node_total" {
onlineNodes, offlineNodes := getNodeHealthyConditionMetric()
fMetric = getSpecificMetricItem(timestamp, MetricNameClusterNodeCount, "node_count", len(onlineNodes)+len(offlineNodes), nil)
} else {
recordingRule := MakeNodeorClusterRule(request, metricsName)
res = client.SendPrometheusRequest(request, recordingRule)
fMetric = *ReformatJson(res, metricsName)
}
return &fMetric
}
func getNodeHealthyConditionMetric() ([]string, []string) {
nodeList, err := client.NewK8sClient().CoreV1().Nodes().List(metaV1.ListOptions{})
if err != nil {
glog.Errorln(err)
return nil, nil
}
var onlineNodes []string
var offlineNodes []string
for _, node := range nodeList.Items {
nodeName := node.Labels["kubernetes.io/hostname"]
nodeRole := node.Labels["role"]
bol := true
for _, cond := range node.Status.Conditions {
if cond.Type == "Ready" && cond.Status == "Unknown" {
bol = false
break
}
}
if nodeRole != "log" {
if bol {
// reachable node
onlineNodes = append(onlineNodes, nodeName)
} else {
// unreachable node
offlineNodes = append(offlineNodes, nodeName)
}
}
}
return onlineNodes, offlineNodes
}
func getExistingNamespace(namespaces []string) ([]string, []string) {
namespaceMap, err := getAllNamespace()
var existedNs []string
var noneExistedNs []string
if err != nil {
return namespaces, nil
}
for _, ns := range namespaces {
if _, ok := namespaceMap[ns]; ok {
existedNs = append(existedNs, ns)
} else {
noneExistedNs = append(noneExistedNs, ns)
}
}
return existedNs, noneExistedNs
}
func getAllNamespace() (map[string]int, error) {
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 {
namespaceMap[item.Name] = 0
}
return namespaceMap, nil
}
func MonitorWorkloadCount(request *restful.Request) FormatedMetric {
namespace := strings.Trim(request.PathParameter("ns_name"), " ")
quotaMetric, err := models.GetNamespaceQuota(namespace)
fMetric := convertQuota2MetricStruct(quotaMetric)
// whether the namespace in request parameters exists?
namespaceMap, e := getAllNamespace()
_, ok := namespaceMap[namespace]
if e != nil {
ok = true
}
if !ok || err != nil {
fMetric.Status = MetricStatusError
fMetric.Data.ResultType = ""
errInfo := make(map[string]interface{})
if err != nil {
errInfo["errormsg"] = err.Error()
} else {
errInfo["errormsg"] = "namespace " + namespace + " does not exist"
}
fMetric.Data.Result = []map[string]interface{}{errInfo}
}
return fMetric
}
func convertQuota2MetricStruct(quotaMetric *models.ResourceQuota) FormatedMetric {
var fMetric FormatedMetric
fMetric.MetricName = MetricNameWorkloadCount
fMetric.Status = MetricStatusSuccess
fMetric.Data.ResultType = ResultTypeVector
timestamp := int64(time.Now().Unix())
var resultItems []map[string]interface{}
hardMap := make(map[string]string)
for resourceName, v := range quotaMetric.Data.Hard {
hardMap[resourceName.String()] = v.String()
}
for resourceName, v := range quotaMetric.Data.Used {
resultItem := make(map[string]interface{})
tmp := make(map[string]string)
tmp[ResultItemMetricResource] = resourceName.String()
resultItem[ResultItemMetric] = tmp
resultItem[ResultItemValue] = []interface{}{timestamp, hardMap[resourceName.String()], v.String()}
resultItems = append(resultItems, resultItem)
}
fMetric.Data.Result = resultItems
return fMetric
}

View File

@@ -0,0 +1,236 @@
/*
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 metrics
const (
ResultTypeVector = "vector"
ResultTypeMatrix = "matrix"
MetricStatusError = "error"
MetricStatusSuccess = "success"
ResultItemMetric = "metric"
ResultItemMetricResource = "resource"
ResultItemValue = "value"
)
const (
MetricNameWorkloadCount = "workload_count"
MetricNameNamespacePodCount = "namespace_pod_count"
MetricNameWorkspaceAllOrganizationCount = "workspace_all_organization_count"
MetricNameWorkspaceAllAccountCount = "workspace_all_account_count"
MetricNameWorkspaceAllProjectCount = "workspace_all_project_count"
MetricNameWorkspaceAllDevopsCount = "workspace_all_devops_project_count"
MetricNameWorkspaceNamespaceCount = "workspace_namespace_count"
MetricNameWorkspaceDevopsCount = "workspace_devops_project_count"
MetricNameWorkspaceMemberCount = "workspace_member_count"
MetricNameWorkspaceRoleCount = "workspace_role_count"
MetricNameClusterHealthyNodeCount = "cluster_node_online"
MetricNameClusterUnhealthyNodeCount = "cluster_node_offline"
MetricNameClusterNodeCount = "cluster_node_total"
)
const (
WorkspaceResourceKindOrganization = "organization"
WorkspaceResourceKindAccount = "account"
WorkspaceResourceKindNamespace = "namespace"
WorkspaceResourceKindDevops = "devops"
WorkspaceResourceKindMember = "member"
WorkspaceResourceKindRole = "role"
)
const (
MetricLevelCluster = "cluster"
MetricLevelNode = "node"
MetricLevelWorkspace = "workspace"
MetricLevelNamespace = "namespace"
MetricLevelPod = "pod"
MetricLevelContainer = "container"
MetricLevelWorkload = "workload"
)
type MetricMap map[string]string
var MetricsNames = []string{
"cluster_cpu_utilisation",
"cluster_cpu_usage",
"cluster_cpu_total",
"cluster_memory_utilisation",
"cluster_pod_count",
"cluster_memory_bytes_available",
"cluster_memory_bytes_total",
"cluster_memory_bytes_usage",
"cluster_net_utilisation",
"cluster_net_bytes_transmitted",
"cluster_net_bytes_received",
"cluster_disk_read_iops",
"cluster_disk_write_iops",
"cluster_disk_read_throughput",
"cluster_disk_write_throughput",
"cluster_disk_size_usage",
"cluster_disk_size_utilisation",
"cluster_disk_size_capacity",
"cluster_disk_size_available",
"cluster_node_online",
"cluster_node_offline",
"cluster_node_total",
"node_cpu_utilisation",
"node_cpu_total",
"node_cpu_usage",
"node_memory_utilisation",
"node_memory_bytes_usage",
"node_memory_bytes_available",
"node_memory_bytes_total",
"node_net_utilisation",
"node_net_bytes_transmitted",
"node_net_bytes_received",
"node_disk_read_iops",
"node_disk_write_iops",
"node_disk_read_throughput",
"node_disk_write_throughput",
"node_disk_size_capacity",
"node_disk_size_available",
"node_disk_size_usage",
"node_disk_size_utilisation",
"node_pod_count",
"node_pod_quota",
"namespace_cpu_usage",
"namespace_memory_usage",
"namespace_memory_usage_wo_cache",
"namespace_net_bytes_transmitted",
"namespace_net_bytes_received",
"namespace_pod_count",
"pod_cpu_usage",
"pod_memory_usage",
"pod_memory_usage_wo_cache",
"pod_net_bytes_transmitted",
"pod_net_bytes_received",
"workload_pod_cpu_usage",
"workload_pod_memory_usage",
"workload_pod_memory_usage_wo_cache",
"workload_pod_net_bytes_transmitted",
"workload_pod_net_bytes_received",
//"container_cpu_usage",
//"container_memory_usage_wo_cache",
//"container_memory_usage",
"workspace_cpu_usage",
"workspace_memory_usage",
"workspace_memory_usage_wo_cache",
"workspace_net_bytes_transmitted",
"workspace_net_bytes_received",
"workspace_pod_count",
}
var RulePromQLTmplMap = MetricMap{
//cluster
"cluster_cpu_utilisation": ":node_cpu_utilisation:avg1m",
"cluster_cpu_usage": `sum (irate(container_cpu_usage_seconds_total{job="kubelet", image!=""}[5m]))`,
"cluster_cpu_total": "sum(node:node_num_cpu:sum)",
"cluster_memory_utilisation": ":node_memory_utilisation:",
"cluster_pod_count": `count(kube_pod_info unless on(pod) kube_pod_completion_time unless on(node) kube_node_labels{label_role="log"})`,
"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": ":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)",
"cluster_disk_write_iops": "sum(node:data_volume_iops_writes:sum)",
"cluster_disk_read_throughput": "sum(node:data_volume_throughput_bytes_read:sum)",
"cluster_disk_write_throughput": "sum(node:data_volume_throughput_bytes_written:sum)",
"cluster_disk_size_usage": `sum(sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:)) - sum(sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:))`,
"cluster_disk_size_utilisation": `(sum(sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:)) - sum(sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:))) / sum(sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:))`,
"cluster_disk_size_capacity": `sum(sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:))`,
"cluster_disk_size_available": `sum(sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:))`,
//node
"node_cpu_utilisation": "node:node_cpu_utilisation:avg1m",
"node_cpu_total": "node:node_num_cpu:sum",
"node_memory_utilisation": "node:node_memory_utilisation:",
"node_memory_bytes_available": "node:node_memory_bytes_available:sum",
"node_memory_bytes_total": "node:node_memory_bytes_total:sum",
// Node network utilisation (bytes received + bytes transmitted per second)
"node_net_utilisation": "node:node_net_utilisation:sum_irate",
// Node network bytes transmitted per second
"node_net_bytes_transmitted": "node:node_net_bytes_transmitted:sum_irate",
// Node network bytes received per second
"node_net_bytes_received": "node:node_net_bytes_received:sum_irate",
// node:data_volume_iops_reads:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_read_iops": "node:data_volume_iops_reads:sum",
// node:data_volume_iops_writes:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_write_iops": "node:data_volume_iops_writes:sum",
// node:data_volume_throughput_bytes_read:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_read_throughput": "node:data_volume_throughput_bytes_read:sum",
// node:data_volume_throughput_bytes_written:sum{node=~"i-5xcldxos|i-6soe9zl1"}
"node_disk_write_throughput": "node:data_volume_throughput_bytes_written:sum",
"node_disk_size_capacity": `sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_disk_size_available": `sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_disk_size_usage": `sum by (node) ((node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1) -sum by (node) ((node_filesystem_avail{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_disk_size_utilisation": `sum by (node) (((node_filesystem_size{mountpoint="/", job="node-exporter"} - node_filesystem_avail{mountpoint="/", job="node-exporter"}) / node_filesystem_size{mountpoint="/", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_pod_count": `count(kube_pod_info$1 unless on(pod) kube_pod_completion_time) by (node)`,
// without log node: unless on(node) kube_node_labels{label_role="log"}
"node_pod_quota": `sum(kube_node_status_capacity_pods$1) by (node)`,
"node_cpu_usage": `sum by (node) (label_join(irate(container_cpu_usage_seconds_total{job="kubelet", image!=""}[5m]), "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:$1)`,
"node_memory_bytes_usage": "node:node_memory_bytes_total:sum$1 - node:node_memory_bytes_available:sum$1",
//namespace
"namespace_cpu_usage": `namespace:container_cpu_usage_seconds_total:sum_rate{namespace=~"$1"}`,
"namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace=~"$1"}`,
"namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace=~"$1"}`,
"namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[5m]))`,
"namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[5m]))`,
"namespace_pod_count": `count(kube_pod_info{namespace=~"$1"} unless on(pod) kube_pod_completion_time) by (namespace)`,
// pod
"pod_cpu_usage": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name="$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_usage": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name="$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_usage_wo_cache": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name="$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name="$2",image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name="$2", interface="eth0", job="kubelet"}[5m]))`,
"pod_net_bytes_received": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name="$2", interface="eth0", job="kubelet"}[5m]))`,
"pod_cpu_usage_all": `sum(irate(container_cpu_usage_seconds_total{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}[5m])) by (namespace, pod_name)`,
"pod_memory_usage_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_memory_usage_wo_cache_all": `sum(container_memory_usage_bytes{job="kubelet", namespace="$1", pod_name=~"$2", image!=""} - container_memory_cache{job="kubelet", namespace="$1", pod_name=~"$2", image!=""}) by (namespace, pod_name)`,
"pod_net_bytes_transmitted_all": `sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", interface="eth0", job="kubelet"}[5m]))`,
"pod_net_bytes_received_all": `sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{namespace="$1", pod_name!="", pod_name=~"$2", interface="eth0", job="kubelet"}[5m]))`,
"pod_cpu_usage_node": `sum by (node, pod) (label_join(irate(container_cpu_usage_seconds_total{job="kubelet",pod_name=~"$2", image!=""}[5m]), "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
"pod_memory_usage_node": `sum by (node, pod) (label_join(container_memory_usage_bytes{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name") * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
"pod_memory_usage_wo_cache_node": `sum by (node, pod) ((label_join(container_memory_usage_bytes{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name") - label_join(container_memory_cache{job="kubelet",pod_name=~"$2", image!=""}, "pod", " ", "pod_name")) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{node=~"$3"})`,
// container
"container_cpu_usage": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", pod_name="$2", container_name="$3"}[5m])) by (namespace, pod_name, container_name)`,
"container_cpu_usage_all": `sum(irate(container_cpu_usage_seconds_total{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}[5m])) by (namespace, pod_name, container_name)`,
"container_memory_usage_wo_cache": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name="$3"} - ignoring(id, image, endpoint, instance, job, name, service) container_memory_cache{namespace="$1", pod_name="$2", container_name="$3"}`,
"container_memory_usage_wo_cache_all": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"} - ignoring(id, image, endpoint, instance, job, name, service) container_memory_cache{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}`,
"container_memory_usage": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name="$3"}`,
"container_memory_usage_all": `container_memory_usage_bytes{namespace="$1", pod_name="$2", container_name=~"$3", container_name!="POD"}`,
// enterprise
"workspace_cpu_usage": `sum(namespace:container_cpu_usage_seconds_total:sum_rate{namespace =~"$1"})`,
"workspace_memory_usage": `sum(namespace:container_memory_usage_bytes:sum{namespace =~"$1"})`,
"workspace_memory_usage_wo_cache": `sum(namespace:container_memory_usage_bytes_wo_cache:sum{namespace =~"$1"})`,
"workspace_net_bytes_transmitted": `sum(sum by (namespace) (irate(container_network_transmit_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[5m])))`,
"workspace_net_bytes_received": `sum(sum by (namespace) (irate(container_network_receive_bytes_total{namespace=~"$1", pod_name!="", interface="eth0", job="kubelet"}[5m])))`,
"workspace_pod_count": `sum(count(kube_pod_info{namespace=~"$1"} unless on(pod) kube_pod_completion_time) by (namespace))`,
}

View File

@@ -67,6 +67,12 @@ func MakeWorkloadRule(request *restful.Request) string {
return rule
}
func MakeWorkspacePromQL(metricsName string, namespaceRe2 string) string {
promql := RulePromQLTmplMap[metricsName]
promql = strings.Replace(promql, "$1", namespaceRe2, -1)
return promql
}
func MakeContainerPromQL(request *restful.Request) string {
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
poName := strings.Trim(request.PathParameter("pod_name"), " ")
@@ -169,9 +175,8 @@ func MakeNodeorClusterRule(request *restful.Request, metricsName string) string
if nodesFilter == "" {
nodesFilter = ".*"
}
if strings.Contains(metricsName, "disk") && (!(strings.Contains(metricsName, "read") || strings.Contains(metricsName, "write"))) {
if strings.Contains(metricsName, "disk_size") || strings.Contains(metricsName, "pod") || strings.Contains(metricsName, "usage") {
// disk size promql
nodesFilter := ""
if nodeID != "" {
nodesFilter = "{" + "node" + "=" + "\"" + nodeID + "\"" + "}"
} else {

0
pkg/models/metrics/nodes.go Normal file → Executable file
View File

0
pkg/models/metrics/pods.go Normal file → Executable file
View File

View File

@@ -220,6 +220,8 @@ func ListResource(resourceName, conditonSrt, pagingStr, order string) (*Resource
}
func generateConditionStr(conditions *searchConditions) string {
shouldUseAnd := false
shouldUseBrackets := false
conditionStr := ""
if conditions == nil {
@@ -242,11 +244,21 @@ func generateConditionStr(conditions *searchConditions) string {
}
}
if len(conditionStr) > 0 {
shouldUseAnd = true
}
for k, v := range conditions.matchOr {
if len(conditionStr) == 0 {
conditionStr = fmt.Sprintf("%s = \"%s\" ", k, v)
} else {
conditionStr = fmt.Sprintf("%s OR %s = \"%s\" ", conditionStr, k, v)
if shouldUseAnd {
conditionStr = fmt.Sprintf("%s And (%s = \"%s\" ", conditionStr, k, v)
shouldUseBrackets = true
shouldUseAnd = false
} else {
conditionStr = fmt.Sprintf("%s OR %s = \"%s\" ", conditionStr, k, v)
}
}
}
@@ -254,10 +266,20 @@ func generateConditionStr(conditions *searchConditions) string {
if len(conditionStr) == 0 {
conditionStr = fmt.Sprintf("%s like '%%%s%%' ", k, v)
} else {
conditionStr = fmt.Sprintf("%s OR %s like '%%%s%%' ", conditionStr, k, v)
if shouldUseAnd {
conditionStr = fmt.Sprintf("%s And (%s like '%%%s%%' ", conditionStr, k, v)
shouldUseAnd = false
shouldUseBrackets = true
} else {
conditionStr = fmt.Sprintf("%s OR %s like '%%%s%%' ", conditionStr, k, v)
}
}
}
if shouldUseBrackets {
conditionStr = fmt.Sprintf("%s )", conditionStr)
}
return conditionStr
}

View File

@@ -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 {

View File

@@ -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")
}