@@ -18,11 +18,8 @@
|
||||
package applications
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
v12 "k8s.io/api/apps/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -33,24 +30,11 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/models/resources"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
OpenPitrixProxyToken string
|
||||
OpenPitrixServer string
|
||||
)
|
||||
|
||||
const (
|
||||
unknown = "-"
|
||||
deploySuffix = "-Deployment"
|
||||
daemonSuffix = "-DaemonSet"
|
||||
stateSuffix = "-StatefulSet"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Name string `json:"name"`
|
||||
RepoName string `json:"repoName"`
|
||||
@@ -70,174 +54,93 @@ type Application struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
}
|
||||
|
||||
type clusterRole struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Name string `json:"name"`
|
||||
AppID string `json:"app_id"`
|
||||
VersionID string `json:"version_id"`
|
||||
Status string `json:"status"`
|
||||
UpdateTime time.Time `json:"status_time"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
RunTimeId string `json:"runtime_id"`
|
||||
Description string `json:"description"`
|
||||
ClusterRoleSets []clusterRole `json:"cluster_role_set"`
|
||||
}
|
||||
|
||||
type clusters struct {
|
||||
Total int `json:"total_count"`
|
||||
Clusters []cluster `json:"cluster_set"`
|
||||
}
|
||||
|
||||
type versionList struct {
|
||||
Total int `json:"total_count"`
|
||||
Versions []version `json:"app_version_set"`
|
||||
}
|
||||
|
||||
type version struct {
|
||||
Name string `json:"name"`
|
||||
VersionID string `json:"version_id"`
|
||||
}
|
||||
|
||||
type runtime struct {
|
||||
RuntimeID string `json:"runtime_id"`
|
||||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
type runtimeList struct {
|
||||
Total int `json:"total_count"`
|
||||
Runtimes []runtime `json:"runtime_set"`
|
||||
}
|
||||
|
||||
type app struct {
|
||||
AppId string `json:"app_id"`
|
||||
Name string `json:"name"`
|
||||
ChartName string `json:"chart_name"`
|
||||
RepoId string `json:"repo_id"`
|
||||
}
|
||||
|
||||
type repo struct {
|
||||
RepoId string `json:"repo_id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
||||
type workLoads struct {
|
||||
Deployments []v12.Deployment `json:"deployments,omitempty"`
|
||||
Statefulsets []v12.StatefulSet `json:"statefulsets,omitempty"`
|
||||
Daemonsets []v12.DaemonSet `json:"daemonsets,omitempty"`
|
||||
Deployments []appsv1.Deployment `json:"deployments,omitempty"`
|
||||
Statefulsets []appsv1.StatefulSet `json:"statefulsets,omitempty"`
|
||||
Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty"`
|
||||
}
|
||||
|
||||
type appList struct {
|
||||
Total int `json:"total_count"`
|
||||
Apps []app `json:"app_set"`
|
||||
func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
|
||||
clusterList, err := openpitrix.ListClusters(runtimeId, conditions.Match["keyword"], conditions.Match["status"], limit, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := models.PageableResponse{TotalCount: clusterList.Total}
|
||||
result.Items = make([]interface{}, 0)
|
||||
for _, item := range clusterList.Clusters {
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := openpitrix.GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
runtimeInfo, _ := openpitrix.GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, _, appId, _ := openpitrix.GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
result.Items = append(result.Items, app)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
type repoList struct {
|
||||
Total int `json:"total_count"`
|
||||
Repos []repo `json:"repo_set"`
|
||||
func GetApp(clusterId string) (*Application, error) {
|
||||
|
||||
item, err := openpitrix.GetCluster(clusterId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.CreateTime = item.CreateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := openpitrix.GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
|
||||
runtimeInfo, _ := openpitrix.GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, repoId, appId, _ := openpitrix.GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
app.RepoName, _ = openpitrix.GetRepo(repoId)
|
||||
|
||||
workloads, err := getWorkLoads(app.Runtime, item.ClusterRoleSets)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
app.WorkLoads = workloads
|
||||
workloadLabels := getLabels(app.Runtime, app.WorkLoads)
|
||||
app.Services = getSvcs(app.Runtime, workloadLabels)
|
||||
app.Ingresses = getIng(app.Runtime, app.Services)
|
||||
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
func GetAppInfo(appId string) (string, string, string, error) {
|
||||
url := fmt.Sprintf("%s/v1/apps?app_id=%s", OpenPitrixServer, appId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
var apps appList
|
||||
err = json.Unmarshal(resp, &apps)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
if len(apps.Apps) == 0 {
|
||||
return unknown, unknown, unknown, err
|
||||
}
|
||||
|
||||
return apps.Apps[0].ChartName, apps.Apps[0].RepoId, apps.Apps[0].AppId, nil
|
||||
}
|
||||
|
||||
func GetRepo(repoId string) (string, error) {
|
||||
url := fmt.Sprintf("%s/v1/repos?repo_id=%s", OpenPitrixServer, repoId)
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var repos repoList
|
||||
err = json.Unmarshal(resp, &repos)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(repos.Repos) == 0 {
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
return repos.Repos[0].Name, nil
|
||||
}
|
||||
|
||||
func GetVersion(versionId string) (string, error) {
|
||||
versionUrl := fmt.Sprintf("%s/v1/app_versions?version_id=%s", OpenPitrixServer, versionId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var versions versionList
|
||||
err = json.Unmarshal(resp, &versions)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(versions.Versions) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
return versions.Versions[0].Name, nil
|
||||
}
|
||||
|
||||
func GetRuntime(runtimeId string) (string, error) {
|
||||
|
||||
versionUrl := fmt.Sprintf("%s/v1/runtimes?runtime_id=%s", OpenPitrixServer, runtimeId)
|
||||
resp, err := makeHttpRequest("GET", versionUrl, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
var runtimes runtimeList
|
||||
err = json.Unmarshal(resp, &runtimes)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
if len(runtimes.Runtimes) == 0 {
|
||||
return unknown, nil
|
||||
}
|
||||
|
||||
return runtimes.Runtimes[0].Zone, nil
|
||||
}
|
||||
|
||||
func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, error) {
|
||||
func getWorkLoads(namespace string, clusterRoles []openpitrix.ClusterRole) (*workLoads, error) {
|
||||
|
||||
var works workLoads
|
||||
for _, clusterRole := range clusterRoles {
|
||||
workLoadName := clusterRole.Role
|
||||
if len(workLoadName) > 0 {
|
||||
if strings.HasSuffix(workLoadName, deploySuffix) {
|
||||
name := strings.Split(workLoadName, deploySuffix)[0]
|
||||
if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) {
|
||||
name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0]
|
||||
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
|
||||
|
||||
@@ -249,8 +152,8 @@ func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, err
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, daemonSuffix) {
|
||||
name := strings.Split(workLoadName, daemonSuffix)[0]
|
||||
if strings.HasSuffix(workLoadName, openpitrix.DaemonSuffix) {
|
||||
name := strings.Split(workLoadName, openpitrix.DaemonSuffix)[0]
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -259,8 +162,8 @@ func GetWorkLoads(namespace string, clusterRoles []clusterRole) (*workLoads, err
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasSuffix(workLoadName, stateSuffix) {
|
||||
name := strings.Split(workLoadName, stateSuffix)[0]
|
||||
if strings.HasSuffix(workLoadName, openpitrix.StateSuffix) {
|
||||
name := strings.Split(workLoadName, openpitrix.StateSuffix)[0]
|
||||
item, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -346,7 +249,7 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
|
||||
|
||||
var ings []v1beta1.Ingress
|
||||
for _, svc := range services {
|
||||
result, err := resources.ListNamespaceResource(namespace, "ingress", ¶ms.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0)
|
||||
result, err := resources.ListResources(namespace, "ingress", ¶ms.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil
|
||||
@@ -379,159 +282,3 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
|
||||
|
||||
return ings
|
||||
}
|
||||
|
||||
func ListApplication(runtimeId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) {
|
||||
if strings.HasSuffix(OpenPitrixServer, "/") {
|
||||
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
|
||||
}
|
||||
|
||||
defaultStatus := "status=active&status=stopped&status=pending&status=ceased"
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?limit=%s&offset=%s", OpenPitrixServer, strconv.Itoa(limit), strconv.Itoa(offset))
|
||||
|
||||
if len(conditions.Fuzzy["name"]) > 0 {
|
||||
url = fmt.Sprintf("%s&search_word=%s", url, conditions.Fuzzy["name"])
|
||||
}
|
||||
|
||||
if len(conditions.Match["status"]) > 0 {
|
||||
url = fmt.Sprintf("%s&status=%s", url, conditions.Match["status"])
|
||||
} else {
|
||||
url = fmt.Sprintf("%s&%s", url, defaultStatus)
|
||||
}
|
||||
|
||||
if len(runtimeId) > 0 {
|
||||
url = fmt.Sprintf("%s&runtime_id=%s", url, runtimeId)
|
||||
}
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Errorf("request %s failed, reason: %s", url, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := models.PageableResponse{TotalCount: clusterList.Total}
|
||||
result.Items = make([]interface{}, 0)
|
||||
for _, item := range clusterList.Clusters {
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
runtimeInfo, _ := GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, _, appId, _ := GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
result.Items = append(result.Items, app)
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func GetApp(clusterId string) (*Application, error) {
|
||||
if strings.HasSuffix(OpenPitrixServer, "/") {
|
||||
OpenPitrixServer = strings.TrimSuffix(OpenPitrixServer, "/")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/clusters?cluster_id=%s", OpenPitrixServer, clusterId)
|
||||
|
||||
resp, err := makeHttpRequest("GET", url, "")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clusterList clusters
|
||||
err = json.Unmarshal(resp, &clusterList)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(clusterList.Clusters) == 0 {
|
||||
return nil, fmt.Errorf("NotFound, clusterId:%s", clusterId)
|
||||
}
|
||||
|
||||
item := clusterList.Clusters[0]
|
||||
var app Application
|
||||
|
||||
app.Name = item.Name
|
||||
app.ClusterID = item.ClusterID
|
||||
app.UpdateTime = item.UpdateTime
|
||||
app.CreateTime = item.CreateTime
|
||||
app.Status = item.Status
|
||||
versionInfo, _ := GetVersion(item.VersionID)
|
||||
app.Version = versionInfo
|
||||
app.VersionId = item.VersionID
|
||||
|
||||
runtimeInfo, _ := GetRuntime(item.RunTimeId)
|
||||
app.Runtime = runtimeInfo
|
||||
app.RuntimeId = item.RunTimeId
|
||||
appInfo, repoId, appId, _ := GetAppInfo(item.AppID)
|
||||
app.App = appInfo
|
||||
app.AppId = appId
|
||||
app.Description = item.Description
|
||||
|
||||
app.RepoName, _ = GetRepo(repoId)
|
||||
|
||||
workloads, err := GetWorkLoads(app.Runtime, item.ClusterRoleSets)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
app.WorkLoads = workloads
|
||||
workloadLabels := getLabels(app.Runtime, app.WorkLoads)
|
||||
app.Services = getSvcs(app.Runtime, workloadLabels)
|
||||
app.Ingresses = getIng(app.Runtime, app.Services)
|
||||
|
||||
return &app, nil
|
||||
}
|
||||
|
||||
func makeHttpRequest(method, url, data string) ([]byte, error) {
|
||||
var req *http.Request
|
||||
|
||||
var err error
|
||||
if method == "GET" {
|
||||
req, err = http.NewRequest(method, url, nil)
|
||||
} else {
|
||||
req, err = http.NewRequest(method, url, strings.NewReader(data))
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", OpenPitrixProxyToken)
|
||||
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
httpClient := &http.Client{}
|
||||
resp, err := httpClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err)
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
err = fmt.Errorf(string(body))
|
||||
}
|
||||
return body, err
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 iam
|
||||
|
||||
import "sync"
|
||||
|
||||
type Counter struct {
|
||||
value int
|
||||
m *sync.Mutex
|
||||
}
|
||||
|
||||
func NewCounter(value int) Counter {
|
||||
c := Counter{}
|
||||
c.m = &sync.Mutex{}
|
||||
c.Set(value)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Counter) Set(value int) {
|
||||
c.m.Lock()
|
||||
c.value = value
|
||||
c.m.Unlock()
|
||||
}
|
||||
|
||||
func (c *Counter) Add(value int) {
|
||||
c.m.Lock()
|
||||
c.value += value
|
||||
c.m.Unlock()
|
||||
}
|
||||
|
||||
func (c *Counter) Sub(value int) {
|
||||
c.m.Lock()
|
||||
c.value -= value
|
||||
c.m.Unlock()
|
||||
}
|
||||
|
||||
func (c *Counter) Get() int {
|
||||
return c.value
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 iam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
)
|
||||
|
||||
const ClusterRoleKind = "ClusterRole"
|
||||
|
||||
// Get user list based on workspace role
|
||||
func WorkspaceRoleUsers(workspace string, roleName string) ([]models.User, error) {
|
||||
|
||||
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
|
||||
|
||||
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
names := make([]string, 0)
|
||||
|
||||
for _, subject := range workspaceRoleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind {
|
||||
names = append(names, subject.Name)
|
||||
}
|
||||
}
|
||||
|
||||
users, err := GetUsers(names)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(users); i++ {
|
||||
users[i].WorkspaceRole = roleName
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUsers(names []string) ([]models.User, error) {
|
||||
var users []models.User
|
||||
|
||||
if names == nil || len(names) == 0 {
|
||||
return make([]models.User, 0), nil
|
||||
}
|
||||
|
||||
conn, err := ldap.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
user, err := UserDetail(name, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, *user)
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUser(name string) (*models.User, error) {
|
||||
|
||||
conn, err := ldap.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := UserDetail(name, conn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) {
|
||||
|
||||
clusterRoles, err := GetClusterRoles(username)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
clusterRules := make([]v1.PolicyRule, 0)
|
||||
for _, role := range clusterRoles {
|
||||
clusterRules = append(clusterRules, role.Rules...)
|
||||
}
|
||||
|
||||
if requiredRule.Size() == 0 {
|
||||
if RulesMatchesRequired(clusterRules, v1.PolicyRule{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
}) {
|
||||
return true, nil, nil
|
||||
}
|
||||
} else {
|
||||
|
||||
if RulesMatchesRequired(clusterRules, requiredRule) {
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
roles, err := GetRoles("", username)
|
||||
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
rulesMapping := make(map[string][]v1.PolicyRule, 0)
|
||||
|
||||
for _, role := range roles {
|
||||
rules := rulesMapping[role.Namespace]
|
||||
if rules == nil {
|
||||
rules = make([]v1.PolicyRule, 0)
|
||||
}
|
||||
rules = append(rules, role.Rules...)
|
||||
rulesMapping[role.Namespace] = rules
|
||||
}
|
||||
|
||||
namespaces = make([]string, 0)
|
||||
|
||||
for namespace, rules := range rulesMapping {
|
||||
if requiredRule.Size() == 0 || RulesMatchesRequired(rules, requiredRule) {
|
||||
namespaces = append(namespaces, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return false, namespaces, nil
|
||||
}
|
||||
|
||||
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
|
||||
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
|
||||
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
|
||||
for _, s := range clusterRoleBinding.Subjects {
|
||||
if s.Kind == v1.UserKind && !slice.ContainsString(users, s.Name, nil) {
|
||||
users = append(users, s.Name)
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
|
||||
for _, rule := range rules {
|
||||
if ruleMatchesRequired(rule, required) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -22,9 +22,12 @@ import (
|
||||
"fmt"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/redis"
|
||||
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -38,11 +41,10 @@ import (
|
||||
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
|
||||
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
|
||||
)
|
||||
|
||||
var (
|
||||
counter Counter
|
||||
adminEmail string
|
||||
adminPassword string
|
||||
tokenExpireTime time.Duration
|
||||
@@ -82,7 +84,7 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
|
||||
nil,
|
||||
)
|
||||
|
||||
groups, err := conn.Search(groupSearchRequest)
|
||||
_, err := conn.Search(groupSearchRequest)
|
||||
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
err = createGroupsBaseDN(conn)
|
||||
@@ -95,14 +97,6 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
|
||||
return fmt.Errorf("iam database init failed: %s\n", err)
|
||||
}
|
||||
|
||||
if groups == nil || len(groups.Entries) == 0 {
|
||||
_, err = CreateGroup(models.Group{Path: constants.SystemWorkspace, Name: constants.SystemWorkspace, Creator: constants.AdminUserName, Description: "system workspace"})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("system-workspace create failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -130,13 +124,10 @@ func checkAndCreateDefaultUser(conn ldap.Client) error {
|
||||
}
|
||||
|
||||
if users == nil || len(users.Entries) == 0 {
|
||||
counter = NewCounter(0)
|
||||
err := CreateUser(models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
|
||||
_, err := CreateUser(&models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
|
||||
if err != nil {
|
||||
return fmt.Errorf("admin create failed: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
counter = NewCounter(len(users.Entries))
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -164,12 +155,12 @@ func createGroupsBaseDN(conn ldap.Client) error {
|
||||
}
|
||||
|
||||
// User login
|
||||
func Login(username string, password string, ip string) (string, error) {
|
||||
func Login(username string, password string, ip string) (*models.Token, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
@@ -185,11 +176,11 @@ func Login(username string, password string, ip string) (string, error) {
|
||||
result, err := conn.Search(userSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result.Entries) != 1 {
|
||||
return "", ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
|
||||
return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
|
||||
}
|
||||
|
||||
uid := result.Entries[0].GetAttributeValue("uid")
|
||||
@@ -200,7 +191,7 @@ func Login(username string, password string, ip string) (string, error) {
|
||||
err = conn.Bind(dn, password)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims := jwt.MapClaims{}
|
||||
@@ -209,13 +200,11 @@ func Login(username string, password string, ip string) (string, error) {
|
||||
claims["username"] = uid
|
||||
claims["email"] = email
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
uToken, _ := token.SignedString(jwtutils.Secret)
|
||||
token := jwtutil.MustSigned(claims)
|
||||
|
||||
loginLog(uid, ip)
|
||||
|
||||
return uToken, nil
|
||||
return &models.Token{Token: token}, nil
|
||||
}
|
||||
|
||||
func loginLog(uid, ip string) {
|
||||
@@ -226,99 +215,6 @@ func loginLog(uid, ip string) {
|
||||
}
|
||||
}
|
||||
|
||||
func UserList(limit int, offset int) (int, []models.User, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
users := make([]models.User, 0)
|
||||
|
||||
pageControl := ldap.NewControlPaging(1000)
|
||||
|
||||
entries := make([]*ldap.Entry, 0)
|
||||
|
||||
cursor := 0
|
||||
l1:
|
||||
for {
|
||||
|
||||
userSearchRequest := ldap.NewSearchRequest(
|
||||
ldapclient.UserSearchBase,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(&(objectClass=inetOrgPerson))",
|
||||
[]string{"uid", "mail", "description"},
|
||||
[]ldap.Control{pageControl},
|
||||
)
|
||||
|
||||
response, err := conn.Search(userSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
for _, entry := range response.Entries {
|
||||
cursor++
|
||||
if cursor > offset {
|
||||
if len(entries) < limit {
|
||||
entries = append(entries, entry)
|
||||
} else {
|
||||
break l1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
|
||||
if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 {
|
||||
pageControl.SetCookie(ctrl.Cookie)
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
for _, v := range entries {
|
||||
|
||||
uid := v.GetAttributeValue("uid")
|
||||
email := v.GetAttributeValue("mail")
|
||||
description := v.GetAttributeValue("description")
|
||||
user := models.User{Username: uid, Email: email, Description: description}
|
||||
|
||||
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if len(avatar) > 0 {
|
||||
if url, ok := avatar[0].(string); ok {
|
||||
user.AvatarUrl = url
|
||||
}
|
||||
}
|
||||
|
||||
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if len(lastLogin) > 0 {
|
||||
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
|
||||
}
|
||||
|
||||
user.ClusterRules = make([]models.SimpleRule, 0)
|
||||
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return counter.Get(), users, nil
|
||||
}
|
||||
|
||||
func LoginLog(username string) ([]string, error) {
|
||||
redisClient := redis.Client()
|
||||
|
||||
@@ -331,48 +227,77 @@ func LoginLog(username string) ([]string, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func Search(keyword string, limit int, offset int) (int, []models.User, error) {
|
||||
func ListUsersByName(names []string) (*models.PageableResponse, error) {
|
||||
users := make([]*models.User, 0)
|
||||
|
||||
for _, name := range names {
|
||||
if !k8sutil.ContainsUser(users, name) {
|
||||
user, err := DescribeUser(name)
|
||||
if err != nil {
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]interface{}, 0)
|
||||
|
||||
for _, u := range users {
|
||||
items = append(items, u)
|
||||
}
|
||||
|
||||
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
|
||||
}
|
||||
|
||||
func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
users := make([]models.User, 0)
|
||||
|
||||
pageControl := ldap.NewControlPaging(80)
|
||||
|
||||
entries := make([]*ldap.Entry, 0)
|
||||
users := make([]models.User, 0)
|
||||
|
||||
filter := "(&(objectClass=inetOrgPerson))"
|
||||
|
||||
if keyword := conditions.Match["keyword"]; keyword != "" {
|
||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
|
||||
}
|
||||
|
||||
cursor := 0
|
||||
l1:
|
||||
for {
|
||||
userSearchRequest := ldap.NewSearchRequest(
|
||||
ldapclient.UserSearchBase,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword),
|
||||
[]string{"uid", "mail", "description"},
|
||||
filter,
|
||||
[]string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"},
|
||||
[]ldap.Control{pageControl},
|
||||
)
|
||||
|
||||
response, err := conn.Search(userSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range response.Entries {
|
||||
cursor++
|
||||
if cursor > offset {
|
||||
if len(entries) < limit {
|
||||
entries = append(entries, entry)
|
||||
} else {
|
||||
break l1
|
||||
}
|
||||
}
|
||||
|
||||
uid := entry.GetAttributeValue("uid")
|
||||
email := entry.GetAttributeValue("mail")
|
||||
description := entry.GetAttributeValue("description")
|
||||
lang := entry.GetAttributeValue("preferredLanguage")
|
||||
createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp"))
|
||||
|
||||
user := models.User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
|
||||
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
|
||||
@@ -384,52 +309,104 @@ l1:
|
||||
break
|
||||
}
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
for _, v := range entries {
|
||||
|
||||
uid := v.GetAttributeValue("uid")
|
||||
email := v.GetAttributeValue("mail")
|
||||
description := v.GetAttributeValue("description")
|
||||
user := models.User{Username: uid, Email: email, Description: description}
|
||||
|
||||
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
if reverse {
|
||||
tmp := i
|
||||
i = j
|
||||
j = tmp
|
||||
}
|
||||
switch orderBy {
|
||||
case "username":
|
||||
fallthrough
|
||||
case "createTime":
|
||||
return users[i].CreateTime.Before(users[j].CreateTime)
|
||||
default:
|
||||
return strings.Compare(users[i].Username, users[j].Username) <= 0
|
||||
}
|
||||
})
|
||||
|
||||
if len(avatar) > 0 {
|
||||
if url, ok := avatar[0].(string); ok {
|
||||
user.AvatarUrl = url
|
||||
items := make([]interface{}, 0)
|
||||
|
||||
for i, user := range users {
|
||||
|
||||
if i >= offset && len(items) < limit {
|
||||
|
||||
avatar, err := getAvatar(user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.AvatarUrl = avatar
|
||||
|
||||
lastLoginTime, err := getLastLoginTime(user.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.LastLoginTime = lastLoginTime
|
||||
|
||||
clusterRole, err := GetUserClusterRole(user.Username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.ClusterRole = clusterRole.Name
|
||||
|
||||
items = append(items, user)
|
||||
}
|
||||
|
||||
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if len(lastLogin) > 0 {
|
||||
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
|
||||
}
|
||||
|
||||
user.ClusterRules = make([]models.SimpleRule, 0)
|
||||
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
return counter.Get(), users, nil
|
||||
return &models.PageableResponse{Items: items, TotalCount: len(users)}, nil
|
||||
}
|
||||
|
||||
func UserDetail(username string, conn ldap.Client) (*models.User, error) {
|
||||
func DescribeUser(username string) (*models.User, error) {
|
||||
|
||||
user, err := GetUserInfo(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groups, err := GetUserGroups(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.Groups = groups
|
||||
|
||||
avatar, err := getAvatar(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.AvatarUrl = avatar
|
||||
|
||||
lastLoginTime, err := getLastLoginTime(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user.LastLoginTime = lastLoginTime
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Get user info only included email description & lang
|
||||
func GetUserInfo(username string) (*models.User, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userSearchRequest := ldap.NewSearchRequest(
|
||||
ldapclient.UserSearchBase,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username),
|
||||
[]string{"mail", "description", "preferredLanguage"},
|
||||
[]string{"mail", "description", "preferredLanguage", "createTimestamp"},
|
||||
nil,
|
||||
)
|
||||
|
||||
@@ -446,7 +423,20 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
|
||||
email := result.Entries[0].GetAttributeValue("mail")
|
||||
description := result.Entries[0].GetAttributeValue("description")
|
||||
lang := result.Entries[0].GetAttributeValue("preferredLanguage")
|
||||
user := models.User{Username: username, Email: email, Description: description, Lang: lang}
|
||||
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
|
||||
user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func GetUserGroups(username string) ([]string, error) {
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
groupSearchRequest := ldap.NewSearchRequest(
|
||||
ldapclient.GroupSearchBase,
|
||||
@@ -456,11 +446,10 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
|
||||
nil,
|
||||
)
|
||||
|
||||
result, err = conn.Search(groupSearchRequest)
|
||||
result, err := conn.Search(groupSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
groups := make([]string, 0)
|
||||
@@ -470,41 +459,47 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
|
||||
groups = append(groups, groupName)
|
||||
}
|
||||
|
||||
user.Groups = groups
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
avatar, err := redisClient.HMGet("kubesphere:users:avatar", username).Result()
|
||||
func getLastLoginTime(username string) (string, error) {
|
||||
lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(lastLogin) > 0 {
|
||||
return strings.Split(lastLogin[0], ",")[0], nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func setAvatar(username, avatar string) error {
|
||||
_, err := redis.Client().HMSet("kubesphere:users:avatar", map[string]interface{}{"username": avatar}).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
func getAvatar(username string) (string, error) {
|
||||
|
||||
avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(avatar) > 0 {
|
||||
if url, ok := avatar[0].(string); ok {
|
||||
user.AvatarUrl = url
|
||||
return url, nil
|
||||
}
|
||||
}
|
||||
|
||||
user.Status = 0
|
||||
|
||||
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(lastLogin) > 0 {
|
||||
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func DeleteUser(username string) error {
|
||||
|
||||
// bind root DN
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -521,13 +516,7 @@ func DeleteUser(username string) error {
|
||||
|
||||
err = deleteRoleBindings(username)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
counter.Sub(1)
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func deleteRoleBindings(username string) error {
|
||||
@@ -539,7 +528,7 @@ func deleteRoleBindings(username string) error {
|
||||
}
|
||||
|
||||
for _, roleBinding := range roleBindings {
|
||||
|
||||
roleBinding = roleBinding.DeepCopy()
|
||||
length1 := len(roleBinding.Subjects)
|
||||
|
||||
for index, subject := range roleBinding.Subjects {
|
||||
@@ -571,6 +560,7 @@ func deleteRoleBindings(username string) error {
|
||||
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
|
||||
|
||||
for _, clusterRoleBinding := range clusterRoleBindings {
|
||||
clusterRoleBinding = clusterRoleBinding.DeepCopy()
|
||||
length1 := len(clusterRoleBinding.Subjects)
|
||||
|
||||
for index, subject := range clusterRoleBinding.Subjects {
|
||||
@@ -637,7 +627,7 @@ func UserCreateCheck(check string) (exist bool, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func CreateUser(user models.User) error {
|
||||
func CreateUser(user *models.User) (*models.User, error) {
|
||||
user.Username = strings.TrimSpace(user.Username)
|
||||
user.Email = strings.TrimSpace(user.Email)
|
||||
user.Password = strings.TrimSpace(user.Password)
|
||||
@@ -646,7 +636,7 @@ func CreateUser(user models.User) error {
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
@@ -662,17 +652,17 @@ func CreateUser(user models.User) error {
|
||||
result, err := conn.Search(userSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result.Entries) > 0 {
|
||||
return ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
|
||||
return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
|
||||
}
|
||||
|
||||
maxUid, err := getMaxUid(conn)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
maxUid += 1
|
||||
@@ -688,7 +678,7 @@ func CreateUser(user models.User) error {
|
||||
userCreateRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox
|
||||
userCreateRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user
|
||||
if user.Lang != "" {
|
||||
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang}) // RFC4519/2307: password of user
|
||||
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang})
|
||||
}
|
||||
if user.Description != "" {
|
||||
userCreateRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information
|
||||
@@ -697,16 +687,22 @@ func CreateUser(user models.User) error {
|
||||
err = conn.Add(userCreateRequest)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
counter.Add(1)
|
||||
if user.AvatarUrl != "" {
|
||||
setAvatar(user.Username, user.AvatarUrl)
|
||||
}
|
||||
|
||||
if user.ClusterRole != "" {
|
||||
CreateClusterRoleBinding(user.Username, user.ClusterRole)
|
||||
err := CreateClusterRoleBinding(user.Username, user.ClusterRole)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return DescribeUser(user.Username)
|
||||
}
|
||||
|
||||
func getMaxUid(conn ldap.Client) (int, error) {
|
||||
@@ -768,11 +764,12 @@ func getMaxGid(conn ldap.Client) (int, error) {
|
||||
return maxGid, nil
|
||||
}
|
||||
|
||||
func UpdateUser(user models.User) error {
|
||||
func UpdateUser(user *models.User) (*models.User, error) {
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
@@ -794,19 +791,27 @@ func UpdateUser(user models.User) error {
|
||||
userModifyRequest.Replace("userPassword", []string{user.Password})
|
||||
}
|
||||
|
||||
if user.AvatarUrl != "" {
|
||||
err = setAvatar(user.Username, user.AvatarUrl)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = conn.Modify(userModifyRequest)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = CreateClusterRoleBinding(user.Username, user.ClusterRole)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return DescribeUser(user.Username)
|
||||
}
|
||||
func DeleteGroup(path string) error {
|
||||
|
||||
@@ -829,13 +834,14 @@ func DeleteGroup(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateGroup(group models.Group) (*models.Group, error) {
|
||||
func CreateGroup(group *models.Group) (*models.Group, error) {
|
||||
|
||||
// bind root DN
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
maxGid, err := getMaxGid(conn)
|
||||
@@ -861,7 +867,9 @@ func CreateGroup(group models.Group) (*models.Group, error) {
|
||||
groupCreateRequest.Attribute("description", []string{group.Description})
|
||||
}
|
||||
|
||||
groupCreateRequest.Attribute("memberUid", []string{group.Creator})
|
||||
if group.Members != nil {
|
||||
groupCreateRequest.Attribute("memberUid", group.Members)
|
||||
}
|
||||
|
||||
err = conn.Add(groupCreateRequest)
|
||||
|
||||
@@ -871,18 +879,7 @@ func CreateGroup(group models.Group) (*models.Group, error) {
|
||||
|
||||
group.Gid = strconv.Itoa(maxGid)
|
||||
|
||||
group.CreateTime = time.Now().UTC().Format("2006-01-02T15:04:05Z")
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
if err := redisClient.HMSet("kubesphere:groups:create-time", map[string]interface{}{group.Name: group.CreateTime}).Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := redisClient.HMSet("kubesphere:groups:creator", map[string]interface{}{group.Name: group.Creator}).Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &group, nil
|
||||
return DescribeGroup(group.Path)
|
||||
}
|
||||
|
||||
func UpdateGroup(group *models.Group) (*models.Group, error) {
|
||||
@@ -894,7 +891,7 @@ func UpdateGroup(group *models.Group) (*models.Group, error) {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
old, err := GroupDetail(group.Path, conn)
|
||||
old, err := DescribeGroup(group.Path)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1027,34 +1024,22 @@ func ChildList(path string) ([]models.Group, error) {
|
||||
|
||||
group.ChildGroups = childGroups
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
|
||||
|
||||
if len(createTime) > 0 {
|
||||
if t, ok := createTime[0].(string); ok {
|
||||
group.CreateTime = t
|
||||
}
|
||||
}
|
||||
|
||||
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
|
||||
|
||||
if len(creator) > 0 {
|
||||
if t, ok := creator[0].(string); ok {
|
||||
group.Creator = t
|
||||
}
|
||||
}
|
||||
|
||||
groups = append(groups, group)
|
||||
}
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
|
||||
func DescribeGroup(path string) (*models.Group, error) {
|
||||
|
||||
searchBase, cn := splitPath(path)
|
||||
|
||||
conn, err := ldapclient.Client()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupSearchRequest := ldap.NewSearchRequest(searchBase,
|
||||
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn),
|
||||
@@ -1083,24 +1068,76 @@ func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
|
||||
|
||||
group.ChildGroups = childGroups
|
||||
|
||||
redisClient := redis.Client()
|
||||
|
||||
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
|
||||
|
||||
if len(createTime) > 0 {
|
||||
if t, ok := createTime[0].(string); ok {
|
||||
group.CreateTime = t
|
||||
}
|
||||
}
|
||||
|
||||
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
|
||||
|
||||
if len(creator) > 0 {
|
||||
if t, ok := creator[0].(string); ok {
|
||||
group.Creator = t
|
||||
}
|
||||
}
|
||||
|
||||
return &group, nil
|
||||
|
||||
}
|
||||
|
||||
func WorkspaceUsersTotalCount(workspace string) (int, error) {
|
||||
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
users := make([]string, 0)
|
||||
|
||||
for _, roleBinding := range workspaceRoleBindings {
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
|
||||
users = append(users, subject.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(users), nil
|
||||
}
|
||||
|
||||
func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
|
||||
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*models.User, 0)
|
||||
|
||||
for _, roleBinding := range workspaceRoleBindings {
|
||||
for _, subject := range roleBinding.Subjects {
|
||||
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
|
||||
user, err := DescribeUser(subject.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prefix := fmt.Sprintf("workspace:%s:", workspace)
|
||||
user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix))
|
||||
users = append(users, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// order & reverse
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
if reverse {
|
||||
tmp := i
|
||||
i = j
|
||||
j = tmp
|
||||
}
|
||||
switch orderBy {
|
||||
default:
|
||||
fallthrough
|
||||
case "name":
|
||||
return strings.Compare(users[i].Username, users[j].Username) <= 0
|
||||
}
|
||||
})
|
||||
|
||||
result := make([]interface{}, 0)
|
||||
|
||||
for i, d := range users {
|
||||
if i >= offset && (limit == -1 || len(result) < limit) {
|
||||
result = append(result, d)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package policy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
@@ -55,292 +56,25 @@ func init() {
|
||||
}
|
||||
|
||||
var (
|
||||
WorkspaceRoleRuleMapping = []models.Rule{
|
||||
{
|
||||
Name: "workspaces",
|
||||
Actions: []models.Action{
|
||||
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
}, {
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"jenkins.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
}, {
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"devops.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{Name: "members",
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"patch", "update"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "devops",
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update", "patch"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/devops"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "projects",
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update", "patch"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "organizations",
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"workspaces/organizations"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"workspaces/organizations"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "edit",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update", "patch"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"workspaces/organizations"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "delete",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"workspaces/organizations"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "roles",
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/roles"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ClusterRoleRuleMapping = []models.Rule{
|
||||
{Name: "workspaces",
|
||||
Actions: []models.Action{
|
||||
{
|
||||
Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"workspaces"},
|
||||
Resources: []string{"monitoring/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"quota", "status", "monitoring", "persistentvolumeclaims"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces", "workspaces/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"", "apps", "extensions", "batch"},
|
||||
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"rolebindings", "roles"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"members"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "create",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"tenant.kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"tenant.kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
},
|
||||
},
|
||||
@@ -349,7 +83,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"tenant.kubesphere.io", "monitoring.kubesphere.io"},
|
||||
Resources: []string{"workspaces", "workspaces/*"},
|
||||
},
|
||||
{
|
||||
@@ -359,7 +93,7 @@ var (
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"", "apps", "extensions", "batch"},
|
||||
APIGroups: []string{"", "apps", "extensions", "batch", "resources.kubesphere.io"},
|
||||
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
|
||||
},
|
||||
{
|
||||
@@ -367,16 +101,6 @@ var (
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
Resources: []string{"rolebindings", "roles"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"members"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
|
||||
@@ -391,9 +115,13 @@ var (
|
||||
Actions: []models.Action{
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"monitoring", "health", "monitoring/*"},
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"monitoring.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
}, {
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"health"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
@@ -405,14 +133,14 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"users", "users/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"clusterrules"},
|
||||
ResourceNames: []string{"mapping"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"rulesmapping"},
|
||||
ResourceNames: []string{"clusterroles"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
@@ -425,12 +153,12 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create", "get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"clusterrules"},
|
||||
ResourceNames: []string{"mapping"},
|
||||
},
|
||||
@@ -445,7 +173,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list", "update", "patch"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
{
|
||||
@@ -459,8 +187,8 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete", "deletecollection"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"accounts"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -483,8 +211,8 @@ var (
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"clusterroles/*"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"clusterroles", "clusterroles/*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -527,15 +255,9 @@ var (
|
||||
APIGroups: []string{"storage.k8s.io"},
|
||||
Resources: []string{"storageclasses"},
|
||||
}, {
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"storage-classes"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"storage/*"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"storageclasses", "storageclasses/*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -578,15 +300,13 @@ var (
|
||||
Resources: []string{"nodes", "events"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"nodes"},
|
||||
Resources: []string{"resources", "monitoring", "monitoring/*"},
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"nodes", "nodes/*"},
|
||||
}, {
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"pods"},
|
||||
Resources: []string{"resources"},
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"monitoring.kubesphere.io"},
|
||||
Resources: []string{"nodes"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -669,14 +389,9 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"list", "get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"components", "components/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"list", "get"},
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"pods"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -726,12 +441,12 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
|
||||
Resources: []string{"rolebindings"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
APIGroups: []string{"iam.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
},
|
||||
@@ -772,15 +487,9 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"rbac.authorization.k8s.io"},
|
||||
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
|
||||
Resources: []string{"roles"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"roles"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "create",
|
||||
@@ -819,7 +528,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"apps", "extensions"},
|
||||
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
|
||||
Resources: []string{"deployments", "deployments/scale"},
|
||||
},
|
||||
{
|
||||
@@ -875,7 +584,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"apps"},
|
||||
APIGroups: []string{"apps", "resources.kubesphere.io"},
|
||||
Resources: []string{"statefulsets"},
|
||||
},
|
||||
{
|
||||
@@ -929,7 +638,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"apps", "extensions"},
|
||||
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
|
||||
Resources: []string{"daemonsets"},
|
||||
},
|
||||
{
|
||||
@@ -974,8 +683,17 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"pod/shell"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"pod/terminal"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"pods"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -997,7 +715,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"list", "get"},
|
||||
APIGroups: []string{""},
|
||||
APIGroups: []string{"", "resources.kubesphere.io"},
|
||||
Resources: []string{"services"},
|
||||
},
|
||||
},
|
||||
@@ -1039,7 +757,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
},
|
||||
@@ -1048,7 +766,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
},
|
||||
@@ -1057,7 +775,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update", "patch"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
},
|
||||
@@ -1066,7 +784,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"router"},
|
||||
},
|
||||
},
|
||||
@@ -1081,7 +799,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"extensions"},
|
||||
APIGroups: []string{"extensions", "resources.kubesphere.io"},
|
||||
Resources: []string{"ingresses"},
|
||||
},
|
||||
},
|
||||
@@ -1121,7 +839,7 @@ var (
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{""},
|
||||
APIGroups: []string{"", "resources.kubesphere.io"},
|
||||
Resources: []string{"persistentvolumeclaims"},
|
||||
},
|
||||
},
|
||||
@@ -1160,10 +878,9 @@ var (
|
||||
{Name: "view",
|
||||
Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"applications"},
|
||||
Resources: []string{"resources"},
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"resources.kubesphere.io"},
|
||||
Resources: []string{"applications"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
@@ -1203,7 +920,7 @@ var (
|
||||
{Name: "view", Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"view", "list"},
|
||||
APIGroups: []string{"batch"},
|
||||
APIGroups: []string{"batch", "resources.kubesphere.io"},
|
||||
Resources: []string{"jobs"},
|
||||
},
|
||||
}},
|
||||
@@ -1236,7 +953,7 @@ var (
|
||||
{Name: "view", Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"view", "list"},
|
||||
APIGroups: []string{"batch"},
|
||||
APIGroups: []string{"batch", "resources.kubesphere.io"},
|
||||
Resources: []string{"cronjobs"},
|
||||
},
|
||||
}},
|
||||
@@ -1269,7 +986,7 @@ var (
|
||||
{Name: "view", Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"view", "list"},
|
||||
APIGroups: []string{""},
|
||||
APIGroups: []string{"", "resources.kubesphere.io"},
|
||||
Resources: []string{"secrets"},
|
||||
},
|
||||
}},
|
||||
@@ -1302,7 +1019,7 @@ var (
|
||||
{Name: "view", Rules: []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"view", "list"},
|
||||
APIGroups: []string{""},
|
||||
APIGroups: []string{"", "resources.kubesphere.io"},
|
||||
Resources: []string{"configmaps"},
|
||||
},
|
||||
}},
|
||||
@@ -1331,3 +1048,16 @@ var (
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func GetClusterAction(module, action string) (models.Action, error) {
|
||||
for _, rule := range ClusterRoleRuleMapping {
|
||||
if rule.Name == module {
|
||||
for _, act := range rule.Actions {
|
||||
if act.Name == action {
|
||||
return act, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return models.Action{}, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ const (
|
||||
QueryLevelWorkload
|
||||
QueryLevelPod
|
||||
QueryLevelContainer
|
||||
)
|
||||
)
|
||||
|
||||
@@ -248,7 +248,7 @@ func FluentbitOutputInsert(output fb.OutputPlugin) *FluentbitOutputsResult {
|
||||
// 1. Update ConfigMap
|
||||
var outputs []fb.OutputPlugin
|
||||
outputs, err := GetFluentbitOutputFromConfigMap()
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
// If the ConfigMap doesn't exist, a new one will be created later
|
||||
glog.Errorln(err)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
Copyright 2019 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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
@@ -802,12 +802,12 @@ func MonitorOneWorkspaceStatistics(wsName string) *FormatedLevelMetric {
|
||||
}()
|
||||
|
||||
go func() {
|
||||
members, errMemb := workspaces.GetOrgMembers(wsName)
|
||||
count, errMemb := workspaces.WorkspaceUserCount(wsName)
|
||||
if errMemb != nil {
|
||||
glog.Errorln(errMemb.Error())
|
||||
}
|
||||
// add member metric
|
||||
memberMetrics = getSpecificMetricItem(timestamp, MetricNameWorkspaceMemberCount, WorkspaceResourceKindMember, len(members), errMemb)
|
||||
memberMetrics = getSpecificMetricItem(timestamp, MetricNameWorkspaceMemberCount, WorkspaceResourceKindMember, count, errMemb)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
/*
|
||||
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
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -255,6 +257,7 @@ func ReformatJson(metric string, metricsName string, needAddParams map[string]st
|
||||
var formatMetric FormatedMetric
|
||||
|
||||
err := jsonIter.Unmarshal([]byte(metric), &formatMetric)
|
||||
|
||||
if err != nil {
|
||||
glog.Errorln("Unmarshal metric json failed", err.Error(), metric)
|
||||
}
|
||||
|
||||
@@ -55,9 +55,9 @@ func getUsage(namespace, resource string) (int, error) {
|
||||
var result *models.PageableResponse
|
||||
var err error
|
||||
if resource == resources.Namespaces || resource == resources.StorageClasses {
|
||||
result, err = resources.ListClusterResource(resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
result, err = resources.ListResources("", resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
} else {
|
||||
result, err = resources.ListNamespaceResource(namespace, resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
result, err = resources.ListResources(namespace, resource, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -20,6 +20,7 @@ package resources
|
||||
import (
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -30,16 +31,34 @@ import (
|
||||
type clusterRoleSearcher struct {
|
||||
}
|
||||
|
||||
func (*clusterRoleSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case ownerKind:
|
||||
fallthrough
|
||||
case ownerName:
|
||||
kind := match[ownerKind]
|
||||
name := match[ownerName]
|
||||
if !k8sutil.IsControlledBy(item.OwnerReferences, kind, name) {
|
||||
return false
|
||||
}
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -62,10 +81,6 @@ func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRol
|
||||
return false
|
||||
}
|
||||
return false
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -77,7 +92,7 @@ func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRol
|
||||
|
||||
func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
@@ -86,7 +101,7 @@ func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool
|
||||
}
|
||||
}
|
||||
|
||||
func (s *clusterRoleSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
func (s *clusterRoleSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
clusterRoles, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type configMapSearcher struct {
|
||||
}
|
||||
|
||||
func (*configMapSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) boo
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) boo
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) boo
|
||||
|
||||
func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -31,6 +31,10 @@ import (
|
||||
type cronJobSearcher struct {
|
||||
}
|
||||
|
||||
func (*cronJobSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).Get(name)
|
||||
}
|
||||
|
||||
func cronJobStatus(item *v1beta1.CronJob) string {
|
||||
if item.Spec.Suspend != nil && *item.Spec.Suspend {
|
||||
return paused
|
||||
@@ -42,12 +46,22 @@ func cronJobStatus(item *v1beta1.CronJob) string {
|
||||
func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case status:
|
||||
if cronJobStatus(item) != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -74,10 +88,6 @@ func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bo
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -98,7 +108,7 @@ func (*cronJobSearcher) compare(a, b *v1beta1.CronJob, orderBy string) bool {
|
||||
return false
|
||||
}
|
||||
return a.Status.LastScheduleTime.Before(b.Status.LastScheduleTime)
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
default:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type daemonSetSearcher struct {
|
||||
}
|
||||
|
||||
func (*daemonSetSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
|
||||
}
|
||||
|
||||
func daemonSetStatus(item *v1.DaemonSet) string {
|
||||
if item.Status.NumberAvailable == 0 {
|
||||
return stopped
|
||||
@@ -48,8 +52,18 @@ func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) boo
|
||||
if daemonSetStatus(item) != v {
|
||||
return false
|
||||
}
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -76,10 +90,6 @@ func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) boo
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -92,7 +102,7 @@ func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) boo
|
||||
|
||||
func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -31,6 +31,10 @@ import (
|
||||
type deploymentSearcher struct {
|
||||
}
|
||||
|
||||
func (*deploymentSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
|
||||
}
|
||||
|
||||
func deploymentStatus(item *v1.Deployment) string {
|
||||
if item.Spec.Replicas != nil {
|
||||
if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 {
|
||||
@@ -52,8 +56,18 @@ func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) b
|
||||
if deploymentStatus(item) != v {
|
||||
return false
|
||||
}
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -80,10 +94,6 @@ func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) b
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -96,7 +106,7 @@ func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) b
|
||||
|
||||
func (*deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -31,6 +31,10 @@ import (
|
||||
type ingressSearcher struct {
|
||||
}
|
||||
|
||||
func (*ingressSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress) bool {
|
||||
for k, v := range match {
|
||||
@@ -39,8 +43,14 @@ func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress)
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -67,10 +77,6 @@ func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress)
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -82,7 +88,7 @@ func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress)
|
||||
|
||||
func (*ingressSearcher) compare(a, b *extensions.Ingress, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -32,6 +32,10 @@ import (
|
||||
type jobSearcher struct {
|
||||
}
|
||||
|
||||
func (*jobSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Batch().V1().Jobs().Lister().Jobs(namespace).Get(name)
|
||||
}
|
||||
|
||||
func jobStatus(item *batchv1.Job) string {
|
||||
status := ""
|
||||
|
||||
@@ -54,8 +58,18 @@ func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool {
|
||||
if jobStatus(item) != v {
|
||||
return false
|
||||
}
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -82,10 +96,6 @@ func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool {
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -111,6 +121,8 @@ func jobUpdateTime(item *batchv1.Job) time.Time {
|
||||
|
||||
func (*jobSearcher) compare(a, b *batchv1.Job, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case updateTime:
|
||||
return jobUpdateTime(a).Before(jobUpdateTime(b))
|
||||
case name:
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type namespaceSearcher struct {
|
||||
}
|
||||
|
||||
func (*namespaceSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) boo
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo
|
||||
|
||||
func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
@@ -90,7 +96,7 @@ func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *namespaceSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
func (s *namespaceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
namespaces, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type nodeSearcher struct {
|
||||
}
|
||||
|
||||
func (*nodeSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().Nodes().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
|
||||
|
||||
func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
@@ -90,7 +96,7 @@ func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *nodeSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
func (s *nodeSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
nodes, err := informers.SharedInformerFactory().Core().V1().Nodes().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type persistentVolumeClaimSearcher struct {
|
||||
}
|
||||
|
||||
func (*persistentVolumeClaimSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.Pe
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.Pe
|
||||
|
||||
func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -31,25 +31,29 @@ import (
|
||||
type podSearcher struct {
|
||||
}
|
||||
|
||||
func podBelongTo(item *v1.Pod, kind string, name string) bool {
|
||||
func (*podSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().Pods().Lister().Pods(namespace).Get(name)
|
||||
}
|
||||
|
||||
if strings.EqualFold(kind, "Deployment") {
|
||||
func podBelongTo(item *v1.Pod, kind string, name string) bool {
|
||||
switch kind {
|
||||
case "Deployment":
|
||||
if podBelongToDeployment(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "ReplicaSet") {
|
||||
case "ReplicaSet":
|
||||
if podBelongToReplicaSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "DaemonSet") {
|
||||
case "DaemonSet":
|
||||
if podBelongToDaemonSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "StatefulSet") {
|
||||
case "StatefulSet":
|
||||
if podBelongToStatefulSet(item, name) {
|
||||
return true
|
||||
}
|
||||
} else if strings.EqualFold(kind, "Job") {
|
||||
case "Job":
|
||||
if podBelongToJob(item, name) {
|
||||
return true
|
||||
}
|
||||
@@ -57,9 +61,9 @@ func podBelongTo(item *v1.Pod, kind string, name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func replicaSetBelongToDeployment(replicaSet *v12.ReplicaSet, name string) bool {
|
||||
func replicaSetBelongToDeployment(replicaSet *v12.ReplicaSet, deploymentName string) bool {
|
||||
for _, owner := range replicaSet.OwnerReferences {
|
||||
if owner.Kind == "Deployment" && owner.Name == name {
|
||||
if owner.Kind == "Deployment" && owner.Name == deploymentName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -84,38 +88,36 @@ func podBelongToJob(item *v1.Pod, name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToReplicaSet(item *v1.Pod, name string) bool {
|
||||
func podBelongToReplicaSet(item *v1.Pod, replicaSetName string) bool {
|
||||
for _, owner := range item.OwnerReferences {
|
||||
if owner.Kind == "ReplicaSet" && owner.Name == name {
|
||||
if owner.Kind == "ReplicaSet" && owner.Name == replicaSetName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToStatefulSet(item *v1.Pod, name string) bool {
|
||||
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, r := range replicas {
|
||||
if replicaSetBelongToDeployment(r, name) {
|
||||
return podBelongToReplicaSet(item, r.Name)
|
||||
func podBelongToStatefulSet(item *v1.Pod, statefulSetName string) bool {
|
||||
for _, owner := range item.OwnerReferences {
|
||||
if owner.Kind == "StatefulSet" && owner.Name == statefulSetName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func podBelongToDeployment(item *v1.Pod, name string) bool {
|
||||
func podBelongToDeployment(item *v1.Pod, deploymentName string) bool {
|
||||
replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, r := range replicas {
|
||||
if replicaSetBelongToDeployment(r, name) {
|
||||
return podBelongToReplicaSet(item, r.Name)
|
||||
if replicaSetBelongToDeployment(r, deploymentName) && podBelongToReplicaSet(item, r.Name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -134,10 +136,10 @@ func podBelongToService(item *v1.Pod, serviceName string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for k, v := range service.Spec.Selector {
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
|
||||
selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated()
|
||||
if !selector.Matches(labels.Set(item.Labels)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -146,11 +148,11 @@ func podBelongToService(item *v1.Pod, serviceName string) bool {
|
||||
func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case "ownerKind":
|
||||
case ownerKind:
|
||||
fallthrough
|
||||
case "ownerName":
|
||||
kind := match["ownerKind"]
|
||||
name := match["ownerName"]
|
||||
case ownerName:
|
||||
kind := match[ownerKind]
|
||||
name := match[ownerName]
|
||||
if !podBelongTo(item, kind, name) {
|
||||
return false
|
||||
}
|
||||
@@ -170,6 +172,10 @@ func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
@@ -200,10 +206,6 @@ func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -215,7 +217,7 @@ func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
|
||||
|
||||
func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -21,39 +21,45 @@ import (
|
||||
"fmt"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
namespacedResources[ConfigMaps] = &configMapSearcher{}
|
||||
namespacedResources[CronJobs] = &cronJobSearcher{}
|
||||
namespacedResources[DaemonSets] = &daemonSetSearcher{}
|
||||
namespacedResources[Deployments] = &deploymentSearcher{}
|
||||
namespacedResources[Ingresses] = &ingressSearcher{}
|
||||
namespacedResources[Jobs] = &jobSearcher{}
|
||||
namespacedResources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{}
|
||||
namespacedResources[Secrets] = &secretSearcher{}
|
||||
namespacedResources[Services] = &serviceSearcher{}
|
||||
namespacedResources[StatefulSets] = &statefulSetSearcher{}
|
||||
namespacedResources[Pods] = &podSearcher{}
|
||||
namespacedResources[Roles] = &roleSearcher{}
|
||||
namespacedResources[S2iBuilders] = &s2iBuilderSearcher{}
|
||||
namespacedResources[S2iRuns] = &s2iRunSearcher{}
|
||||
resources[ConfigMaps] = &configMapSearcher{}
|
||||
resources[CronJobs] = &cronJobSearcher{}
|
||||
resources[DaemonSets] = &daemonSetSearcher{}
|
||||
resources[Deployments] = &deploymentSearcher{}
|
||||
resources[Ingresses] = &ingressSearcher{}
|
||||
resources[Jobs] = &jobSearcher{}
|
||||
resources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{}
|
||||
resources[Secrets] = &secretSearcher{}
|
||||
resources[Services] = &serviceSearcher{}
|
||||
resources[StatefulSets] = &statefulSetSearcher{}
|
||||
resources[Pods] = &podSearcher{}
|
||||
resources[Roles] = &roleSearcher{}
|
||||
resources[S2iBuilders] = &s2iBuilderSearcher{}
|
||||
resources[S2iRuns] = &s2iRunSearcher{}
|
||||
|
||||
clusterResources[Nodes] = &nodeSearcher{}
|
||||
clusterResources[Namespaces] = &namespaceSearcher{}
|
||||
clusterResources[ClusterRoles] = &clusterRoleSearcher{}
|
||||
clusterResources[StorageClasses] = &storageClassesSearcher{}
|
||||
clusterResources[S2iBuilderTemplates] = &s2iBuilderTemplateSearcher{}
|
||||
resources[Nodes] = &nodeSearcher{}
|
||||
resources[Namespaces] = &namespaceSearcher{}
|
||||
resources[ClusterRoles] = &clusterRoleSearcher{}
|
||||
resources[StorageClasses] = &storageClassesSearcher{}
|
||||
resources[S2iBuilderTemplates] = &s2iBuilderTemplateSearcher{}
|
||||
resources[Workspaces] = &workspaceSearcher{}
|
||||
}
|
||||
|
||||
var namespacedResources = make(map[string]namespacedSearcherInterface)
|
||||
var clusterResources = make(map[string]clusterSearcherInterface)
|
||||
var (
|
||||
resources = make(map[string]resourceSearchInterface)
|
||||
clusterResources = []string{Nodes, Workspaces, Namespaces, ClusterRoles, StorageClasses, S2iBuilderTemplates}
|
||||
)
|
||||
|
||||
const (
|
||||
name = "name"
|
||||
label = "label"
|
||||
createTime = "createTime"
|
||||
ownerKind = "ownerKind"
|
||||
ownerName = "ownerName"
|
||||
CreateTime = "CreateTime"
|
||||
updateTime = "updateTime"
|
||||
lastScheduleTime = "lastScheduleTime"
|
||||
displayName = "displayName"
|
||||
@@ -72,6 +78,8 @@ const (
|
||||
Deployments = "deployments"
|
||||
DaemonSets = "daemonsets"
|
||||
Roles = "roles"
|
||||
Workspaces = "workspaces"
|
||||
WorkspaceRoles = "workspaceroles"
|
||||
CronJobs = "cronjobs"
|
||||
ConfigMaps = "configmaps"
|
||||
Ingresses = "ingresses"
|
||||
@@ -90,72 +98,58 @@ const (
|
||||
S2iRuns = "s2iruns"
|
||||
)
|
||||
|
||||
type namespacedSearcherInterface interface {
|
||||
type resourceSearchInterface interface {
|
||||
get(namespace, name string) (interface{}, error)
|
||||
search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
|
||||
}
|
||||
type clusterSearcherInterface interface {
|
||||
search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
|
||||
|
||||
func ListResourcesByName(namespace, resource string, names []string) (*models.PageableResponse, error) {
|
||||
items := make([]interface{}, 0)
|
||||
if searcher, ok := resources[resource]; ok {
|
||||
for _, name := range names {
|
||||
item, err := searcher.get(namespace, name)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
} else {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
return &models.PageableResponse{TotalCount: len(items), Items: items}, nil
|
||||
}
|
||||
|
||||
func ListNamespaceResource(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
func ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
items := make([]interface{}, 0)
|
||||
total := 0
|
||||
var err error
|
||||
var result []interface{}
|
||||
|
||||
if searcher, ok := namespacedResources[resource]; ok {
|
||||
// none namespace resource
|
||||
if namespace != "" && sliceutil.HasString(clusterResources, resource) {
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
if searcher, ok := resources[resource]; ok {
|
||||
result, err = searcher.search(namespace, conditions, orderBy, reverse)
|
||||
} else {
|
||||
return nil, fmt.Errorf("not support")
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
total = len(result)
|
||||
|
||||
for i, d := range result {
|
||||
if i >= offset && (limit == -1 || len(items) < limit) {
|
||||
items = append(items, d)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{TotalCount: total, Items: items}, nil
|
||||
}
|
||||
|
||||
func ListClusterResource(resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
items := make([]interface{}, 0)
|
||||
total := 0
|
||||
var err error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []interface{}
|
||||
|
||||
if searcher, ok := clusterResources[resource]; ok {
|
||||
result, err = searcher.search(conditions, orderBy, reverse)
|
||||
} else if searcher, ok := namespacedResources[resource]; ok {
|
||||
result, err = searcher.search("", conditions, orderBy, reverse)
|
||||
} else {
|
||||
return nil, fmt.Errorf("not support")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
total = len(result)
|
||||
|
||||
for i, d := range result {
|
||||
if i >= offset && len(items) < limit {
|
||||
items = append(items, d)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{TotalCount: total, Items: items}, nil
|
||||
return &models.PageableResponse{TotalCount: len(result), Items: items}, nil
|
||||
}
|
||||
|
||||
func searchFuzzy(m map[string]string, key, value string) bool {
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type roleSearcher struct {
|
||||
}
|
||||
|
||||
func (*roleSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -62,10 +72,6 @@ func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -77,7 +83,7 @@ func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
|
||||
|
||||
func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type s2iBuilderSearcher struct {
|
||||
}
|
||||
|
||||
func (*s2iBuilderSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*s2iBuilderSearcher) match(match map[string]string, item *v1alpha1.S2iBuilder) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*s2iBuilderSearcher) match(match map[string]string, item *v1alpha1.S2iBuil
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*s2iBuilderSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuil
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*s2iBuilderSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuil
|
||||
|
||||
func (*s2iBuilderSearcher) compare(a, b *v1alpha1.S2iBuilder, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type s2iBuilderTemplateSearcher struct {
|
||||
}
|
||||
|
||||
func (*s2iBuilderTemplateSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilderTemplates().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*s2iBuilderTemplateSearcher) match(match map[string]string, item *v1alpha1.S2iBuilderTemplate) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*s2iBuilderTemplateSearcher) match(match map[string]string, item *v1alpha1
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -62,10 +72,6 @@ func (*s2iBuilderTemplateSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1
|
||||
return false
|
||||
}
|
||||
return false
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -77,7 +83,7 @@ func (*s2iBuilderTemplateSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1
|
||||
|
||||
func (*s2iBuilderTemplateSearcher) compare(a, b *v1alpha1.S2iBuilderTemplate, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
@@ -86,7 +92,7 @@ func (*s2iBuilderTemplateSearcher) compare(a, b *v1alpha1.S2iBuilderTemplate, or
|
||||
}
|
||||
}
|
||||
|
||||
func (s *s2iBuilderTemplateSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
func (s *s2iBuilderTemplateSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
builderTemplates, err := informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilderTemplates().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -33,6 +33,10 @@ import (
|
||||
type s2iRunSearcher struct {
|
||||
}
|
||||
|
||||
func (*s2iRunSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) bool {
|
||||
for k, v := range match {
|
||||
@@ -42,11 +46,17 @@ func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) boo
|
||||
return false
|
||||
}
|
||||
case status:
|
||||
if string(item.Status.RunState) != v{
|
||||
if string(item.Status.RunState) != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -73,10 +83,6 @@ func (*s2iRunSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iRun) boo
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -88,7 +94,7 @@ func (*s2iRunSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iRun) boo
|
||||
|
||||
func (*s2iRunSearcher) compare(a, b *v1alpha1.S2iRun, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type secretSearcher struct {
|
||||
}
|
||||
|
||||
func (*secretSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool {
|
||||
for k, v := range match {
|
||||
@@ -42,8 +46,14 @@ func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool {
|
||||
if string(item.Type) != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -70,10 +80,6 @@ func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool {
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -85,7 +91,7 @@ func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool {
|
||||
|
||||
func (*secretSearcher) compare(a, b *v1.Secret, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type serviceSearcher struct {
|
||||
}
|
||||
|
||||
func (*serviceSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Core().V1().Services().Lister().Services(namespace).Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool {
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -66,10 +76,6 @@ func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool {
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -81,7 +87,7 @@ func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool {
|
||||
|
||||
func (*serviceSearcher) compare(a, b *v1.Service, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type statefulSetSearcher struct {
|
||||
}
|
||||
|
||||
func (*statefulSetSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
|
||||
}
|
||||
|
||||
func statefulSetStatus(item *v1.StatefulSet) string {
|
||||
if item.Spec.Replicas != nil {
|
||||
if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 {
|
||||
@@ -52,7 +56,9 @@ func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet)
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -95,7 +101,7 @@ func (*statefulSetSearcher) fuzzy(fuzzy map[string]string, item *v1.StatefulSet)
|
||||
|
||||
func (*statefulSetSearcher) compare(a, b *v1.StatefulSet, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
type storageClassesSearcher struct {
|
||||
}
|
||||
|
||||
func (*storageClassesSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool {
|
||||
for k, v := range match {
|
||||
@@ -38,8 +42,14 @@ func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageCl
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -62,10 +72,6 @@ func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageCl
|
||||
return false
|
||||
}
|
||||
return false
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
@@ -77,7 +83,7 @@ func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageCl
|
||||
|
||||
func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case createTime:
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
@@ -86,7 +92,7 @@ func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bo
|
||||
}
|
||||
}
|
||||
|
||||
func (s *storageClassesSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
func (s *storageClassesSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
storageClasses, err := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
|
||||
132
pkg/models/resources/workspaces.go
Normal file
132
pkg/models/resources/workspaces.go
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 resources
|
||||
|
||||
import (
|
||||
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type workspaceSearcher struct {
|
||||
}
|
||||
|
||||
func (*workspaceSearcher) get(namespace, name string) (interface{}, error) {
|
||||
return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(name)
|
||||
}
|
||||
|
||||
// exactly Match
|
||||
func (*workspaceSearcher) match(match map[string]string, item *tenantv1alpha1.Workspace) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case name:
|
||||
if item.Name != v && item.Labels[displayName] != v {
|
||||
return false
|
||||
}
|
||||
case keyword:
|
||||
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Fuzzy searchInNamespace
|
||||
func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *tenantv1alpha1.Workspace) bool {
|
||||
for k, v := range fuzzy {
|
||||
switch k {
|
||||
case name:
|
||||
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels[displayName], v) {
|
||||
return false
|
||||
}
|
||||
case label:
|
||||
if !searchFuzzy(item.Labels, "", v) {
|
||||
return false
|
||||
}
|
||||
case annotation:
|
||||
if !searchFuzzy(item.Annotations, "", v) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
case app:
|
||||
if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (*workspaceSearcher) compare(a, b *tenantv1alpha1.Workspace, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case CreateTime:
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case name:
|
||||
fallthrough
|
||||
default:
|
||||
return strings.Compare(a.Name, b.Name) <= 0
|
||||
}
|
||||
}
|
||||
|
||||
func (s *workspaceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
|
||||
|
||||
workspaces, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]*tenantv1alpha1.Workspace, 0)
|
||||
|
||||
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
|
||||
result = workspaces
|
||||
} else {
|
||||
for _, item := range workspaces {
|
||||
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
if reverse {
|
||||
tmp := i
|
||||
i = j
|
||||
j = tmp
|
||||
}
|
||||
return s.compare(result[i], result[j], orderBy)
|
||||
})
|
||||
|
||||
r := make([]interface{}, 0)
|
||||
for _, i := range result {
|
||||
r = append(r, i)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@@ -32,12 +32,9 @@ import (
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"k8s.io/api/rbac/v1"
|
||||
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
)
|
||||
|
||||
func GetAllRouters() ([]*corev1.Service, error) {
|
||||
@@ -54,39 +51,6 @@ func GetAllRouters() ([]*corev1.Service, error) {
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func GetAllRoutersOfUser(username string) ([]*corev1.Service, error) {
|
||||
allNamespace, namespaces, err := iam.GetUserNamespaces(username, v1.PolicyRule{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"services"},
|
||||
})
|
||||
|
||||
// return by cluster role
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if allNamespace {
|
||||
return GetAllRouters()
|
||||
}
|
||||
|
||||
routers := make([]*corev1.Service, 0)
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
router, err := GetRouter(namespace)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return routers, err
|
||||
} else if router != nil {
|
||||
routers = append(routers, router)
|
||||
}
|
||||
}
|
||||
|
||||
return routers, nil
|
||||
|
||||
}
|
||||
|
||||
// Get router from a namespace
|
||||
func GetRouter(namespace string) (*corev1.Service, error) {
|
||||
serviceName := constants.IngressControllerPrefix + namespace
|
||||
|
||||
@@ -40,7 +40,7 @@ func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
|
||||
notReadyStatus = "pending"
|
||||
}
|
||||
|
||||
notReadyList, err = resources.ListNamespaceResource(namespace, resource, ¶ms.Conditions{Match: map[string]string{"status": notReadyStatus}}, "", false, -1, 0)
|
||||
notReadyList, err = resources.ListResources(namespace, resource, ¶ms.Conditions{Match: map[string]string{"status": notReadyStatus}}, "", false, -1, 0)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
119
pkg/models/tenant/devops.go
Normal file
119
pkg/models/tenant/devops.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 tenant
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
kserr "kubesphere.io/kubesphere/pkg/errors"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ListDevopsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
|
||||
|
||||
db := mysql.Client()
|
||||
|
||||
var workspaceDOPBindings []models.WorkspaceDPBinding
|
||||
|
||||
if err := db.Where("workspace = ?", workspace).Find(&workspaceDOPBindings).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
devOpsProjects := make([]models.DevopsProject, 0)
|
||||
|
||||
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), nil)
|
||||
request.Header.Add(constants.UserNameHeader, username)
|
||||
|
||||
resp, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode > 200 {
|
||||
return nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &devOpsProjects)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if keyword := conditions.Match["keyword"]; keyword != "" {
|
||||
for i := 0; i < len(devOpsProjects); i++ {
|
||||
if !strings.Contains(devOpsProjects[i].Name, keyword) {
|
||||
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(devOpsProjects, func(i, j int) bool {
|
||||
switch orderBy {
|
||||
case "name":
|
||||
if reverse {
|
||||
return devOpsProjects[i].Name < devOpsProjects[j].Name
|
||||
} else {
|
||||
return devOpsProjects[i].Name > devOpsProjects[j].Name
|
||||
}
|
||||
default:
|
||||
if reverse {
|
||||
return devOpsProjects[i].CreateTime.After(*devOpsProjects[j].CreateTime)
|
||||
} else {
|
||||
return devOpsProjects[i].CreateTime.Before(*devOpsProjects[j].CreateTime)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for i := 0; i < len(devOpsProjects); i++ {
|
||||
inWorkspace := false
|
||||
|
||||
for _, binding := range workspaceDOPBindings {
|
||||
if binding.DevOpsProject == *devOpsProjects[i].ProjectId {
|
||||
inWorkspace = true
|
||||
}
|
||||
}
|
||||
if !inWorkspace {
|
||||
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
// limit offset
|
||||
result := make([]interface{}, 0)
|
||||
for i, v := range devOpsProjects {
|
||||
if len(result) < limit && i >= offset {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{Items: result, TotalCount: len(devOpsProjects)}, nil
|
||||
}
|
||||
134
pkg/models/tenant/namespaces.go
Normal file
134
pkg/models/tenant/namespaces.go
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 tenant
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type namespaceSearcher struct {
|
||||
}
|
||||
|
||||
// Exactly Match
|
||||
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
default:
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool {
|
||||
|
||||
for k, v := range fuzzy {
|
||||
switch k {
|
||||
case "name":
|
||||
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels["displayName"], v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case "createTime":
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case "name":
|
||||
fallthrough
|
||||
default:
|
||||
return strings.Compare(a.Name, b.Name) <= 0
|
||||
}
|
||||
}
|
||||
|
||||
func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) {
|
||||
|
||||
roles, err := iam.GetUserRoles("", username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaces := make([]*v1.Namespace, 0)
|
||||
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
|
||||
for _, role := range roles {
|
||||
namespace, err := namespaceLister.Get(role.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespaces = append(namespaces, namespace)
|
||||
}
|
||||
|
||||
return namespaces, nil
|
||||
}
|
||||
|
||||
func (s *namespaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) {
|
||||
|
||||
rules, err := iam.GetUserClusterRules(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
namespaces := make([]*v1.Namespace, 0)
|
||||
|
||||
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"namespaces"}}) {
|
||||
namespaces, err = informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything())
|
||||
} else {
|
||||
namespaces, err = s.GetNamespaces(username)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]*v1.Namespace, 0)
|
||||
|
||||
for _, namespace := range namespaces {
|
||||
if s.match(conditions.Match, namespace) && s.fuzzy(conditions.Fuzzy, namespace) {
|
||||
result = append(result, namespace)
|
||||
}
|
||||
}
|
||||
|
||||
// order & reverse
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
if reverse {
|
||||
tmp := i
|
||||
i = j
|
||||
j = tmp
|
||||
}
|
||||
return s.compare(result[i], result[j], orderBy)
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
103
pkg/models/tenant/tenant.go
Normal file
103
pkg/models/tenant/tenant.go
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 tenant
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
ws "kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
workspaces = workspaceSearcher{}
|
||||
namespaces = namespaceSearcher{}
|
||||
)
|
||||
|
||||
func CreateNamespace(workspaceName string, namespace *v1.Namespace, username string) (*v1.Namespace, error) {
|
||||
if namespace.Labels == nil {
|
||||
namespace.Labels = make(map[string]string, 0)
|
||||
}
|
||||
if username != "" {
|
||||
namespace.Labels[constants.CreatorLabelKey] = username
|
||||
}
|
||||
|
||||
namespace.Labels[constants.WorkspaceLabelKey] = workspaceName
|
||||
|
||||
return k8s.Client().CoreV1().Namespaces().Create(namespace)
|
||||
}
|
||||
|
||||
func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
|
||||
workspaces, err := workspaces.search(username, conditions, orderBy, reverse)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// limit offset
|
||||
result := make([]interface{}, 0)
|
||||
for i, workspace := range workspaces {
|
||||
if len(result) < limit && i >= offset {
|
||||
workspace := workspace.DeepCopy()
|
||||
ns, err := ListNamespaces(username, ¶ms.Conditions{Match: map[string]string{"kubesphere.io/workspace": workspace.Name}}, "", false, 1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if workspace.Annotations == nil {
|
||||
workspace.Annotations = make(map[string]string)
|
||||
}
|
||||
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
|
||||
devops, err := ListDevopsProjects(workspace.Name, username, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
|
||||
userCount, err := ws.WorkspaceUserCount(workspace.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
|
||||
result = append(result, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
|
||||
}
|
||||
|
||||
func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
|
||||
namespaces, err := namespaces.search(username, conditions, orderBy, reverse)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// limit offset
|
||||
result := make([]interface{}, 0)
|
||||
for i, v := range namespaces {
|
||||
if len(result) < limit && i >= offset {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
|
||||
return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil
|
||||
}
|
||||
130
pkg/models/tenant/workspaces.go
Normal file
130
pkg/models/tenant/workspaces.go
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 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 tenant
|
||||
|
||||
import (
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type workspaceSearcher struct {
|
||||
}
|
||||
|
||||
// Exactly Match
|
||||
func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspace) bool {
|
||||
for k, v := range match {
|
||||
switch k {
|
||||
case "name":
|
||||
if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if item.Labels[k] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool {
|
||||
|
||||
for k, v := range fuzzy {
|
||||
switch k {
|
||||
case "name":
|
||||
if !strings.Contains(item.Name, v) && !strings.Contains(item.Labels["displayName"], v) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool {
|
||||
switch orderBy {
|
||||
case "createTime":
|
||||
return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time)
|
||||
case "name":
|
||||
fallthrough
|
||||
default:
|
||||
return strings.Compare(a.Name, b.Name) <= 0
|
||||
}
|
||||
}
|
||||
|
||||
func (s *workspaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) {
|
||||
rules, err := iam.GetUserClusterRules(username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workspaces := make([]*v1alpha1.Workspace, 0)
|
||||
|
||||
if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"workspaces"}}) {
|
||||
workspaces, err = informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
workspaceRoles, err := iam.GetUserWorkspaceRoleMap(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k := range workspaceRoles {
|
||||
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workspaces = append(workspaces, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]*v1alpha1.Workspace, 0)
|
||||
|
||||
for _, workspace := range workspaces {
|
||||
if s.match(conditions.Match, workspace) && s.fuzzy(conditions.Fuzzy, workspace) {
|
||||
result = append(result, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
// order & reverse
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
if reverse {
|
||||
tmp := i
|
||||
i = j
|
||||
j = tmp
|
||||
}
|
||||
return s.compare(result[i], result[j], orderBy)
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
|
||||
return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
|
||||
}
|
||||
301
pkg/models/terminal/terminal.go
Normal file
301
pkg/models/terminal/terminal.go
Normal file
@@ -0,0 +1,301 @@
|
||||
// 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.
|
||||
//
|
||||
// the code is mainly from:
|
||||
// https://github.com/kubernetes/dashboard/blob/master/src/app/backend/handler/terminal.go
|
||||
// thanks to the related developer
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"gopkg.in/igm/sockjs-go.v2/sockjs"
|
||||
"io"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
)
|
||||
|
||||
// PtyHandler is what remotecommand expects from a pty
|
||||
type PtyHandler interface {
|
||||
io.Reader
|
||||
io.Writer
|
||||
remotecommand.TerminalSizeQueue
|
||||
}
|
||||
|
||||
// TerminalSession implements PtyHandler (using a SockJS connection)
|
||||
type TerminalSession struct {
|
||||
id string
|
||||
bound chan error
|
||||
sockJSSession sockjs.Session
|
||||
sizeChan chan remotecommand.TerminalSize
|
||||
}
|
||||
|
||||
// TerminalMessage is the messaging protocol between ShellController and TerminalSession.
|
||||
//
|
||||
// OP DIRECTION FIELD(S) USED DESCRIPTION
|
||||
// ---------------------------------------------------------------------
|
||||
// bind fe->be SessionID Id sent back from TerminalResponse
|
||||
// stdin fe->be Data Keystrokes/paste buffer
|
||||
// resize fe->be Rows, Cols New terminal size
|
||||
// stdout be->fe Data Output from the process
|
||||
// toast be->fe Data OOB message to be shown to the user
|
||||
type TerminalMessage struct {
|
||||
Op, Data, SessionID string
|
||||
Rows, Cols uint16
|
||||
}
|
||||
|
||||
// TerminalSize handles pty->process resize events
|
||||
// Called in a loop from remotecommand as long as the process is running
|
||||
func (t TerminalSession) Next() *remotecommand.TerminalSize {
|
||||
select {
|
||||
case size := <-t.sizeChan:
|
||||
return &size
|
||||
}
|
||||
}
|
||||
|
||||
// Read handles pty->process messages (stdin, resize)
|
||||
// Called in a loop from remotecommand as long as the process is running
|
||||
func (t TerminalSession) Read(p []byte) (int, error) {
|
||||
m, err := t.sockJSSession.Recv()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var msg TerminalMessage
|
||||
if err := json.Unmarshal([]byte(m), &msg); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch msg.Op {
|
||||
case "stdin":
|
||||
return copy(p, msg.Data), nil
|
||||
case "resize":
|
||||
t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows}
|
||||
return 0, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown message type '%s'", msg.Op)
|
||||
}
|
||||
}
|
||||
|
||||
// Write handles process->pty stdout
|
||||
// Called from remotecommand whenever there is any output
|
||||
func (t TerminalSession) Write(p []byte) (int, error) {
|
||||
msg, err := json.Marshal(TerminalMessage{
|
||||
Op: "stdout",
|
||||
Data: string(p),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err = t.sockJSSession.Send(string(msg)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Toast can be used to send the user any OOB messages
|
||||
// hterm puts these in the center of the terminal
|
||||
func (t TerminalSession) Toast(p string) error {
|
||||
msg, err := json.Marshal(TerminalMessage{
|
||||
Op: "toast",
|
||||
Data: p,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = t.sockJSSession.Send(string(msg)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close shuts down the SockJS connection and sends the status code and reason to the client
|
||||
// Can happen if the process exits or if there is an error starting up the process
|
||||
// For now the status code is unused and reason is shown to the user (unless "")
|
||||
func (t TerminalSession) Close(status uint32, reason string) {
|
||||
t.sockJSSession.Close(status, reason)
|
||||
}
|
||||
|
||||
// terminalSessions stores a map of all TerminalSession objects
|
||||
// FIXME: this structure needs locking
|
||||
var terminalSessions = make(map[string]TerminalSession)
|
||||
|
||||
// handleTerminalSession is Called by net/http for any new /api/sockjs connections
|
||||
func HandleTerminalSession(session sockjs.Session) {
|
||||
glog.Infof("handleTerminalSession, ID:%s", session.ID())
|
||||
var (
|
||||
buf string
|
||||
err error
|
||||
msg TerminalMessage
|
||||
terminalSession TerminalSession
|
||||
ok bool
|
||||
)
|
||||
|
||||
if buf, err = session.Recv(); err != nil {
|
||||
glog.Errorf("handleTerminalSession: can't Recv: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(buf), &msg); err != nil {
|
||||
glog.Errorf("handleTerminalSession: can't UnMarshal (%v): %s", err, buf)
|
||||
return
|
||||
}
|
||||
|
||||
if msg.Op != "bind" {
|
||||
glog.Errorf("handleTerminalSession: expected 'bind' message, got: %s", buf)
|
||||
return
|
||||
}
|
||||
|
||||
if terminalSession, ok = terminalSessions[msg.SessionID]; !ok {
|
||||
glog.Errorf("handleTerminalSession: can't find session '%s'", msg.SessionID)
|
||||
return
|
||||
}
|
||||
|
||||
terminalSession.sockJSSession = session
|
||||
terminalSessions[msg.SessionID] = terminalSession
|
||||
terminalSession.bound <- nil
|
||||
}
|
||||
|
||||
// startProcess is called by handleAttach
|
||||
// Executed cmd in the container specified in request and connects it up with the ptyHandler (a session)
|
||||
func startProcess(namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error {
|
||||
|
||||
k8sClient := k8s.Client()
|
||||
cfg, err := k8s.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := k8sClient.CoreV1().RESTClient().Post().
|
||||
Resource("pods").
|
||||
Name(podName).
|
||||
Namespace(namespace).
|
||||
SubResource("exec")
|
||||
req.VersionedParams(&v1.PodExecOptions{
|
||||
Container: containerName,
|
||||
Command: cmd,
|
||||
Stdin: true,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
TTY: true,
|
||||
}, scheme.ParameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = exec.Stream(remotecommand.StreamOptions{
|
||||
Stdin: ptyHandler,
|
||||
Stdout: ptyHandler,
|
||||
Stderr: ptyHandler,
|
||||
TerminalSizeQueue: ptyHandler,
|
||||
Tty: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// genTerminalSessionId generates a random session ID string. The format is not really interesting.
|
||||
// This ID is used to identify the session when the client opens the SockJS connection.
|
||||
// Not the same as the SockJS session id! We can't use that as that is generated
|
||||
// on the client side and we don't have it yet at this point.
|
||||
func genTerminalSessionId() (string, error) {
|
||||
|
||||
bytes := make([]byte, 16)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
id := make([]byte, hex.EncodedLen(len(bytes)))
|
||||
hex.Encode(id, bytes)
|
||||
glog.Infof("genTerminalSessionId, id:" + string(id))
|
||||
return string(id), nil
|
||||
}
|
||||
|
||||
// isValidShell checks if the shell is an allowed one
|
||||
func isValidShell(validShells []string, shell string) bool {
|
||||
for _, validShell := range validShells {
|
||||
if validShell == shell {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WaitingForConnection is called from apihandler.handleAttach as a goroutine
|
||||
// Waits for the SockJS connection to be opened by the client the session to be bound in handleTerminalSession
|
||||
func WaitingForConnection(shell string, namespace, podName, containerName string, sessionId string) {
|
||||
glog.Infof("WaitingForConnection, ID:%s", sessionId)
|
||||
session := terminalSessions[sessionId]
|
||||
select {
|
||||
case <-session.bound:
|
||||
close(session.bound)
|
||||
defer delete(terminalSessions, sessionId)
|
||||
var err error
|
||||
validShells := []string{"sh", "bash"}
|
||||
|
||||
if isValidShell(validShells, shell) {
|
||||
cmd := []string{shell}
|
||||
err = startProcess(namespace, podName, containerName, cmd, session)
|
||||
} else {
|
||||
// No shell given or it was not valid: try some shells until one succeeds or all fail
|
||||
// FIXME: if the first shell fails then the first keyboard event is lost
|
||||
for _, testShell := range validShells {
|
||||
cmd := []string{testShell}
|
||||
if err = startProcess(namespace, podName, containerName, cmd, session); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
session.Close(2, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
session.Close(1, "Process exited")
|
||||
}
|
||||
}
|
||||
|
||||
func NewSession(shell, namespace, podName, containerName string) (string, error) {
|
||||
sessionId, err := genTerminalSessionId()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
terminalSessions[sessionId] = TerminalSession{
|
||||
id: sessionId,
|
||||
bound: make(chan error),
|
||||
sizeChan: make(chan remotecommand.TerminalSize),
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
go WaitingForConnection(shell, namespace, podName, containerName, sessionId)
|
||||
|
||||
return sessionId, nil
|
||||
}
|
||||
@@ -36,15 +36,6 @@ type Workspace struct {
|
||||
DevopsProjects []string `json:"devops_projects"`
|
||||
}
|
||||
|
||||
type UserInvite struct {
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
func (g Group) GetCreateTime() (time.Time, error) {
|
||||
return time.Parse("2006-01-02T15:04:05Z", g.CreateTime)
|
||||
}
|
||||
|
||||
type WorkspaceDPBinding struct {
|
||||
Workspace string `gorm:"primary_key"`
|
||||
DevOpsProject string `gorm:"primary_key"`
|
||||
@@ -76,27 +67,23 @@ type SimpleRule struct {
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Username string `json:"username"`
|
||||
//UID string `json:"uid"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CurrentPassword string `json:"current_password,omitempty"`
|
||||
//Extra map[string]interface{} `json:"extra"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
LastLoginTime string `json:"last_login_time"`
|
||||
Status int `json:"status"`
|
||||
ClusterRole string `json:"cluster_role"`
|
||||
ClusterRules []SimpleRule `json:"cluster_rules"`
|
||||
Roles map[string]string `json:"roles,omitempty"`
|
||||
Rules map[string][]SimpleRule `json:"rules,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
RoleBinding string `json:"role_binding,omitempty"`
|
||||
Lang string `json:"lang,omitempty"`
|
||||
WorkspaceRoles map[string]string `json:"workspace_roles,omitempty"`
|
||||
WorkspaceRole string `json:"workspace_role,omitempty"`
|
||||
WorkspaceRules map[string][]SimpleRule `json:"workspace_rules,omitempty"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Lang string `json:"lang,omitempty"`
|
||||
Description string `json:"description"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CurrentPassword string `json:"current_password,omitempty"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
LastLoginTime string `json:"last_login_time"`
|
||||
Status int `json:"status"`
|
||||
ClusterRole string `json:"cluster_role"`
|
||||
Roles map[string]string `json:"roles,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
RoleBinding string `json:"role_binding,omitempty"`
|
||||
RoleBindTime *time.Time `json:"role_bind_time,omitempty"`
|
||||
WorkspaceRole string `json:"workspace_role,omitempty"`
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
@@ -105,8 +92,6 @@ type Group struct {
|
||||
Gid string `json:"gid"`
|
||||
Members []string `json:"members"`
|
||||
Logo string `json:"logo"`
|
||||
Creator string `json:"creator"`
|
||||
CreateTime string `json:"create_time"`
|
||||
ChildGroups []string `json:"child_groups"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
@@ -22,9 +22,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources"
|
||||
"kubesphere.io/kubesphere/pkg/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
|
||||
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"net/http"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
@@ -32,22 +38,17 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
"errors"
|
||||
"regexp"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"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"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"sort"
|
||||
|
||||
@@ -128,21 +129,13 @@ func CreateDevopsProject(username string, workspace string, devops models.Devops
|
||||
}
|
||||
|
||||
func createDefaultDevopsRoleBinding(workspace string, project models.DevopsProject) error {
|
||||
admins, err := iam.GetWorkspaceUsers(workspace, constants.WorkspaceAdmin)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
admins := []string{""}
|
||||
|
||||
for _, admin := range admins {
|
||||
createDevopsRoleBinding(workspace, *project.ProjectId, admin, constants.DevopsOwner)
|
||||
}
|
||||
|
||||
viewers, err := iam.GetWorkspaceUsers(workspace, constants.WorkspaceViewer)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
viewers := []string{""}
|
||||
|
||||
for _, viewer := range viewers {
|
||||
createDevopsRoleBinding(workspace, *project.ProjectId, viewer, constants.DevopsReporter)
|
||||
@@ -151,33 +144,6 @@ func createDefaultDevopsRoleBinding(workspace string, project models.DevopsProje
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteDevopsRoleBinding(workspace string, projectId string, user string) {
|
||||
projects := make([]string, 0)
|
||||
|
||||
if projectId != "" {
|
||||
projects = append(projects, projectId)
|
||||
} else {
|
||||
p, err := GetDevOpsProjects(workspace)
|
||||
if err != nil {
|
||||
glog.Warning("delete devops role binding failed", workspace, projectId, user)
|
||||
return
|
||||
}
|
||||
projects = append(projects, p...)
|
||||
}
|
||||
|
||||
for _, project := range projects {
|
||||
request, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members/%s", constants.DevopsAPIServer, project, user), nil)
|
||||
request.Header.Add("X-Token-Username", "admin")
|
||||
resp, err := http.DefaultClient.Do(request)
|
||||
if err != nil || resp.StatusCode > 200 {
|
||||
glog.Warning("delete devops role binding failed", workspace, project, user)
|
||||
}
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createDevopsRoleBinding(workspace string, projectId string, user string, role string) {
|
||||
|
||||
projects := make([]string, 0)
|
||||
@@ -242,23 +208,17 @@ func ListNamespaceByUser(workspaceName string, username string, keyword string,
|
||||
}
|
||||
})
|
||||
|
||||
clusterRoles, err := iam.GetClusterRoles(username)
|
||||
rules, err := iam.GetUserClusterRules(username)
|
||||
|
||||
if err != nil {
|
||||
return 0, 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) {
|
||||
for i := 0; i < len(namespaces); i++ {
|
||||
roles, err := iam.GetRoles(namespaces[i].Name, username)
|
||||
roles, err := iam.GetUserRoles(namespaces[i].Name, username)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -313,13 +273,12 @@ func DeleteNamespace(workspace string, namespaceName string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if namespace.Labels != nil && namespace.Labels["kubesphere.io/workspace"] == workspace {
|
||||
if namespace.Labels[constants.WorkspaceLabelKey] == workspace {
|
||||
deletePolicy := meta_v1.DeletePropagationForeground
|
||||
return k8s.Client().CoreV1().Namespaces().Delete(namespaceName, &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
|
||||
} else {
|
||||
return errors.New("resource not found")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Delete(workspace *models.Workspace) error {
|
||||
@@ -339,6 +298,7 @@ func Delete(workspace *models.Workspace) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO
|
||||
func release(workspace *models.Workspace) error {
|
||||
for _, namespace := range workspace.Namespaces {
|
||||
err := DeleteNamespace(workspace.Name, namespace)
|
||||
@@ -348,7 +308,7 @@ func release(workspace *models.Workspace) error {
|
||||
}
|
||||
|
||||
for _, devops := range workspace.DevopsProjects {
|
||||
err := DeleteDevopsProject(workspace.Creator, devops)
|
||||
err := DeleteDevopsProject("admin", devops)
|
||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
||||
return err
|
||||
}
|
||||
@@ -381,30 +341,6 @@ func workspaceRoleRelease(workspace string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Create(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
|
||||
group, err := iam.CreateGroup(workspace.Group)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
created := models.Workspace{
|
||||
Group: *group,
|
||||
}
|
||||
|
||||
created.Members = make([]string, 0)
|
||||
created.Namespaces = make([]string, 0)
|
||||
created.DevopsProjects = make([]string, 0)
|
||||
|
||||
err = WorkspaceRoleInit(workspace)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &created, nil
|
||||
}
|
||||
|
||||
func Edit(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
|
||||
group, err := iam.UpdateGroup(&workspace.Group)
|
||||
@@ -418,24 +354,8 @@ func Edit(workspace *models.Workspace) (*models.Workspace, error) {
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
func Detail(name string) (*models.Workspace, error) {
|
||||
|
||||
conn, err := ldap.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
group, err := iam.GroupDetail(name, conn)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := mysql.Client()
|
||||
|
||||
workspace, err := convertGroupToWorkspace(db, *group)
|
||||
func DescribeWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
|
||||
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -444,48 +364,6 @@ func Detail(name string) (*models.Workspace, error) {
|
||||
return workspace, nil
|
||||
}
|
||||
|
||||
// List all workspaces for the current user
|
||||
func ListWorkspaceByUser(username string, keyword string) ([]*models.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"}}
|
||||
|
||||
var workspaces []*models.Workspace
|
||||
if iam.RulesMatchesRequired(rules, workspacesManager) {
|
||||
workspaces, err = fetch(nil)
|
||||
} else {
|
||||
workspaceNames := make([]string, 0)
|
||||
for _, clusterRole := range clusterRoles {
|
||||
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(clusterRole.Name); len(groups) == 3 {
|
||||
if !slice.ContainsString(workspaceNames, groups[1], nil) {
|
||||
workspaceNames = append(workspaceNames, groups[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
workspaces, err = fetch(workspaceNames)
|
||||
}
|
||||
|
||||
if keyword != "" {
|
||||
for i := 0; i < len(workspaces); i++ {
|
||||
if !strings.Contains(workspaces[i].Name, keyword) {
|
||||
workspaces = append(workspaces[:i], workspaces[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
return workspaces, err
|
||||
}
|
||||
|
||||
func fetch(names []string) ([]*models.Workspace, error) {
|
||||
|
||||
if names != nil && len(names) == 0 {
|
||||
@@ -505,7 +383,7 @@ func fetch(names []string) ([]*models.Workspace, error) {
|
||||
}
|
||||
defer conn.Close()
|
||||
for _, name := range names {
|
||||
group, err := iam.GroupDetail(name, conn)
|
||||
group, err := iam.DescribeGroup(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -527,90 +405,6 @@ func fetch(names []string) ([]*models.Workspace, error) {
|
||||
return workspaces, nil
|
||||
}
|
||||
|
||||
func ListDevopsProjectsByUser(username string, workspace string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []models.DevopsProject, error) {
|
||||
|
||||
db := mysql.Client()
|
||||
|
||||
var workspaceDOPBindings []models.WorkspaceDPBinding
|
||||
|
||||
if err := db.Where("workspace = ?", workspace).Find(&workspaceDOPBindings).Error; err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
devOpsProjects := make([]models.DevopsProject, 0)
|
||||
|
||||
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), nil)
|
||||
request.Header.Add(constants.UserNameHeader, username)
|
||||
|
||||
result, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer result.Body.Close()
|
||||
data, err := ioutil.ReadAll(result.Body)
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if result.StatusCode > 200 {
|
||||
return 0, nil, kserr.Parse(data)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &devOpsProjects)
|
||||
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if keyword != "" {
|
||||
for i := 0; i < len(devOpsProjects); i++ {
|
||||
if !strings.Contains(devOpsProjects[i].Name, keyword) {
|
||||
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(devOpsProjects, func(i, j int) bool {
|
||||
switch orderBy {
|
||||
case "name":
|
||||
if reverse {
|
||||
return devOpsProjects[i].Name < devOpsProjects[j].Name
|
||||
} else {
|
||||
return devOpsProjects[i].Name > devOpsProjects[j].Name
|
||||
}
|
||||
default:
|
||||
if reverse {
|
||||
return devOpsProjects[i].CreateTime.After(*devOpsProjects[j].CreateTime)
|
||||
} else {
|
||||
return devOpsProjects[i].CreateTime.Before(*devOpsProjects[j].CreateTime)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for i := 0; i < len(devOpsProjects); i++ {
|
||||
inWorkspace := false
|
||||
|
||||
for _, binding := range workspaceDOPBindings {
|
||||
if binding.DevOpsProject == *devOpsProjects[i].ProjectId {
|
||||
inWorkspace = true
|
||||
}
|
||||
}
|
||||
if !inWorkspace {
|
||||
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
if len(devOpsProjects) < offset {
|
||||
return len(devOpsProjects), make([]models.DevopsProject, 0), nil
|
||||
} else if len(devOpsProjects) < limit+offset {
|
||||
return len(devOpsProjects), devOpsProjects[offset:], nil
|
||||
} else {
|
||||
return len(devOpsProjects), devOpsProjects[offset : limit+offset], nil
|
||||
}
|
||||
}
|
||||
func convertGroupToWorkspace(db *gorm.DB, group models.Group) (*models.Workspace, error) {
|
||||
namespaces, err := Namespaces(group.Name)
|
||||
|
||||
@@ -642,449 +436,73 @@ func convertGroupToWorkspace(db *gorm.DB, group models.Group) (*models.Workspace
|
||||
return &workspace, nil
|
||||
}
|
||||
|
||||
func CreateNamespace(namespace *core.Namespace) (*core.Namespace, error) {
|
||||
func InviteUser(workspaceName string, user *models.User) error {
|
||||
|
||||
ns, err := k8s.Client().CoreV1().Namespaces().Create(namespace)
|
||||
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func Invite(workspaceName string, users []models.UserInvite) error {
|
||||
for _, user := range users {
|
||||
if !slice.ContainsString(constants.WorkSpaceRoles, user.Role, nil) {
|
||||
return fmt.Errorf("role %s not exist", user.Role)
|
||||
}
|
||||
}
|
||||
|
||||
workspace, err := Detail(workspaceName)
|
||||
|
||||
if err != nil {
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if !slice.ContainsString(workspace.Members, user.Username, nil) {
|
||||
workspace.Members = append(workspace.Members, user.Username)
|
||||
}
|
||||
}
|
||||
workspaceRoleName := fmt.Sprintf("workspace:%s:%s", workspaceName, strings.TrimPrefix(user.WorkspaceRole, "workspace-"))
|
||||
|
||||
workspace, err = Edit(workspace)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
err := CreateWorkspaceRoleBinding(workspace, user.Username, user.Role)
|
||||
if workspaceRole != nil && workspaceRole.Name != workspaceRoleName {
|
||||
err := DeleteWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return CreateWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole)
|
||||
}
|
||||
|
||||
func NamespaceExistCheck(namespaceName string) (bool, error) {
|
||||
func CreateWorkspaceRoleBinding(workspace, username string, role string) error {
|
||||
|
||||
_, err := k8s.Client().CoreV1().Namespaces().Get(namespaceName, meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
|
||||
return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
|
||||
}
|
||||
return true, nil
|
||||
|
||||
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
|
||||
|
||||
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
|
||||
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !k8sutil.ContainsUser(workspaceRoleBinding.Subjects, username) {
|
||||
workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username})
|
||||
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoveMembers(workspaceName string, users []string) error {
|
||||
func DeleteWorkspaceRoleBinding(workspace, username string, role string) error {
|
||||
|
||||
workspace, err := Detail(workspaceName)
|
||||
if !sliceutil.HasString(constants.WorkSpaceRoles, role) {
|
||||
return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role)
|
||||
}
|
||||
|
||||
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
|
||||
|
||||
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
|
||||
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = UnbindWorkspace(workspace, users)
|
||||
|
||||
if err != nil {
|
||||
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:]...)
|
||||
for i, v := range workspaceRoleBinding.Subjects {
|
||||
if v.Kind == v1.UserKind && v.Name == username {
|
||||
workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects[:i], workspaceRoleBinding.Subjects[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
workspace, err = Edit(workspace)
|
||||
workspaceRoleBinding, err = k8s.Client().RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Roles(workspace *models.Workspace) ([]*v1.ClusterRole, error) {
|
||||
roles := make([]*v1.ClusterRole, 0)
|
||||
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
|
||||
for _, name := range constants.WorkSpaceRoles {
|
||||
|
||||
clusterRole, err := clusterRoleLister.Get(fmt.Sprintf("system:%s:%s", workspace.Name, name))
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
go WorkspaceRoleInit(workspace)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterRole = clusterRole.DeepCopy()
|
||||
|
||||
clusterRole.Name = name
|
||||
roles = append(roles, clusterRole)
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
func WorkspaceRoleInit(workspace *models.Workspace) error {
|
||||
k8sClient := k8s.Client()
|
||||
|
||||
admin := new(v1.ClusterRole)
|
||||
admin.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceAdmin)
|
||||
admin.Kind = iam.ClusterRoleKind
|
||||
admin.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"kubesphere.io", "account.kubesphere.io"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
Resources: []string{"workspaces", "workspaces/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"devops.kubesphere.io", "jenkins.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"namespaces"},
|
||||
Resources: []string{"status/*", "monitoring/*", "quota/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"list"},
|
||||
APIGroups: []string{"account.kubesphere.io"},
|
||||
Resources: []string{"users"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"workspaces"},
|
||||
Resources: []string{"monitoring/" + workspace.Name},
|
||||
},
|
||||
}
|
||||
|
||||
admin.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
regular := new(v1.ClusterRole)
|
||||
regular.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceRegular)
|
||||
regular.Kind = iam.ClusterRoleKind
|
||||
regular.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
}, {
|
||||
Verbs: []string{"create"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces", "workspaces/devops"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"delete"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"workspaces/namespaces", "workspaces/devops"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"namespaces"},
|
||||
Resources: []string{"quota/*", "status/*", "monitoring/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"devops.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
}, {
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"jenkins.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
Resources: []string{"workspaces/members"},
|
||||
},
|
||||
}
|
||||
|
||||
regular.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
viewer := new(v1.ClusterRole)
|
||||
viewer.Name = fmt.Sprintf("system:%s:%s", workspace.Name, constants.WorkspaceViewer)
|
||||
viewer.Kind = iam.ClusterRoleKind
|
||||
viewer.Rules = []v1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io", "account.kubesphere.io"},
|
||||
ResourceNames: []string{workspace.Name},
|
||||
Resources: []string{"workspaces", "workspaces/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"namespaces"},
|
||||
Resources: []string{"quota/*", "status/*", "monitoring/*"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
Resources: []string{"resources"},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get"},
|
||||
APIGroups: []string{"kubesphere.io"},
|
||||
ResourceNames: []string{"workspaces"},
|
||||
Resources: []string{"monitoring/" + workspace.Name},
|
||||
},
|
||||
{
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"devops.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
}, {
|
||||
Verbs: []string{"get", "list"},
|
||||
APIGroups: []string{"jenkins.kubesphere.io"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
}
|
||||
|
||||
viewer.Labels = map[string]string{"creator": "system"}
|
||||
|
||||
_, err := k8sClient.RbacV1().ClusterRoles().Create(admin)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", admin.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
adminRoleBinding := new(v1.ClusterRoleBinding)
|
||||
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) {
|
||||
log.Println("cluster rolebinding create failed", adminRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoles().Create(regular)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", viewer.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
regularRoleBinding := new(v1.ClusterRoleBinding)
|
||||
regularRoleBinding.Name = regular.Name
|
||||
regularRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: regular.Name}
|
||||
regularRoleBinding.Subjects = make([]v1.Subject, 0)
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Create(regularRoleBinding)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster rolebinding create failed", regularRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoles().Create(viewer)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster role create failed", viewer.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
viewerRoleBinding := new(v1.ClusterRoleBinding)
|
||||
viewerRoleBinding.Name = viewer.Name
|
||||
viewerRoleBinding.RoleRef = v1.RoleRef{Kind: "ClusterRole", Name: viewer.Name}
|
||||
viewerRoleBinding.Subjects = make([]v1.Subject, 0)
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Create(viewerRoleBinding)
|
||||
if err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
log.Println("cluster rolebinding create failed", viewerRoleBinding.Name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unbindWorkspaceRole(workspace string, users []string) error {
|
||||
k8sClient := k8s.Client()
|
||||
|
||||
for _, name := range constants.WorkSpaceRoles {
|
||||
roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace, name), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
modify := false
|
||||
|
||||
for i := 0; i < len(roleBinding.Subjects); i++ {
|
||||
if roleBinding.Subjects[i].Kind == v1.UserKind && slice.ContainsString(users, roleBinding.Subjects[i].Name, nil) {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
i--
|
||||
modify = true
|
||||
}
|
||||
}
|
||||
|
||||
if modify {
|
||||
roleBinding, err = k8sClient.RbacV1().ClusterRoleBindings().Update(roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unbindNamespacesRole(namespaces []string, users []string) error {
|
||||
|
||||
k8sClient := k8s.Client()
|
||||
for _, namespace := range namespaces {
|
||||
|
||||
roleBindings, err := k8sClient.RbacV1().RoleBindings(namespace).List(meta_v1.ListOptions{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, roleBinding := range roleBindings.Items {
|
||||
|
||||
modify := false
|
||||
for i := 0; i < len(roleBinding.Subjects); i++ {
|
||||
if roleBinding.Subjects[i].Kind == v1.UserKind && slice.ContainsString(users, roleBinding.Subjects[i].Name, nil) {
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
modify = true
|
||||
}
|
||||
}
|
||||
if modify {
|
||||
_, err := k8sClient.RbacV1().RoleBindings(namespace).Update(&roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnbindWorkspace(workspace *models.Workspace, users []string) error {
|
||||
|
||||
err := unbindNamespacesRole(workspace.Namespaces, users)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = unbindWorkspaceRole(workspace.Name, users)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateWorkspaceRoleBinding(workspace *models.Workspace, username string, role string) error {
|
||||
|
||||
k8sClient := k8s.Client()
|
||||
|
||||
for _, roleName := range constants.WorkSpaceRoles {
|
||||
roleBinding, err := k8sClient.RbacV1().ClusterRoleBindings().Get(fmt.Sprintf("system:%s:%s", workspace.Name, roleName), meta_v1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
go WorkspaceRoleInit(workspace)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
modify := false
|
||||
|
||||
for i, v := range roleBinding.Subjects {
|
||||
if v.Kind == v1.UserKind && v.Name == username {
|
||||
if roleName == role {
|
||||
return nil
|
||||
} else {
|
||||
modify = true
|
||||
roleBinding.Subjects = append(roleBinding.Subjects[:i], roleBinding.Subjects[i+1:]...)
|
||||
if roleName == constants.WorkspaceAdmin || roleName == constants.WorkspaceViewer {
|
||||
go deleteDevopsRoleBinding(workspace.Name, "", username)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if roleName == role {
|
||||
modify = true
|
||||
roleBinding.Subjects = append(roleBinding.Subjects, v1.Subject{Kind: v1.UserKind, Name: username})
|
||||
if roleName == constants.WorkspaceAdmin {
|
||||
go createDevopsRoleBinding(workspace.Name, "", username, constants.DevopsOwner)
|
||||
} else if roleName == constants.WorkspaceViewer {
|
||||
go createDevopsRoleBinding(workspace.Name, "", username, constants.DevopsReporter)
|
||||
}
|
||||
}
|
||||
|
||||
if !modify {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = k8sClient.RbacV1().ClusterRoleBindings().Update(roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func GetDevOpsProjects(workspaceName string) ([]string, error) {
|
||||
@@ -1105,12 +523,12 @@ func GetDevOpsProjects(workspaceName string) ([]string, error) {
|
||||
return devOpsProjects, nil
|
||||
}
|
||||
|
||||
func GetOrgMembers(workspace string) ([]string, error) {
|
||||
ws, err := Detail(workspace)
|
||||
func WorkspaceUserCount(workspace string) (int, error) {
|
||||
count, err := iam.WorkspaceUsersTotalCount(workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
return ws.Members, nil
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func GetOrgRoles(name string) ([]string, error) {
|
||||
@@ -1135,13 +553,13 @@ func WorkspaceNamespaces(workspaceName string) ([]string, error) {
|
||||
|
||||
func WorkspaceCount() (int, error) {
|
||||
|
||||
workspaces, err := iam.ChildList("")
|
||||
ws, err := resources.ListResources("", resources.Workspaces, ¶ms.Conditions{}, "", false, 1, 0)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(workspaces), nil
|
||||
return ws.TotalCount, nil
|
||||
}
|
||||
|
||||
func GetAllProjectNums() (int, error) {
|
||||
@@ -1155,7 +573,6 @@ func GetAllProjectNums() (int, error) {
|
||||
|
||||
func GetAllDevOpsProjectsNums() (int, error) {
|
||||
db := mysql.Client()
|
||||
|
||||
var count int
|
||||
if err := db.Model(&models.WorkspaceDPBinding{}).Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
@@ -1164,11 +581,9 @@ func GetAllDevOpsProjectsNums() (int, error) {
|
||||
}
|
||||
|
||||
func GetAllAccountNums() (int, error) {
|
||||
totalCount, _, err := iam.UserList(1, 0)
|
||||
|
||||
users, err := iam.ListUsers(¶ms.Conditions{}, "", false, 1, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return totalCount, nil
|
||||
return users.TotalCount, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user