提交 5c4efd53 编写于 作者: H hongming 提交者: zryfish

refactor tenant api

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 71633730
...@@ -192,9 +192,10 @@ func addWebService(c *restful.Container) error { ...@@ -192,9 +192,10 @@ func addWebService(c *restful.Container) error {
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Add user to workspace"). Doc("Add user to workspace").
Metadata(restfulspec.KeyOpenAPITags, tags)) Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{workspace}/members"). ws.Route(ws.DELETE("/workspaces/{workspace}/members/{username}").
To(iam.RemoveUser). To(iam.RemoveUser).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("name", "username")).
Doc("Remove user from workspace"). Doc("Remove user from workspace").
Metadata(restfulspec.KeyOpenAPITags, tags)) Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}"). ws.Route(ws.GET("/workspaces/{workspace}/members/{username}").
......
...@@ -91,7 +91,7 @@ func addWebService(c *restful.Container) error { ...@@ -91,7 +91,7 @@ func addWebService(c *restful.Container) error {
To(resources.ApplicationHandler). To(resources.ApplicationHandler).
Writes(models.PageableResponse{}). Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags). Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Cluster level resource query"). Doc("List applications in cluster").
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false). Required(false).
DataFormat("key=value,key~value"). DataFormat("key=value,key~value").
...@@ -103,9 +103,24 @@ func addWebService(c *restful.Container) error { ...@@ -103,9 +103,24 @@ func addWebService(c *restful.Container) error {
DataFormat("limit=%d,page=%d"). DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1"))) DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/namespaces/{namespace}/applications").
To(resources.NamespacedApplicationHandler).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("List applications").
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=value,key~value").
DefaultValue("")).
Param(webservice.PathParameter("namespace", "namespace")).
Param(webservice.QueryParameter(params.PagingParam, "page").
Required(false).
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims"). webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
To(resources.GetPvcListBySc). To(resources.GetPvcListBySc).
Doc("get user's kubectl pod"). Doc("query persistent volume claims by storageclass").
Param(webservice.PathParameter("username", "username")). Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags)) Metadata(restfulspec.KeyOpenAPITags, tags))
......
...@@ -42,6 +42,10 @@ func addWebService(c *restful.Container) error { ...@@ -42,6 +42,10 @@ func addWebService(c *restful.Container) error {
To(tenant.ListWorkspaces). To(tenant.ListWorkspaces).
Doc("List workspace by user"). Doc("List workspace by user").
Metadata(restfulspec.KeyOpenAPITags, tags)) Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}").
To(tenant.DescribeWorkspace).
Doc("Get workspace detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/rules"). ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(tenant.ListWorkspaceRules). To(tenant.ListWorkspaceRules).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
...@@ -96,7 +100,7 @@ func addWebService(c *restful.Container) error { ...@@ -96,7 +100,7 @@ func addWebService(c *restful.Container) error {
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create devops project"). Doc("Create devops project").
Metadata(restfulspec.KeyOpenAPITags, tags)) Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{workspace}/devops"). ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{id}").
To(tenant.DeleteDevopsProject). To(tenant.DeleteDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Delete devops project"). Doc("Delete devops project").
......
...@@ -41,7 +41,7 @@ func addWebService(c *restful.Container) error { ...@@ -41,7 +41,7 @@ func addWebService(c *restful.Container) error {
tags := []string{"Terminal"} tags := []string{"Terminal"}
webservice.Route(webservice.GET("/namespace/{namespace}/pods/{pods}"). webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pods}").
To(terminal.CreateTerminalSession). To(terminal.CreateTerminalSession).
Doc("create terminal session"). Doc("create terminal session").
Metadata(restfulspec.KeyOpenAPITags, tags). Metadata(restfulspec.KeyOpenAPITags, tags).
......
...@@ -142,7 +142,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) { ...@@ -142,7 +142,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) {
for i := 0; i < len(group.Members); i++ { for i := 0; i < len(group.Members); i++ {
name := group.Members[i] name := group.Members[i]
user, err := iam.DescribeUser(name) user, err := iam.GetUserInfo(name)
if err != nil { if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
......
...@@ -234,22 +234,11 @@ func ListUsers(req *restful.Request, resp *restful.Response) { ...@@ -234,22 +234,11 @@ func ListUsers(req *restful.Request, resp *restful.Response) {
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam) orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.ParseReverse(req) reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return return
} }
if len(names) > 0 {
users, err := iam.ListUsersByName(names)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
return
}
users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset) users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset)
......
...@@ -91,7 +91,7 @@ func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) { ...@@ -91,7 +91,7 @@ func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) {
return return
} }
user, err := iam.DescribeUser(username) user, err := iam.GetUserInfo(username)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
...@@ -132,15 +132,9 @@ func InviteUser(req *restful.Request, resp *restful.Response) { ...@@ -132,15 +132,9 @@ func InviteUser(req *restful.Request, resp *restful.Response) {
func RemoveUser(req *restful.Request, resp *restful.Response) { func RemoveUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
var user models.User username := req.PathParameter("username")
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = workspaces.InviteUser(workspace, &user)
err := workspaces.RemoveUser(workspace, username)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return return
......
...@@ -19,10 +19,11 @@ package resources ...@@ -19,10 +19,11 @@ package resources
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors" "kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/applications" "kubesphere.io/kubesphere/pkg/models/applications"
"kubesphere.io/kubesphere/pkg/models/resources"
//"kubesphere.io/kubesphere/pkg/models/applications"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"net/http" "net/http"
) )
...@@ -58,3 +59,42 @@ func ApplicationHandler(req *restful.Request, resp *restful.Response) { ...@@ -58,3 +59,42 @@ func ApplicationHandler(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(result) resp.WriteAsJson(result)
} }
func NamespacedApplicationHandler(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
namespaceName := req.PathParameter("namespace")
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
if err != nil {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
}
namespace, err := resources.GetResource("", resources.Namespaces, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
var runtimeId string
if ns, ok := namespace.(*v1.Namespace); ok {
runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]
}
if runtimeId == "" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.New("openpitrix runtime not init"))
return
}
result, err := applications.ListApplication(runtimeId, conditions, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
...@@ -19,7 +19,6 @@ package resources ...@@ -19,7 +19,6 @@ package resources
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources" "kubesphere.io/kubesphere/pkg/models/resources"
"net/http" "net/http"
...@@ -34,7 +33,6 @@ func ListResources(req *restful.Request, resp *restful.Response) { ...@@ -34,7 +33,6 @@ func ListResources(req *restful.Request, resp *restful.Response) {
orderBy := req.QueryParameter(params.OrderByParam) orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req) reverse := params.ParseReverse(req)
names := params.ParseArray(req.QueryParameter(params.NameParam))
if orderBy == "" { if orderBy == "" {
orderBy = resources.CreateTime orderBy = resources.CreateTime
...@@ -46,12 +44,7 @@ func ListResources(req *restful.Request, resp *restful.Response) { ...@@ -46,12 +44,7 @@ func ListResources(req *restful.Request, resp *restful.Response) {
return return
} }
var result *models.PageableResponse result, err := resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
if len(names) > 0 {
result, err = resources.ListResourcesByName(namespace, resourceName, names)
} else {
result, err = resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
}
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
......
...@@ -21,6 +21,7 @@ package routers ...@@ -21,6 +21,7 @@ package routers
import ( import (
"fmt" "fmt"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"net/http" "net/http"
"kubesphere.io/kubesphere/pkg/errors" "kubesphere.io/kubesphere/pkg/errors"
...@@ -57,7 +58,11 @@ func GetRouter(request *restful.Request, response *restful.Response) { ...@@ -57,7 +58,11 @@ func GetRouter(request *restful.Request, response *restful.Response) {
router, err := routers.GetRouter(namespace) router, err := routers.GetRouter(namespace)
if err != nil { if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) if k8serr.IsNotFound(err) {
response.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return return
} }
......
...@@ -19,6 +19,7 @@ package tenant ...@@ -19,6 +19,7 @@ package tenant
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/golang/glog"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors" k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
...@@ -26,10 +27,11 @@ import ( ...@@ -26,10 +27,11 @@ import (
"kubesphere.io/kubesphere/pkg/errors" "kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/models/tenant" "kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces" "kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"log" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"net/http" "net/http"
) )
...@@ -54,6 +56,11 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) { ...@@ -54,6 +56,11 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req) reverse := params.ParseReverse(req)
if orderBy == "" {
orderBy = resources.CreateTime
reverse = true
}
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return return
...@@ -69,6 +76,20 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) { ...@@ -69,6 +76,20 @@ func ListWorkspaces(req *restful.Request, resp *restful.Response) {
resp.WriteAsJson(result) resp.WriteAsJson(result)
} }
func DescribeWorkspace(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
workspaceName := req.PathParameter("workspace")
result, err := tenant.DescribeWorkspace(username, workspaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func ListNamespaces(req *restful.Request, resp *restful.Response) { func ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
username := req.PathParameter("username") username := req.PathParameter("username")
...@@ -88,7 +109,7 @@ func ListNamespaces(req *restful.Request, resp *restful.Response) { ...@@ -88,7 +109,7 @@ func ListNamespaces(req *restful.Request, resp *restful.Response) {
return return
} }
conditions.Match["kubesphere.io/workspace"] = workspace conditions.Match[constants.WorkspaceLabelKey] = workspace
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset) result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
...@@ -188,18 +209,24 @@ func ListDevopsProjects(req *restful.Request, resp *restful.Response) { ...@@ -188,18 +209,24 @@ func ListDevopsProjects(req *restful.Request, resp *restful.Response) {
func DeleteDevopsProject(req *restful.Request, resp *restful.Response) { func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id") devops := req.PathParameter("id")
workspace := req.PathParameter("workspace") workspaceName := req.PathParameter("workspace")
force := req.QueryParameter("force")
username := req.HeaderParameter(constants.UserNameHeader) username := req.HeaderParameter(constants.UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops) _, err := tenant.GetWorkspace(workspaceName)
if err != nil && force != "true" { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = kubesphere.Client().DeleteDevopsProject(username, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return return
} }
err = workspaces.DeleteDevopsProject(username, devops) err = workspaces.UnBindDevopsProject(workspaceName, devops)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
...@@ -211,7 +238,7 @@ func DeleteDevopsProject(req *restful.Request, resp *restful.Response) { ...@@ -211,7 +238,7 @@ func DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
func CreateDevopsProject(req *restful.Request, resp *restful.Response) { func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace") workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader) username := req.HeaderParameter(constants.UserNameHeader)
var devops models.DevopsProject var devops models.DevopsProject
...@@ -223,8 +250,8 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) { ...@@ -223,8 +250,8 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
return return
} }
log.Println("create workspace", username, workspace, devops) glog.Infoln("create workspace", username, workspaceName, devops)
project, err := workspaces.CreateDevopsProject(username, workspace, devops) project, err := workspaces.CreateDevopsProject(username, workspaceName, &devops)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
...@@ -232,7 +259,6 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) { ...@@ -232,7 +259,6 @@ func CreateDevopsProject(req *restful.Request, resp *restful.Response) {
} }
resp.WriteAsJson(project) resp.WriteAsJson(project)
} }
func ListNamespaceRules(req *restful.Request, resp *restful.Response) { func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
......
...@@ -47,7 +47,5 @@ const ( ...@@ -47,7 +47,5 @@ const (
var ( var (
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer} WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
SystemWorkspace = "system-workspace"
DevopsAPIServer = "ks-devops.kubesphere-devops-system.svc"
SystemNamespaces = []string{KubeSphereNamespace, OpenPitrixNamespace, KubeSystemNamespace} SystemNamespaces = []string{KubeSphereNamespace, OpenPitrixNamespace, KubeSystemNamespace}
) )
...@@ -153,9 +153,9 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res ...@@ -153,9 +153,9 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res
return reconcile.Result{}, err return reconcile.Result{}, err
} }
if err = r.createGroup(instance); err != nil { //if err = r.createGroup(instance); err != nil {
return reconcile.Result{}, err // return reconcile.Result{}, err
} //}
if err = r.createWorkspaceRoleBindings(instance); err != nil { if err = r.createWorkspaceRoleBindings(instance); err != nil {
return reconcile.Result{}, err return reconcile.Result{}, err
...@@ -369,7 +369,7 @@ func (r *ReconcileWorkspace) createWorkspaceRoleBindings(instance *tenantv1alpha ...@@ -369,7 +369,7 @@ func (r *ReconcileWorkspace) createWorkspaceRoleBindings(instance *tenantv1alpha
regularRoleBinding := &rbac.ClusterRoleBinding{} regularRoleBinding := &rbac.ClusterRoleBinding{}
regularRoleBinding.Name = getWorkspaceRegularRoleBindingName(instance.Name) regularRoleBinding.Name = getWorkspaceRegularRoleBindingName(instance.Name)
regularRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name} regularRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name}
regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceViewerRoleName(instance.Name)} regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceRegularRoleName(instance.Name)}
regularRoleBinding.Subjects = []rbac.Subject{} regularRoleBinding.Subjects = []rbac.Subject{}
if err = controllerutil.SetControllerReference(instance, regularRoleBinding, r.scheme); err != nil { if err = controllerutil.SetControllerReference(instance, regularRoleBinding, r.scheme); err != nil {
......
...@@ -18,20 +18,17 @@ ...@@ -18,20 +18,17 @@
package iam package iam
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"io/ioutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources" "kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"sort" "sort"
"strings" "strings"
...@@ -47,7 +44,7 @@ import ( ...@@ -47,7 +44,7 @@ import (
const ClusterRoleKind = "ClusterRole" const ClusterRoleKind = "ClusterRole"
func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) { func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) {
role, err := GetUserDevopsRole(projectId, username) role, err := kubesphere.Client().GetUserDevopsRole(username, projectId)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -100,53 +97,6 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { ...@@ -100,53 +97,6 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule {
return rules return rules
} }
func GetUserDevopsRole(projectId string, username string) (string, error) {
//Hard fix
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > 200 {
return "", errors.New(string(data))
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
// Get user roles in namespace // Get user roles in namespace
func GetUserRoles(namespace, username string) ([]*v1.Role, error) { func GetUserRoles(namespace, username string) ([]*v1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
...@@ -267,13 +217,6 @@ func GetUserRules(namespace, username string) ([]v1.PolicyRule, error) { ...@@ -267,13 +217,6 @@ func GetUserRules(namespace, username string) ([]v1.PolicyRule, error) {
return rules, nil return rules, nil
} }
func isUserFacingClusterRole(role *v1.ClusterRole) bool {
if role.Labels[constants.CreatorLabelKey] != "" {
return true
}
return false
}
func GetWorkspaceRoleBindings(workspace string) ([]*v1.ClusterRoleBinding, error) { func GetWorkspaceRoleBindings(workspace string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
...@@ -386,7 +329,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, ...@@ -386,7 +329,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions,
for _, roleBinding := range roleBindings { for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects { for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name) user, err := GetUserInfo(subject.Name)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue continue
} }
...@@ -436,7 +379,7 @@ func RoleUsers(namespace string, roleName string) ([]*models.User, error) { ...@@ -436,7 +379,7 @@ func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
for _, roleBinding := range roleBindings { for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects { for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name) user, err := GetUserInfo(subject.Name)
if err != nil { if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
...@@ -491,10 +434,14 @@ func NamespaceUsers(namespaceName string) ([]*models.User, error) { ...@@ -491,10 +434,14 @@ func NamespaceUsers(namespaceName string) ([]*models.User, error) {
users := make([]*models.User, 0) users := make([]*models.User, 0)
for _, roleBinding := range roleBindings { for _, roleBinding := range roleBindings {
// controlled by ks-controller-manager
if roleBinding.Name == "admin" || roleBinding.Name == "viewer" {
continue
}
for _, subject := range roleBinding.Subjects { for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name) user, err := GetUserInfo(subject.Name)
if err != nil { if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/redis" "kubesphere.io/kubesphere/pkg/simple/client/redis"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
...@@ -232,7 +233,7 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) { ...@@ -232,7 +233,7 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) {
for _, name := range names { for _, name := range names {
if !k8sutil.ContainsUser(users, name) { if !k8sutil.ContainsUser(users, name) {
user, err := DescribeUser(name) user, err := GetUserInfo(name)
if err != nil { if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue continue
...@@ -252,6 +253,30 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) { ...@@ -252,6 +253,30 @@ func ListUsersByName(names []string) (*models.PageableResponse, error) {
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
} }
func ListUserByEmail(email []string) (*models.PageableResponse, error) {
users := make([]*models.User, 0)
for _, mail := range email {
user, err := GetUserInfoByEmail(mail)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
if !k8sutil.ContainsUser(users, user.Username) {
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) { func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conn, err := ldapclient.Client() conn, err := ldapclient.Client()
...@@ -272,6 +297,22 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi ...@@ -272,6 +297,22 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword) filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
} }
if username := conditions.Match["username"]; username != "" {
uidFilter := ""
for _, username := range strings.Split(username, "|") {
uidFilter += fmt.Sprintf("(uid=%s)", username)
}
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter)
}
if email := conditions.Match["email"]; email != "" {
emailFilter := ""
for _, username := range strings.Split(email, "|") {
emailFilter += fmt.Sprintf("(mail=%s)", username)
}
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter)
}
for { for {
userSearchRequest := ldap.NewSearchRequest( userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase, ldapclient.UserSearchBase,
...@@ -331,26 +372,13 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi ...@@ -331,26 +372,13 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi
if i >= offset && len(items) < limit { if i >= offset && len(items) < limit {
avatar, err := getAvatar(user.Username) user.AvatarUrl = getAvatar(user.Username)
if err != nil { user.LastLoginTime = getLastLoginTime(user.Username)
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) clusterRole, err := GetUserClusterRole(user.Username)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user.ClusterRole = clusterRole.Name user.ClusterRole = clusterRole.Name
items = append(items, user) items = append(items, user)
} }
} }
...@@ -373,23 +401,47 @@ func DescribeUser(username string) (*models.User, error) { ...@@ -373,23 +401,47 @@ func DescribeUser(username string) (*models.User, error) {
} }
user.Groups = groups user.Groups = groups
user.AvatarUrl = getAvatar(username)
avatar, err := getAvatar(username) return user, nil
}
func GetUserInfoByEmail(mail string) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil { if err != nil {
return nil, err return nil, err
} }
user.AvatarUrl = avatar userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(mail=%s))", mail),
[]string{"uid", "description", "preferredLanguage", "createTimestamp"},
nil,
)
lastLoginTime, err := getLastLoginTime(username) result, err := conn.Search(userSearchRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user.LastLoginTime = lastLoginTime if len(result.Entries) != 1 {
return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, fmt.Errorf("user %s does not exist", mail))
}
username := result.Entries[0].GetAttributeValue("uid")
description := result.Entries[0].GetAttributeValue("description")
lang := result.Entries[0].GetAttributeValue("preferredLanguage")
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: mail, Description: description, Lang: lang, CreateTime: createTimestamp}
user.LastLoginTime = getLastLoginTime(username)
clusterRole, err := GetUserClusterRole(user.Username)
if err != nil {
return nil, err
}
user.ClusterRole = clusterRole.Name
return user, nil return user, nil
} }
...@@ -426,6 +478,8 @@ func GetUserInfo(username string) (*models.User, error) { ...@@ -426,6 +478,8 @@ func GetUserInfo(username string) (*models.User, error) {
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp")) createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
user.LastLoginTime = getLastLoginTime(username)
return user, nil return user, nil
} }
...@@ -462,17 +516,18 @@ func GetUserGroups(username string) ([]string, error) { ...@@ -462,17 +516,18 @@ func GetUserGroups(username string) ([]string, error) {
return groups, nil return groups, nil
} }
func getLastLoginTime(username string) (string, error) { func getLastLoginTime(username string) string {
lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result() lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil { if err != nil {
return "", err return ""
} }
if len(lastLogin) > 0 { if len(lastLogin) > 0 {
return strings.Split(lastLogin[0], ",")[0], nil return strings.Split(lastLogin[0], ",")[0]
} }
return "", nil
return ""
} }
func setAvatar(username, avatar string) error { func setAvatar(username, avatar string) error {
...@@ -480,20 +535,21 @@ func setAvatar(username, avatar string) error { ...@@ -480,20 +535,21 @@ func setAvatar(username, avatar string) error {
return err return err
} }
func getAvatar(username string) (string, error) { func getAvatar(username string) string {
avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result() avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result()
if err != nil { if err != nil {
return "", err return ""
} }
if len(avatar) > 0 { if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok { if url, ok := avatar[0].(string); ok {
return url, nil return url
} }
} }
return "", nil
return ""
} }
func DeleteUser(username string) error { func DeleteUser(username string) error {
...@@ -811,7 +867,7 @@ func UpdateUser(user *models.User) (*models.User, error) { ...@@ -811,7 +867,7 @@ func UpdateUser(user *models.User) (*models.User, error) {
return nil, err return nil, err
} }
return DescribeUser(user.Username) return GetUserInfo(user.Username)
} }
func DeleteGroup(path string) error { func DeleteGroup(path string) error {
...@@ -1105,13 +1161,15 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy ...@@ -1105,13 +1161,15 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy
for _, roleBinding := range workspaceRoleBindings { for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects { for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name) user, err := GetUserInfo(subject.Name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
prefix := fmt.Sprintf("workspace:%s:", workspace) prefix := fmt.Sprintf("workspace:%s:", workspace)
user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix)) user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix))
users = append(users, user) if matchConditions(conditions, user) {
users = append(users, user)
}
} }
} }
} }
...@@ -1141,3 +1199,31 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy ...@@ -1141,3 +1199,31 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
} }
func matchConditions(conditions *params.Conditions, user *models.User) bool {
for k, v := range conditions.Match {
switch k {
case "keyword":
if !strings.Contains(user.Username, v) &&
!strings.Contains(user.Email, v) &&
!strings.Contains(user.Description, v) {
return false
}
case "name":
names := strings.Split(v, "|")
if !sliceutil.HasString(names, user.Username) {
return false
}
case "email":
email := strings.Split(v, "|")
if !sliceutil.HasString(email, user.Email) {
return false
}
case "role":
if user.WorkspaceRole != v {
return false
}
}
}
return true
}
...@@ -126,6 +126,18 @@ var ( ...@@ -126,6 +126,18 @@ var (
}, },
}, },
}, },
{
Name: "logging",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"get", "list"},
APIGroups: []string{"logging.kubesphere.io"},
Resources: []string{"*"},
}},
},
},
},
{ {
Name: "accounts", Name: "accounts",
Actions: []models.Action{ Actions: []models.Action{
...@@ -683,8 +695,8 @@ var ( ...@@ -683,8 +695,8 @@ var (
Rules: []v1.PolicyRule{ Rules: []v1.PolicyRule{
{ {
Verbs: []string{"get"}, Verbs: []string{"get"},
APIGroups: []string{"resources.kubesphere.io"}, APIGroups: []string{"terminal.kubesphere.io"},
Resources: []string{"pod/terminal"}, Resources: []string{"pods"},
}, },
}, },
}, },
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
package resources package resources
import ( import (
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
...@@ -55,6 +56,12 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol ...@@ -55,6 +56,12 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol
if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) {
return false return false
} }
case "userfacing":
if v == "true" {
if !isUserFacingClusterRole(item) {
return false
}
}
default: default:
if item.Labels[k] != v { if item.Labels[k] != v {
return false return false
...@@ -134,3 +141,10 @@ func (s *clusterRoleSearcher) search(namespace string, conditions *params.Condit ...@@ -134,3 +141,10 @@ func (s *clusterRoleSearcher) search(namespace string, conditions *params.Condit
} }
return r, nil return r, nil
} }
func isUserFacingClusterRole(role *rbac.ClusterRole) bool {
if role.Labels[constants.CreatorLabelKey] != "" && role.Labels[constants.WorkspaceLabelKey] == "" {
return true
}
return false
}
...@@ -103,24 +103,15 @@ type resourceSearchInterface interface { ...@@ -103,24 +103,15 @@ type resourceSearchInterface interface {
search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
} }
func ListResourcesByName(namespace, resource string, names []string) (*models.PageableResponse, error) { func GetResource(namespace, resource, name string) (interface{}, error) {
items := make([]interface{}, 0)
if searcher, ok := resources[resource]; ok { if searcher, ok := resources[resource]; ok {
for _, name := range names { resource, err := searcher.get(namespace, name)
item, err := searcher.get(namespace, name) if err != nil {
return nil, err
if err != nil {
return nil, err
}
items = append(items, item)
} }
return resource, nil
} else {
return nil, fmt.Errorf("not found")
} }
return nil, fmt.Errorf("resource %s not found", resource)
return &models.PageableResponse{TotalCount: len(items), Items: items}, nil
} }
func ListResources(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) {
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"github.com/golang/glog" "github.com/golang/glog"
"io/ioutil" "io/ioutil"
"k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"sort" "sort"
...@@ -127,7 +128,7 @@ func GetRouter(namespace string) (*corev1.Service, error) { ...@@ -127,7 +128,7 @@ func GetRouter(namespace string) (*corev1.Service, error) {
} }
} }
return nil, fmt.Errorf("resources not found %s", serviceName) return nil, errors.NewNotFound(corev1.Resource("service"), serviceName)
} }
// Load all resource yamls // Load all resource yamls
......
...@@ -18,15 +18,10 @@ ...@@ -18,15 +18,10 @@
package tenant package tenant
import ( 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/models"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http"
"sort" "sort"
"strings" "strings"
) )
...@@ -41,79 +36,55 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition ...@@ -41,79 +36,55 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition
return nil, err return nil, err
} }
devOpsProjects := make([]models.DevopsProject, 0) projects, err := kubesphere.Client().ListDevopsProjects(username)
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 { if err != nil {
return nil, err return nil, err
} }
if keyword := conditions.Match["keyword"]; keyword != "" { if keyword := conditions.Match["keyword"]; keyword != "" {
for i := 0; i < len(devOpsProjects); i++ { for i := 0; i < len(projects); i++ {
if !strings.Contains(devOpsProjects[i].Name, keyword) { if !strings.Contains(projects[i].Name, keyword) {
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...) projects = append(projects[:i], projects[i+1:]...)
i-- i--
} }
} }
} }
sort.Slice(devOpsProjects, func(i, j int) bool { sort.Slice(projects, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy { switch orderBy {
case "name": case "name":
if reverse { return projects[i].Name > projects[j].Name
return devOpsProjects[i].Name < devOpsProjects[j].Name
} else {
return devOpsProjects[i].Name > devOpsProjects[j].Name
}
default: default:
if reverse { return projects[i].CreateTime.Before(*projects[j].CreateTime)
return devOpsProjects[i].CreateTime.After(*devOpsProjects[j].CreateTime)
} else {
return devOpsProjects[i].CreateTime.Before(*devOpsProjects[j].CreateTime)
}
} }
}) })
for i := 0; i < len(devOpsProjects); i++ { for i := 0; i < len(projects); i++ {
inWorkspace := false inWorkspace := false
for _, binding := range workspaceDOPBindings { for _, binding := range workspaceDOPBindings {
if binding.DevOpsProject == *devOpsProjects[i].ProjectId { if binding.DevOpsProject == projects[i].ProjectId {
inWorkspace = true inWorkspace = true
} }
} }
if !inWorkspace { if !inWorkspace {
devOpsProjects = append(devOpsProjects[:i], devOpsProjects[i+1:]...) projects = append(projects[:i], projects[i+1:]...)
i-- i--
} }
} }
// limit offset // limit offset
result := make([]interface{}, 0) result := make([]interface{}, 0)
for i, v := range devOpsProjects { for i, v := range projects {
if len(result) < limit && i >= offset { if len(result) < limit && i >= offset {
result = append(result, v) result = append(result, v)
} }
} }
return &models.PageableResponse{Items: result, TotalCount: len(devOpsProjects)}, nil return &models.PageableResponse{Items: result, TotalCount: len(projects)}, nil
} }
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
...@@ -35,6 +36,14 @@ type namespaceSearcher struct { ...@@ -35,6 +36,14 @@ type namespaceSearcher struct {
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match { for k, v := range match {
switch k { switch k {
case "name":
if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v {
return false
}
case "keyword":
if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) {
return false
}
default: default:
if item.Labels[k] != v { if item.Labels[k] != v {
return false return false
......
...@@ -19,7 +19,9 @@ package tenant ...@@ -19,7 +19,9 @@ package tenant
import ( import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
ws "kubesphere.io/kubesphere/pkg/models/workspaces" ws "kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
...@@ -45,6 +47,18 @@ func CreateNamespace(workspaceName string, namespace *v1.Namespace, username str ...@@ -45,6 +47,18 @@ func CreateNamespace(workspaceName string, namespace *v1.Namespace, username str
return k8s.Client().CoreV1().Namespaces().Create(namespace) return k8s.Client().CoreV1().Namespaces().Create(namespace)
} }
func DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
if err != nil {
return nil, err
}
workspace = appendAnnotations(username, workspace)
return workspace, nil
}
func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { 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) workspaces, err := workspaces.search(username, conditions, orderBy, reverse)
...@@ -57,25 +71,7 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri ...@@ -57,25 +71,7 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
result := make([]interface{}, 0) result := make([]interface{}, 0)
for i, workspace := range workspaces { for i, workspace := range workspaces {
if len(result) < limit && i >= offset { if len(result) < limit && i >= offset {
workspace := workspace.DeepCopy() workspace := appendAnnotations(username, workspace)
ns, err := ListNamespaces(username, &params.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, &params.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) result = append(result, workspace)
} }
} }
...@@ -83,6 +79,32 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri ...@@ -83,6 +79,32 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri
return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil
} }
func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace {
workspace = workspace.DeepCopy()
if workspace.Annotations == nil {
workspace.Annotations = make(map[string]string)
}
ns, err := ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
} else {
workspace.Annotations["kubesphere.io/namespace-count"] = "-1"
}
devops, err := ListDevopsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
} else {
workspace.Annotations["kubesphere.io/devops-count"] = "-1"
}
userCount, err := ws.WorkspaceUserCount(workspace.Name)
if err == nil {
workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount)
} else {
workspace.Annotations["kubesphere.io/member-count"] = "-1"
}
return workspace
}
func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { 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) namespaces, err := namespaces.search(username, conditions, orderBy, reverse)
......
...@@ -40,6 +40,10 @@ func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspac ...@@ -40,6 +40,10 @@ func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspac
if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v { if item.Name != v && item.Labels[constants.DisplayNameLabelKey] != v {
return false return false
} }
case "keyword":
if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) {
return false
}
default: default:
if item.Labels[k] != v { if item.Labels[k] != v {
return false return false
...@@ -128,3 +132,16 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition ...@@ -128,3 +132,16 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition
func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) { func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName) return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
} }
func contains(m map[string]string, key, value string) bool {
for k, v := range m {
if key == "" {
if strings.Contains(k, value) || strings.Contains(v, value) {
return true
}
} else if k == key && strings.Contains(v, value) {
return true
}
}
return false
}
...@@ -42,7 +42,7 @@ type WorkspaceDPBinding struct { ...@@ -42,7 +42,7 @@ type WorkspaceDPBinding struct {
} }
type DevopsProject struct { type DevopsProject struct {
ProjectId *string `json:"project_id,omitempty"` ProjectId string `json:"project_id,omitempty"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Creator string `json:"creator"` Creator string `json:"creator"`
......
...@@ -18,41 +18,29 @@ ...@@ -18,41 +18,29 @@
package workspaces package workspaces
import ( import (
"bytes"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/resources" "kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params" "kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/ldap" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
"kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/utils/sliceutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"strings" "strings"
"github.com/jinzhu/gorm"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
"errors" "errors"
"github.com/golang/glog"
"k8s.io/api/rbac/v1" "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"sort"
kserr "kubesphere.io/kubesphere/pkg/errors"
) )
func UnBindDevopsProject(workspace string, devops string) error { func UnBindDevopsProject(workspace string, devops string) error {
...@@ -60,191 +48,26 @@ func UnBindDevopsProject(workspace string, devops string) error { ...@@ -60,191 +48,26 @@ func UnBindDevopsProject(workspace string, devops string) error {
return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
} }
func DeleteDevopsProject(username string, devops string) error { func CreateDevopsProject(username string, workspace string, devops *models.DevopsProject) (*models.DevopsProject, error) {
request, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf("http://%s/api/v1alpha/projects/%s", constants.DevopsAPIServer, devops), nil)
request.Header.Add("X-Token-Username", username)
result, err := http.DefaultClient.Do(request)
if err != nil {
return err
}
defer result.Body.Close()
data, err := ioutil.ReadAll(result.Body)
if err != nil {
return err
}
if result.StatusCode > 200 {
return kserr.Parse(data)
}
return nil
}
func CreateDevopsProject(username string, workspace string, devops models.DevopsProject) (*models.DevopsProject, error) {
data, err := json.Marshal(devops)
if err != nil {
return nil, err
}
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), bytes.NewReader(data))
request.Header.Add("X-Token-Username", username)
request.Header.Add("Content-Type", "application/json")
result, err := http.DefaultClient.Do(request)
if err != nil {
return nil, err
}
defer result.Body.Close()
data, err = ioutil.ReadAll(result.Body)
if err != nil {
return nil, err
}
if result.StatusCode > 200 { created, err := kubesphere.Client().CreateDevopsProject(username, devops)
return nil, kserr.Parse(data)
}
var project models.DevopsProject
err = json.Unmarshal(data, &project)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = BindingDevopsProject(workspace, *project.ProjectId) err = BindingDevopsProject(workspace, created.ProjectId)
if err != nil { if err != nil {
DeleteDevopsProject(username, *project.ProjectId)
return nil, err return nil, err
} }
go createDefaultDevopsRoleBinding(workspace, project) return created, nil
return &project, nil
}
func createDefaultDevopsRoleBinding(workspace string, project models.DevopsProject) error {
admins := []string{""}
for _, admin := range admins {
createDevopsRoleBinding(workspace, *project.ProjectId, admin, constants.DevopsOwner)
}
viewers := []string{""}
for _, viewer := range viewers {
createDevopsRoleBinding(workspace, *project.ProjectId, viewer, constants.DevopsReporter)
}
return nil
}
func createDevopsRoleBinding(workspace string, projectId string, user string, role string) {
projects := make([]string, 0)
if projectId != "" {
projects = append(projects, projectId)
} else {
p, err := GetDevOpsProjects(workspace)
if err != nil {
glog.Warning("create devops role binding failed", workspace, projectId, user, role)
return
}
projects = append(projects, p...)
}
for _, project := range projects {
data := []byte(fmt.Sprintf(`{"username":"%s","role":"%s"}`, user, role))
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, project), bytes.NewReader(data))
request.Header.Add("Content-Type", "application/json")
request.Header.Add("X-Token-Username", "admin")
resp, err := http.DefaultClient.Do(request)
if err != nil || resp.StatusCode > 200 {
glog.Warning(fmt.Sprintf("create devops role binding failed %s,%s,%s,%s", workspace, project, user, role))
}
if resp != nil {
resp.Body.Close()
}
}
}
func ListNamespaceByUser(workspaceName string, username string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []*core.Namespace, error) {
namespaces, err := Namespaces(workspaceName)
if err != nil {
return 0, nil, err
}
if keyword != "" {
for i := 0; i < len(namespaces); i++ {
if !strings.Contains(namespaces[i].Name, keyword) {
namespaces = append(namespaces[:i], namespaces[i+1:]...)
i--
}
}
}
sort.Slice(namespaces, func(i, j int) bool {
switch orderBy {
case "name":
if reverse {
return namespaces[i].Name < namespaces[j].Name
} else {
return namespaces[i].Name > namespaces[j].Name
}
default:
if reverse {
return namespaces[i].CreationTimestamp.Time.After(namespaces[j].CreationTimestamp.Time)
} else {
return namespaces[i].CreationTimestamp.Time.Before(namespaces[j].CreationTimestamp.Time)
}
}
})
rules, err := iam.GetUserClusterRules(username)
if err != nil {
return 0, nil, err
}
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.GetUserRoles(namespaces[i].Name, username)
if err != nil {
return 0, nil, err
}
rules := make([]v1.PolicyRule, 0)
for _, role := range roles {
rules = append(rules, role.Rules...)
}
if !iam.RulesMatchesRequired(rules, v1.PolicyRule{APIGroups: []string{""}, ResourceNames: []string{namespaces[i].Name}, Verbs: []string{"get"}, Resources: []string{"namespaces"}}) {
namespaces = append(namespaces[:i], namespaces[i+1:]...)
i--
}
}
}
if len(namespaces) < offset {
return len(namespaces), namespaces, nil
} else if len(namespaces) < limit+offset {
return len(namespaces), namespaces[offset:], nil
} else {
return len(namespaces), namespaces[offset : limit+offset], nil
}
} }
func Namespaces(workspaceName string) ([]*core.Namespace, error) { func Namespaces(workspaceName string) ([]*core.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspaceName})) namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName}))
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -281,161 +104,18 @@ func DeleteNamespace(workspace string, namespaceName string) error { ...@@ -281,161 +104,18 @@ func DeleteNamespace(workspace string, namespaceName string) error {
} }
} }
func Delete(workspace *models.Workspace) error { func RemoveUser(workspaceName string, username string) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, username)
err := release(workspace)
if err != nil { if err != nil {
return err return err
} }
err = DeleteWorkspaceRoleBinding(workspaceName, username, workspaceRole.Labels[constants.DisplayNameLabelKey])
err = iam.DeleteGroup(workspace.Name)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// TODO
func release(workspace *models.Workspace) error {
for _, namespace := range workspace.Namespaces {
err := DeleteNamespace(workspace.Name, namespace)
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
for _, devops := range workspace.DevopsProjects {
err := DeleteDevopsProject("admin", devops)
if err != nil && !strings.Contains(err.Error(), "not found") {
return err
}
}
err := workspaceRoleRelease(workspace.Name)
return err
}
func workspaceRoleRelease(workspace string) error {
k8sClient := k8s.Client()
deletePolicy := meta_v1.DeletePropagationForeground
for _, role := range constants.WorkSpaceRoles {
err := k8sClient.RbacV1().ClusterRoles().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
for _, role := range constants.WorkSpaceRoles {
err := k8sClient.RbacV1().ClusterRoleBindings().Delete(fmt.Sprintf("system:%s:%s", workspace, role), &meta_v1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil && !apierrors.IsNotFound(err) {
return err
}
}
return nil
}
func Edit(workspace *models.Workspace) (*models.Workspace, error) {
group, err := iam.UpdateGroup(&workspace.Group)
if err != nil {
return nil, err
}
workspace.Group = *group
return workspace, nil
}
func DescribeWorkspace(workspaceName string) (*v1alpha1.Workspace, error) {
workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName)
if err != nil {
return nil, err
}
return workspace, nil
}
func fetch(names []string) ([]*models.Workspace, error) {
if names != nil && len(names) == 0 {
return make([]*models.Workspace, 0), nil
}
var groups []models.Group
var err error
if names == nil {
groups, err = iam.ChildList("")
if err != nil {
return nil, err
}
} else {
conn, err := ldap.Client()
if err != nil {
return nil, err
}
defer conn.Close()
for _, name := range names {
group, err := iam.DescribeGroup(name)
if err != nil {
return nil, err
}
groups = append(groups, *group)
}
}
db := mysql.Client()
workspaces := make([]*models.Workspace, 0)
for _, group := range groups {
workspace, err := convertGroupToWorkspace(db, group)
if err != nil {
return nil, err
}
workspaces = append(workspaces, workspace)
}
return workspaces, nil
}
func convertGroupToWorkspace(db *gorm.DB, group models.Group) (*models.Workspace, error) {
namespaces, err := Namespaces(group.Name)
if err != nil {
return nil, err
}
namespacesNames := make([]string, 0)
for _, namespace := range namespaces {
namespacesNames = append(namespacesNames, namespace.Name)
}
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", group.Name).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
}
devOpsProjects := make([]string, 0)
for _, workspaceDOPBinding := range workspaceDOPBindings {
devOpsProjects = append(devOpsProjects, workspaceDOPBinding.DevOpsProject)
}
workspace := models.Workspace{Group: group}
workspace.Namespaces = namespacesNames
workspace.DevopsProjects = devOpsProjects
return &workspace, nil
}
func InviteUser(workspaceName string, user *models.User) error { func InviteUser(workspaceName string, user *models.User) error {
workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username) workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username)
...@@ -463,7 +143,6 @@ func CreateWorkspaceRoleBinding(workspace, username string, role string) error { ...@@ -463,7 +143,6 @@ func CreateWorkspaceRoleBinding(workspace, username string, role string) error {
} }
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
workspaceRoleBinding = workspaceRoleBinding.DeepCopy() workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
if err != nil { if err != nil {
...@@ -487,11 +166,10 @@ func DeleteWorkspaceRoleBinding(workspace, username string, role string) error { ...@@ -487,11 +166,10 @@ func DeleteWorkspaceRoleBinding(workspace, username string, role string) error {
roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-"))
workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName)
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
if err != nil { if err != nil {
return err return err
} }
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
for i, v := range workspaceRoleBinding.Subjects { for i, v := range workspaceRoleBinding.Subjects {
if v.Kind == v1.UserKind && v.Name == username { if v.Kind == v1.UserKind && v.Name == username {
......
...@@ -82,18 +82,6 @@ func ParseReverse(req *restful.Request) bool { ...@@ -82,18 +82,6 @@ func ParseReverse(req *restful.Request) bool {
return b return b
} }
func ParseArray(str string) []string {
arr := make([]string, 0)
for _, item := range strings.Split(str, ",") {
if item = strings.TrimSpace(item); item != "" {
arr = append(arr, item)
}
}
return arr
}
type Conditions struct { type Conditions struct {
Match map[string]string Match map[string]string
Fuzzy map[string]string Fuzzy map[string]string
......
/*
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 kubesphere
import (
"bytes"
"encoding/json"
"fmt"
"github.com/golang/glog"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"net/http"
)
func (c client) DeleteDevopsProject(username string, projectId string) error {
request, _ := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v1alpha/projects/%s", devopsAPIServer, projectId), nil)
if username == "" {
username = constants.AdminUserName
}
request.Header.Add("X-Token-Username", username)
resp, err := c.client.Do(request)
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode > http.StatusOK {
return Error{resp.StatusCode, string(data)}
}
return nil
}
func (c client) GetUserDevopsRole(username string, projectId string) (string, error) {
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1alpha/projects/%s/members", devopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := c.client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > http.StatusOK {
return "", Error{resp.StatusCode, string(data)}
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
func (c client) CreateDevopsProject(username string, project *models.DevopsProject) (*models.DevopsProject, error) {
data, err := json.Marshal(project)
if err != nil {
return nil, err
}
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1alpha/projects", devopsAPIServer), bytes.NewReader(data))
request.Header.Add("X-Token-Username", username)
request.Header.Add("Content-Type", "application/json")
resp, err := c.client.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 > http.StatusOK {
return nil, Error{resp.StatusCode, string(data)}
}
var created models.DevopsProject
err = json.Unmarshal(data, &created)
if err != nil {
return nil, err
}
return &created, nil
}
func (c client) CreateDevopsRoleBinding(projectId string, user string, role string) {
projects := make([]string, 0)
projects = append(projects, projectId)
for _, project := range projects {
data := []byte(fmt.Sprintf(`{"username":"%s","role":"%s"}`, user, role))
request, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/v1alpha/projects/%s/members", devopsAPIServer, project), bytes.NewReader(data))
request.Header.Add("Content-Type", "application/json")
request.Header.Add("X-Token-Username", "admin")
resp, err := c.client.Do(request)
if err != nil || resp.StatusCode > 200 {
glog.Warning(fmt.Sprintf("create devops role binding failed %s,%s,%s", project, user, role))
}
if resp != nil {
resp.Body.Close()
}
}
}
func (c client) ListDevopsProjects(username string) ([]models.DevopsProject, error) {
projects := make([]models.DevopsProject, 0)
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1alpha/projects", devopsAPIServer), nil)
request.Header.Add(constants.UserNameHeader, username)
resp, err := c.client.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 > http.StatusOK {
return nil, Error{resp.StatusCode, string(data)}
}
err = json.Unmarshal(data, &projects)
if err != nil {
return nil, err
}
return projects, nil
}
...@@ -32,6 +32,7 @@ import ( ...@@ -32,6 +32,7 @@ import (
var ( var (
accountAPIServer string accountAPIServer string
devopsAPIServer string
once sync.Once once sync.Once
c client c client
) )
...@@ -41,6 +42,11 @@ type Interface interface { ...@@ -41,6 +42,11 @@ type Interface interface {
UpdateGroup(group *models.Group) (*models.Group, error) UpdateGroup(group *models.Group) (*models.Group, error)
DescribeGroup(name string) (*models.Group, error) DescribeGroup(name string) (*models.Group, error)
DeleteGroup(name string) error DeleteGroup(name string) error
DeleteDevopsProject(username string, projectId string) error
GetUserDevopsRole(username string, projectId string) (string, error)
CreateDevopsProject(username string, project *models.DevopsProject) (*models.DevopsProject, error)
CreateDevopsRoleBinding(projectId string, user string, role string)
ListDevopsProjects(username string) ([]models.DevopsProject, error)
} }
type client struct { type client struct {
...@@ -49,6 +55,7 @@ type client struct { ...@@ -49,6 +55,7 @@ type client struct {
func init() { func init() {
flag.StringVar(&accountAPIServer, "ks-account-api-server", "http://ks-account.kubesphere-system.svc", "kubesphere account api server") flag.StringVar(&accountAPIServer, "ks-account-api-server", "http://ks-account.kubesphere-system.svc", "kubesphere account api server")
flag.StringVar(&devopsAPIServer, "ks-devops-api-server", "http://ks-devops.kubesphere-devops-system.svc", "kubesphere devops api server")
} }
func Client() Interface { func Client() Interface {
......
...@@ -273,11 +273,10 @@ func makeHttpRequest(method, url, data string) ([]byte, error) { ...@@ -273,11 +273,10 @@ func makeHttpRequest(method, url, data string) ([]byte, error) {
return nil, err return nil, err
} }
httpClient := &http.Client{} resp, err := http.DefaultClient.Do(req)
resp, err := httpClient.Do(req)
if err != nil { if err != nil {
err := fmt.Errorf("Request to %s failed, method: %s, reason: %s ", url, method, err) err := fmt.Errorf("Request to %s failed, method: %s,token: %s, reason: %s ", url, method, openpitrixProxyToken, err)
glog.Error(err) glog.Error(err)
return nil, err return nil, err
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册