From 71849f028f8afb6f270f2e8ec07128e2e2e1cfa2 Mon Sep 17 00:00:00 2001 From: hongming Date: Mon, 13 Jan 2020 13:36:21 +0800 Subject: [PATCH] [WIP] API refactor (#1737) * refactor openpitrix API Signed-off-by: hongming * add openpitrix mock client Signed-off-by: hongming * refactor tenant API Signed-off-by: hongming * refactor IAM API Signed-off-by: hongming * refactor IAM API Signed-off-by: hongming --- go.mod | 2 + pkg/api/iam/v1alpha2/types.go | 70 + pkg/api/utils.go | 16 +- pkg/apiserver/apiserver.go | 2 +- pkg/apiserver/iam/am.go | 182 -- pkg/apiserver/iam/auth.go | 124 +- pkg/apiserver/iam/groups.go | 2 +- pkg/apiserver/iam/im.go | 56 +- pkg/apiserver/iam/workspaces.go | 3 +- pkg/apiserver/openpitrix/applications.go | 280 --- pkg/apiserver/openpitrix/apps.go | 548 ----- pkg/apiserver/openpitrix/attachments.go | 54 - pkg/apiserver/openpitrix/categories.go | 173 -- pkg/apiserver/openpitrix/repos.go | 223 -- pkg/apiserver/tenant/tenant.go | 440 ---- pkg/constants/constants.go | 1 + .../namespace/namespace_controller.go | 10 +- pkg/kapis/iam/group.go | 18 - pkg/kapis/iam/install/install.go | 33 - pkg/kapis/iam/v1alpha2/handler.go | 326 +++ pkg/kapis/iam/v1alpha2/register.go | 82 +- pkg/kapis/kapis.go | 1 + pkg/kapis/openpitrix/group.go | 18 - pkg/kapis/openpitrix/install/install.go | 33 - pkg/kapis/openpitrix/v1/handler.go | 911 +++++++- pkg/kapis/openpitrix/v1/register.go | 186 +- pkg/kapis/operations/group.go | 18 - pkg/kapis/operations/install/install.go | 33 - pkg/kapis/operations/v1alpha2/register.go | 8 +- pkg/kapis/resources/v1alpha2/handler.go | 10 +- pkg/kapis/tenant/group.go | 17 - pkg/kapis/tenant/install/install.go | 33 - pkg/kapis/tenant/v1alpha2/handler.go | 399 ++++ pkg/kapis/tenant/v1alpha2/register.go | 71 +- pkg/kapis/terminal/group.go | 18 - pkg/kapis/terminal/install/install.go | 33 - pkg/kapis/terminal/v1alpha2/register.go | 4 +- pkg/models/iam/am.go | 391 +--- pkg/models/iam/im.go | 769 ++----- pkg/models/iam/policy/policy.go | 73 +- pkg/models/iam/types.go | 61 + pkg/models/iam/utils.go | 180 ++ .../{application => }/applications.go | 138 +- pkg/models/openpitrix/applications_test.go | 114 + pkg/models/openpitrix/{app => }/apps.go | 294 +-- .../{attachment => }/attachments.go | 28 +- .../openpitrix/{category => }/categories.go | 82 +- pkg/models/openpitrix/interface.go | 49 + pkg/models/openpitrix/{repo => }/repos.go | 133 +- pkg/models/openpitrix/{types => }/types.go | 29 +- .../openpitrix/{utils/convert.go => utils.go} | 77 +- .../resources/v1alpha2/resource/resources.go | 4 + pkg/models/tenant/devops.go | 15 +- pkg/models/tenant/namespaces.go | 52 +- pkg/models/tenant/tenant.go | 75 +- pkg/models/tenant/workspaces.go | 308 ++- pkg/models/types.go | 41 - pkg/models/workspaces/workspaces.go | 276 --- pkg/server/params/params.go | 26 +- pkg/simple/client/factory.go | 4 +- pkg/simple/client/ldap/channel.go | 2 +- pkg/simple/client/ldap/conn.go | 2 +- pkg/simple/client/ldap/ldap.go | 18 +- pkg/simple/client/openpitrix/mock.go | 2017 +++++++++++++++++ .../{openpitrixclient.go => openpitrix.go} | 75 +- pkg/utils/k8sutil/k8sutil.go | 10 +- 66 files changed, 5415 insertions(+), 4366 deletions(-) create mode 100644 pkg/api/iam/v1alpha2/types.go delete mode 100644 pkg/apiserver/openpitrix/applications.go delete mode 100644 pkg/apiserver/openpitrix/apps.go delete mode 100644 pkg/apiserver/openpitrix/attachments.go delete mode 100644 pkg/apiserver/openpitrix/categories.go delete mode 100644 pkg/apiserver/openpitrix/repos.go delete mode 100644 pkg/apiserver/tenant/tenant.go delete mode 100644 pkg/kapis/iam/group.go delete mode 100644 pkg/kapis/iam/install/install.go delete mode 100644 pkg/kapis/openpitrix/group.go delete mode 100644 pkg/kapis/openpitrix/install/install.go delete mode 100644 pkg/kapis/operations/group.go delete mode 100644 pkg/kapis/operations/install/install.go delete mode 100644 pkg/kapis/tenant/group.go delete mode 100644 pkg/kapis/tenant/install/install.go delete mode 100644 pkg/kapis/terminal/group.go delete mode 100644 pkg/kapis/terminal/install/install.go create mode 100644 pkg/models/iam/types.go create mode 100644 pkg/models/iam/utils.go rename pkg/models/openpitrix/{application => }/applications.go (69%) create mode 100644 pkg/models/openpitrix/applications_test.go rename pkg/models/openpitrix/{app => }/apps.go (61%) rename pkg/models/openpitrix/{attachment => }/attachments.go (65%) rename pkg/models/openpitrix/{category => }/categories.go (59%) create mode 100644 pkg/models/openpitrix/interface.go rename pkg/models/openpitrix/{repo => }/repos.go (64%) rename pkg/models/openpitrix/{types => }/types.go (96%) rename pkg/models/openpitrix/{utils/convert.go => utils.go} (87%) delete mode 100644 pkg/models/workspaces/workspaces.go create mode 100644 pkg/simple/client/openpitrix/mock.go rename pkg/simple/client/openpitrix/{openpitrixclient.go => openpitrix.go} (77%) diff --git a/go.mod b/go.mod index a659c9311..e55e0cf8a 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/go-sql-driver/mysql v1.4.1 github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 github.com/golang/example v0.0.0-20170904185048-46695d81d1fa + github.com/golang/mock v1.2.0 github.com/golang/protobuf v1.3.2 github.com/google/go-cmp v0.3.0 github.com/google/go-querystring v1.0.0 // indirect @@ -79,6 +80,7 @@ require ( github.com/stretchr/testify v1.4.0 github.com/xanzy/ssh-agent v0.2.1 // indirect golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 google.golang.org/grpc v1.23.1 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect diff --git a/pkg/api/iam/v1alpha2/types.go b/pkg/api/iam/v1alpha2/types.go new file mode 100644 index 000000000..7c527a263 --- /dev/null +++ b/pkg/api/iam/v1alpha2/types.go @@ -0,0 +1,70 @@ +/* + * + * Copyright 2020 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 v1alpha2 + +import ( + "fmt" + "kubesphere.io/kubesphere/pkg/models/iam" + "net/mail" +) + +const minPasswordLength = 6 + +type Spec struct { + Token string `json:"token" description:"access token"` +} + +type Status struct { + Authenticated bool `json:"authenticated" description:"is authenticated"` + User map[string]interface{} `json:"user,omitempty" description:"user info"` +} + +type TokenReview struct { + APIVersion string `json:"apiVersion" description:"Kubernetes API version"` + Kind string `json:"kind" description:"kind of the API object"` + Spec *Spec `json:"spec,omitempty"` + Status *Status `json:"status,omitempty" description:"token review status"` +} + +type LoginRequest struct { + Username string `json:"username" description:"username"` + Password string `json:"password" description:"password"` +} + +type UserCreateRequest struct { + *iam.User + ClusterRole string `json:"cluster_role"` +} + +func (request *UserCreateRequest) Validate() error { + if request.Username == "" { + return fmt.Errorf("username must not be empty") + } + + // Parses a single RFC 5322 address, e.g. "Barry Gibbs " + if _, err := mail.ParseAddress(request.Email); err != nil { + return fmt.Errorf("invalid email: %s", request.Email) + } + + if len(request.Password) < minPasswordLength { + return fmt.Errorf("password must be at least %d characters long", minPasswordLength) + } + + return nil +} diff --git a/pkg/api/utils.go b/pkg/api/utils.go index fca266a73..c59cd1a7a 100644 --- a/pkg/api/utils.go +++ b/pkg/api/utils.go @@ -1,16 +1,24 @@ package api import ( - "github.com/emicklei/go-restful" - "net/http" + "github.com/emicklei/go-restful" + "net/http" ) func HandleInternalError(response *restful.Response, err error) { - statusCode := http.StatusInternalServerError + statusCode := http.StatusInternalServerError - response.WriteError(statusCode, err) + response.WriteError(statusCode, err) } func HandleBadRequest(response *restful.Response, err error) { } + +func HandleNotFound(response *restful.Response, err error) { + +} + +func HandleForbidden(response *restful.Response, err error) { + +} diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index b3ab9bf0c..4760ed1c7 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -32,7 +32,7 @@ type Dependencies struct { // Injected Dependencies KubeClient k8s.Client S3 s3.Interface - OpenPitrix openpitrix.Interface + OpenPitrix openpitrix.Client Monitoring monitoring.Interface Logging logging.Interface Devops devops.Interface diff --git a/pkg/apiserver/iam/am.go b/pkg/apiserver/iam/am.go index 21cc996a6..6c25b043f 100644 --- a/pkg/apiserver/iam/am.go +++ b/pkg/apiserver/iam/am.go @@ -16,185 +16,3 @@ */ package iam - -import ( - "github.com/emicklei/go-restful" - "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "net/http" - "sort" - - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/iam/policy" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -type RoleList struct { - ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"` - Roles []*v1.Role `json:"roles" description:"role list"` -} - -func ListRoleUsers(req *restful.Request, resp *restful.Response) { - roleName := req.PathParameter("role") - namespace := req.PathParameter("namespace") - - users, err := iam.RoleUsers(namespace, roleName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(users) -} - -func ListClusterRoles(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListClusterRoles(conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) - -} - -func ListRoles(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListRoles(namespace, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) - -} - -// List users by namespace -func ListNamespaceUsers(req *restful.Request, resp *restful.Response) { - - namespace := req.PathParameter("namespace") - - users, err := iam.NamespaceUsers(namespace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - // sort by time by default - sort.Slice(users, func(i, j int) bool { - return users[i].RoleBindTime.After(*users[j].RoleBindTime) - }) - - resp.WriteAsJson(users) -} - -func ListUserRoles(req *restful.Request, resp *restful.Response) { - - username := req.PathParameter("user") - - roles, err := iam.GetUserRoles("", username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - _, clusterRoles, err := iam.GetUserClusterRoles(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - roleList := RoleList{} - roleList.Roles = roles - roleList.ClusterRoles = clusterRoles - - resp.WriteAsJson(roleList) -} - -func RulesMapping(req *restful.Request, resp *restful.Response) { - rules := policy.RoleRuleMapping - resp.WriteAsJson(rules) -} - -func ClusterRulesMapping(req *restful.Request, resp *restful.Response) { - rules := policy.ClusterRoleRuleMapping - resp.WriteAsJson(rules) -} - -func ListClusterRoleRules(req *restful.Request, resp *restful.Response) { - clusterRoleName := req.PathParameter("clusterrole") - rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName) - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - resp.WriteAsJson(rules) -} - -func ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { - clusterRoleName := req.PathParameter("clusterrole") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset) - - if err != nil { - if k8serr.IsNotFound(err) { - resp.WriteError(http.StatusNotFound, err) - } else { - resp.WriteError(http.StatusInternalServerError, err) - } - return - } - - resp.WriteAsJson(result) -} - -func ListRoleRules(req *restful.Request, resp *restful.Response) { - namespaceName := req.PathParameter("namespace") - roleName := req.PathParameter("role") - - rules, err := iam.GetRoleSimpleRules(namespaceName, roleName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(rules) -} diff --git a/pkg/apiserver/iam/auth.go b/pkg/apiserver/iam/auth.go index 07892b8aa..e098b462f 100644 --- a/pkg/apiserver/iam/auth.go +++ b/pkg/apiserver/iam/auth.go @@ -19,38 +19,15 @@ package iam import ( "fmt" - "github.com/dgrijalva/jwt-go" "github.com/emicklei/go-restful" - "k8s.io/klog" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/utils/iputil" - "kubesphere.io/kubesphere/pkg/utils/jwtutil" "net/http" ) -type Spec struct { - Token string `json:"token" description:"access token"` -} - -type Status struct { - Authenticated bool `json:"authenticated" description:"is authenticated"` - User map[string]interface{} `json:"user,omitempty" description:"user info"` -} - -type TokenReview struct { - APIVersion string `json:"apiVersion" description:"Kubernetes API version"` - Kind string `json:"kind" description:"kind of the API object"` - Spec *Spec `json:"spec,omitempty"` - Status *Status `json:"status,omitempty" description:"token review status"` -} - -type LoginRequest struct { - Username string `json:"username" description:"username"` - Password string `json:"password" description:"password"` -} - type OAuthRequest struct { GrantType string `json:"grant_type"` Username string `json:"username,omitempty" description:"username"` @@ -58,36 +35,6 @@ type OAuthRequest struct { RefreshToken string `json:"refresh_token,omitempty"` } -const ( - KindTokenReview = "TokenReview" -) - -func Login(req *restful.Request, resp *restful.Response) { - var loginRequest LoginRequest - - err := req.ReadEntity(&loginRequest) - - if err != nil || loginRequest.Username == "" || loginRequest.Password == "" { - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password")) - return - } - - ip := iputil.RemoteIp(req.Request) - - token, err := iam.Login(loginRequest.Username, loginRequest.Password, ip) - - if err != nil { - if serviceError, ok := err.(restful.ServiceError); ok { - resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message)) - return - } - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err)) - return - } - - resp.WriteAsJson(token) -} - func OAuth(req *restful.Request, resp *restful.Response) { authRequest := &OAuthRequest{} @@ -120,72 +67,3 @@ func OAuth(req *restful.Request, resp *restful.Response) { resp.WriteEntity(result) } - -// k8s token review -func TokenReviewHandler(req *restful.Request, resp *restful.Response) { - var tokenReview TokenReview - - err := req.ReadEntity(&tokenReview) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if tokenReview.Spec == nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("token must not be null")) - return - } - - uToken := tokenReview.Spec.Token - - token, err := jwtutil.ValidateToken(uToken) - - if err != nil { - klog.Errorln("token review failed", uToken, err) - failed := TokenReview{APIVersion: tokenReview.APIVersion, - Kind: KindTokenReview, - Status: &Status{ - Authenticated: false, - }, - } - resp.WriteAsJson(failed) - return - } - - claims := token.Claims.(jwt.MapClaims) - - username, ok := claims["username"].(string) - - if !ok { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("username not found")) - return - } - - user, err := iam.GetUserInfo(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - groups, err := iam.GetUserGroups(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - user.Groups = groups - - success := TokenReview{APIVersion: tokenReview.APIVersion, - Kind: KindTokenReview, - Status: &Status{ - Authenticated: true, - User: map[string]interface{}{"username": user.Username, "uid": user.Username, "groups": user.Groups}, - }, - } - - resp.WriteAsJson(success) - return -} diff --git a/pkg/apiserver/iam/groups.go b/pkg/apiserver/iam/groups.go index 42b2d3080..e59f0796a 100644 --- a/pkg/apiserver/iam/groups.go +++ b/pkg/apiserver/iam/groups.go @@ -136,7 +136,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) { return } - users := make([]*models.User, 0) + users := make([]*iam.User, 0) modify := false diff --git a/pkg/apiserver/iam/im.go b/pkg/apiserver/iam/im.go index 2957d0e21..9a708bc0c 100644 --- a/pkg/apiserver/iam/im.go +++ b/pkg/apiserver/iam/im.go @@ -29,60 +29,10 @@ import ( "github.com/go-ldap/ldap" rbacv1 "k8s.io/api/rbac/v1" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/server/errors" ) -func CreateUser(req *restful.Request, resp *restful.Response) { - var user models.User - - err := req.ReadEntity(&user) - - if err != nil { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if user.Username == "" { - err = fmt.Errorf("invalid username: %s", user.Username) - klog.Info(err, user.Username) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - // Parses a single RFC 5322 address, e.g. "Barry Gibbs " - if _, err = mail.ParseAddress(user.Email); err != nil { - err = fmt.Errorf("invalid email: %s", user.Email) - klog.Info(err, user.Email) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if len(user.Password) < 6 { - err = fmt.Errorf("invalid password") - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - created, err := iam.CreateUser(&user) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) - return - } - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(created) -} - func DeleteUser(req *restful.Request, resp *restful.Response) { username := req.PathParameter("user") @@ -110,7 +60,7 @@ func UpdateUser(req *restful.Request, resp *restful.Response) { usernameInPath := req.PathParameter("user") usernameInHeader := req.HeaderParameter(constants.UserNameHeader) - var user models.User + var user iam.User err := req.ReadEntity(&user) @@ -254,8 +204,8 @@ func DescribeUser(req *restful.Request, resp *restful.Response) { } result := struct { - *models.User - ClusterRules []models.SimpleRule `json:"cluster_rules"` + *iam.User + ClusterRules []iam.SimpleRule `json:"cluster_rules"` }{ User: user, ClusterRules: clusterRules, diff --git a/pkg/apiserver/iam/workspaces.go b/pkg/apiserver/iam/workspaces.go index 7bbdecb3f..41722275a 100644 --- a/pkg/apiserver/iam/workspaces.go +++ b/pkg/apiserver/iam/workspaces.go @@ -21,7 +21,6 @@ import ( "github.com/emicklei/go-restful" k8serr "k8s.io/apimachinery/pkg/api/errors" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/workspaces" "kubesphere.io/kubesphere/pkg/server/errors" @@ -113,7 +112,7 @@ func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) { func InviteUser(req *restful.Request, resp *restful.Response) { workspace := req.PathParameter("workspace") - var user models.User + var user iam.User err := req.ReadEntity(&user) if err != nil { resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) diff --git a/pkg/apiserver/openpitrix/applications.go b/pkg/apiserver/openpitrix/applications.go deleted file mode 100644 index ec12d5247..000000000 --- a/pkg/apiserver/openpitrix/applications.go +++ /dev/null @@ -1,280 +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 openpitrix - -import ( - "fmt" - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/api/core/v1" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func ListApplications(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)) - orderBy := req.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(req) - - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if namespaceName != "" { - namespace, err := v1alpha2.GetResource("", v1alpha2.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - 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.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) - return - } else { - conditions.Match["runtime_id"] = runtimeId - } - } - - result, err := openpitrix.ListApplications(conditions, limit, offset, orderBy, reverse) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func DescribeApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", v1alpha2.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - 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 != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - resp.WriteEntity(app) - return -} - -func CreateApplication(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - var createClusterRequest types.CreateClusterRequest - err := req.ReadEntity(&createClusterRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - createClusterRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - err = openpitrix.CreateApplication(namespace, createClusterRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteEntity(errors.None) -} - -func ModifyApplication(req *restful.Request, resp *restful.Response) { - var modifyClusterAttributesRequest types.ModifyClusterAttributesRequest - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - err := req.ReadEntity(&modifyClusterAttributesRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", v1alpha2.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - 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 != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - err = openpitrix.PatchApplication(&modifyClusterAttributesRequest) - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", v1alpha2.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - 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 != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - err = openpitrix.DeleteApplication(clusterId) - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} diff --git a/pkg/apiserver/openpitrix/apps.go b/pkg/apiserver/openpitrix/apps.go deleted file mode 100644 index 71023907f..000000000 --- a/pkg/apiserver/openpitrix/apps.go +++ /dev/null @@ -1,548 +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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/openpitrix/app" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" - "strings" -) - -func GetAppVersionPackage(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - versionId := req.PathParameter("version") - - result, err := app.GetAppVersionPackage(appId, versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DoAppAction(req *restful.Request, resp *restful.Response) { - var doActionRequest types.ActionRequest - err := req.ReadEntity(&doActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - appId := req.PathParameter("app") - - err = app.DoAppAction(appId, &doActionRequest) - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DoAppVersionAction(req *restful.Request, resp *restful.Response) { - var doActionRequest types.ActionRequest - err := req.ReadEntity(&doActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - doActionRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - versionId := req.PathParameter("version") - - err = app.DoAppVersionAction(versionId, &doActionRequest) - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func GetAppVersionFiles(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - getAppVersionFilesRequest := &types.GetAppVersionFilesRequest{} - if f := req.QueryParameter("files"); f != "" { - getAppVersionFilesRequest.Files = strings.Split(f, ",") - } - - result, err := app.GetAppVersionFiles(versionId, getAppVersionFilesRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListAppVersionAudits(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - appId := req.PathParameter("app") - versionId := req.PathParameter("version") - if orderBy == "" { - orderBy = "status_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - conditions.Match["app"] = appId - if versionId != "" { - conditions.Match["version"] = versionId - } - - result, err := app.ListAppVersionAudits(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListReviews(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "status_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := app.ListAppVersionReviews(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListAppVersions(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - appId := req.PathParameter("app") - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - conditions.Match["app"] = appId - - result, err := app.ListAppVersions(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if version, ok := item.(*types.AppVersion); ok { - statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": version.AppId, "version_id": version.VersionId}}, 0, 0, "", false) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - version.ClusterTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} - -func ListApps(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := app.ListApps(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if app, ok := item.(*types.App); ok { - status := "active|used|enabled|stopped|pending|creating|upgrading|updating|rollbacking|stopping|starting|recovering|resizing|scaling|deleting" - statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": app.AppId, "status": status}}, 0, 0, "", false) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - app.ClusterTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} - -func ModifyApp(req *restful.Request, resp *restful.Response) { - - var patchAppRequest types.ModifyAppRequest - err := req.ReadEntity(&patchAppRequest) - appId := req.PathParameter("app") - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = app.PatchApp(appId, &patchAppRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeApp(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - - result, err := app.DescribeApp(appId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DeleteApp(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - - err := app.DeleteApp(appId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func CreateApp(req *restful.Request, resp *restful.Response) { - createAppRequest := &types.CreateAppRequest{} - err := req.ReadEntity(createAppRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - createAppRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validatePackageRequest := &types.ValidatePackageRequest{ - VersionPackage: createAppRequest.VersionPackage, - VersionType: createAppRequest.VersionType, - } - result, err = app.ValidatePackage(validatePackageRequest) - } else { - result, err = app.CreateApp(createAppRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func CreateAppVersion(req *restful.Request, resp *restful.Response) { - var createAppVersionRequest types.CreateAppVersionRequest - err := req.ReadEntity(&createAppVersionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - // override app id - createAppVersionRequest.AppId = req.PathParameter("app") - createAppVersionRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validatePackageRequest := &types.ValidatePackageRequest{ - VersionPackage: createAppVersionRequest.Package, - VersionType: createAppVersionRequest.Type, - } - result, err = app.ValidatePackage(validatePackageRequest) - } else { - result, err = app.CreateAppVersion(&createAppVersionRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ModifyAppVersion(req *restful.Request, resp *restful.Response) { - - var patchAppVersionRequest types.ModifyAppVersionRequest - err := req.ReadEntity(&patchAppVersionRequest) - versionId := req.PathParameter("version") - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = app.PatchAppVersion(versionId, &patchAppVersionRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteAppVersion(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - - err := app.DeleteAppVersion(versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeAppVersion(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - - result, err := app.DescribeAppVersion(versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/attachments.go b/pkg/apiserver/openpitrix/attachments.go deleted file mode 100644 index 7dc71e041..000000000 --- a/pkg/apiserver/openpitrix/attachments.go +++ /dev/null @@ -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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "kubesphere.io/kubesphere/pkg/models/openpitrix/attachment" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func DescribeAttachment(req *restful.Request, resp *restful.Response) { - attachmentId := req.PathParameter("attachment") - fileName := req.QueryParameter("filename") - result, err := attachment.DescribeAttachment(attachmentId) - // file raw - if fileName != "" { - data := result.AttachmentContent[fileName] - resp.Write(data) - resp.Header().Set("Content-Type", "text/plain") - return - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/categories.go b/pkg/apiserver/openpitrix/categories.go deleted file mode 100644 index 20b5f5746..000000000 --- a/pkg/apiserver/openpitrix/categories.go +++ /dev/null @@ -1,173 +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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/openpitrix/app" - "kubesphere.io/kubesphere/pkg/models/openpitrix/category" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" -) - -func CreateCategory(req *restful.Request, resp *restful.Response) { - createCategoryRequest := &types.CreateCategoryRequest{} - err := req.ReadEntity(createCategoryRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := category.CreateCategory(createCategoryRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func DeleteCategory(req *restful.Request, resp *restful.Response) { - categoryId := req.PathParameter("category") - - err := category.DeleteCategory(categoryId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} -func ModifyCategory(req *restful.Request, resp *restful.Response) { - var modifyCategoryRequest types.ModifyCategoryRequest - categoryId := req.PathParameter("category") - err := req.ReadEntity(&modifyCategoryRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = category.PatchCategory(categoryId, &modifyCategoryRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} -func DescribeCategory(req *restful.Request, resp *restful.Response) { - categoryId := req.PathParameter("category") - - result, err := category.DescribeCategory(categoryId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func ListCategories(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := category.ListCategories(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if category, ok := item.(*types.Category); ok { - statisticsResult, err := app.ListApps(¶ms.Conditions{Match: map[string]string{"category_id": category.CategoryID, "status": app.StatusActive, "repo": app.BuiltinRepoId}}, "", false, 0, 0) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - category.AppTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/repos.go b/pkg/apiserver/openpitrix/repos.go deleted file mode 100644 index d933fc2a5..000000000 --- a/pkg/apiserver/openpitrix/repos.go +++ /dev/null @@ -1,223 +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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/openpitrix/repo" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" -) - -func CreateRepo(req *restful.Request, resp *restful.Response) { - createRepoRequest := &types.CreateRepoRequest{} - err := req.ReadEntity(createRepoRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validateRepoRequest := &types.ValidateRepoRequest{ - Type: createRepoRequest.Type, - Url: createRepoRequest.URL, - Credential: createRepoRequest.Credential, - } - result, err = repo.ValidateRepo(validateRepoRequest) - } else { - result, err = repo.CreateRepo(createRepoRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DoRepoAction(req *restful.Request, resp *restful.Response) { - repoActionRequest := &types.RepoActionRequest{} - repoId := req.PathParameter("repo") - err := req.ReadEntity(repoActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = repo.DoRepoAction(repoId, repoActionRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteRepo(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - - err := repo.DeleteRepo(repoId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func ModifyRepo(req *restful.Request, resp *restful.Response) { - var updateRepoRequest types.ModifyRepoRequest - repoId := req.PathParameter("repo") - err := req.ReadEntity(&updateRepoRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = repo.PatchRepo(repoId, &updateRepoRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeRepo(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - - result, err := repo.DescribeRepo(repoId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func ListRepos(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := repo.ListRepos(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListRepoEvents(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := repo.ListRepoEvents(repoId, conditions, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/tenant/tenant.go b/pkg/apiserver/tenant/tenant.go deleted file mode 100644 index 759bd62a1..000000000 --- a/pkg/apiserver/tenant/tenant.go +++ /dev/null @@ -1,440 +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 tenant - -import ( - "github.com/emicklei/go-restful" - "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/net" - "k8s.io/klog" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/apiserver/logging" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/metrics" - "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" - "kubesphere.io/kubesphere/pkg/models/tenant" - "kubesphere.io/kubesphere/pkg/models/workspaces" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" - "strings" -) - -func ListWorkspaceRules(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(rules) -} - -func ListWorkspaces(req *restful.Request, resp *restful.Response) { - username := req.HeaderParameter(constants.UserNameHeader) - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if orderBy == "" { - orderBy = v1alpha2.CreateTime - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - 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 { - klog.Errorf("describe workspace failed: %+v", err) - if k8serr.IsNotFound(err) { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - resp.WriteAsJson(result) -} -func ListNamespacesByUsername(req *restful.Request, resp *restful.Response) { - ListNamespaces(req, resp) -} - -func ListNamespaces(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - // /workspaces/{workspace}/members/{username}/namespaces - if username == "" { - // /workspaces/{workspace}/namespaces - username = req.HeaderParameter(constants.UserNameHeader) - } - - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - conditions.Match[constants.WorkspaceLabelKey] = workspace - - result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespaces := make([]*v1.Namespace, 0) - - for _, item := range result.Items { - namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy()) - } - - namespaces = metrics.GetNamespacesWithMetrics(namespaces) - - items := make([]interface{}, 0) - - for _, item := range namespaces { - items = append(items, item) - } - - result.Items = items - - resp.WriteAsJson(result) -} - -func CreateNamespace(req *restful.Request, resp *restful.Response) { - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - var namespace v1.Namespace - err := req.ReadEntity(&namespace) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - workspace, err := tenant.GetWorkspace(workspaceName) - - err = checkResourceQuotas(workspace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - if err != nil { - if k8serr.IsNotFound(err) { - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - created, err := tenant.CreateNamespace(workspaceName, &namespace, username) - - if err != nil { - if k8serr.IsAlreadyExists(err) { - resp.WriteHeaderAndEntity(http.StatusConflict, err) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, err) - } - return - } - resp.WriteAsJson(created) -} - -func DeleteNamespace(req *restful.Request, resp *restful.Response) { - workspaceName := req.PathParameter("workspace") - namespaceName := req.PathParameter("namespace") - - err := workspaces.DeleteNamespace(workspaceName, namespaceName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} - -func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error { - return nil -} - -func ListDevopsProjectsByUsername(req *restful.Request, resp *restful.Response) { - ListDevopsProjects(req, resp) -} - -func ListDevopsProjects(req *restful.Request, resp *restful.Response) { - - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - if username == "" { - username = req.HeaderParameter(constants.UserNameHeader) - } - orderBy := req.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(req) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - result, err := tenant.ListDevopsProjects(workspace, username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(result) -} - -func GetDevOpsProjectsCount(req *restful.Request, resp *restful.Response) { - username := req.HeaderParameter(constants.UserNameHeader) - - result, err := tenant.GetDevOpsProjectsCount(username) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(struct { - Count uint32 `json:"count"` - }{Count: result}) -} -func DeleteDevopsProject(req *restful.Request, resp *restful.Response) { - projectId := req.PathParameter("devops") - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - _, err := tenant.GetWorkspace(workspaceName) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - err = tenant.DeleteDevOpsProject(projectId, username) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(errors.None) -} - -func CreateDevopsProject(req *restful.Request, resp *restful.Response) { - - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - var devops devopsv1alpha2.DevOpsProject - - err := req.ReadEntity(&devops) - - if err != nil { - klog.Infof("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - klog.Infoln("create workspace", username, workspaceName, devops) - project, err := tenant.CreateDevopsProject(username, workspaceName, &devops) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) -} - -func ListNamespaceRules(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := iam.GetUserNamespaceSimpleRules(namespace, username) - - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - - resp.WriteAsJson(rules) -} - -func ListDevopsRules(req *restful.Request, resp *restful.Response) { - - devops := req.PathParameter("devops") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := tenant.GetUserDevopsSimpleRules(username, devops) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(rules) -} - -func LogQuery(req *restful.Request, resp *restful.Response) { - operation := req.QueryParameter("operation") - req, err := regenerateLoggingRequest(req) - switch { - case err != nil: - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - case req != nil: - logging.LoggingQueryCluster(req, resp) - default: - if operation == "export" { - resp.Header().Set(restful.HEADER_ContentType, "text/plain") - resp.Header().Set("Content-Disposition", "attachment") - resp.Write(nil) - } else { - resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)}) - } - } -} - -// override namespace query conditions -func regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) { - - username := req.HeaderParameter(constants.UserNameHeader) - - // regenerate the request for log query - newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster") - values := req.Request.URL.Query() - - clusterRules, err := iam.GetUserClusterRules(username) - if err != nil { - klog.Errorln(err) - return nil, err - } - - hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) - // if the user is not a cluster admin - if !hasClusterLogAccess { - queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",") - // then the user can only view logs of namespaces he belongs to - namespaces := make([]string, 0) - roles, err := iam.GetUserRoles("", username) - if err != nil { - klog.Errorln(err) - return nil, err - } - for _, role := range roles { - if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) { - namespaces = append(namespaces, role.Namespace) - } - } - - // if the user belongs to no namespace - // then no log visible - if len(namespaces) == 0 { - return nil, nil - } else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" { - values.Set("namespaces", strings.Join(namespaces, ",")) - } else { - inter := intersection(queryNamespaces, namespaces) - if len(inter) == 0 { - return nil, nil - } - values.Set("namespaces", strings.Join(inter, ",")) - } - } - - newUrl.RawQuery = values.Encode() - - // forward the request to logging model - newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil) - return restful.NewRequest(newHttpRequest), nil -} - -func intersection(s1, s2 []string) (inter []string) { - hash := make(map[string]bool) - for _, e := range s1 { - hash[e] = true - } - for _, e := range s2 { - // If elements present in the hashmap then append intersection list. - if hash[e] { - inter = append(inter, e) - } - } - //Remove dups from slice. - inter = removeDups(inter) - return -} - -//Remove dups from slice. -func removeDups(elements []string) (nodups []string) { - encountered := make(map[string]bool) - for _, element := range elements { - if !encountered[element] { - nodups = append(nodups, element) - encountered[element] = true - } - } - return -} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 70d5bc1ba..278f58116 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -77,6 +77,7 @@ const ( ComponentMetricsTag = "Component Metrics" LogQueryTag = "Log Query" FluentBitSetting = "Fluent Bit Setting" + TerminalTag = "Terminal" ) var ( diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index e7a7020e8..638f705ed 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -100,7 +100,7 @@ var _ reconcile.Reconciler = &ReconcileNamespace{} // ReconcileNamespace reconciles a Namespace object type ReconcileNamespace struct { client.Client - openpitrixClient *openpitrix.Client + openpitrixClient openpitrix.Client scheme *runtime.Scheme } @@ -361,7 +361,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) adminKubeConfigName := fmt.Sprintf("kubeconfig-%s", constants.AdminUserName) - runtimeCredentials, err := r.openpitrixClient.Runtime().DescribeRuntimeCredentials(openpitrix.SystemContext(), &pb.DescribeRuntimeCredentialsRequest{SearchWord: &wrappers.StringValue{Value: adminKubeConfigName}, Limit: 1}) + runtimeCredentials, err := r.openpitrixClient.DescribeRuntimeCredentials(openpitrix.SystemContext(), &pb.DescribeRuntimeCredentialsRequest{SearchWord: &wrappers.StringValue{Value: adminKubeConfigName}, Limit: 1}) if err != nil { klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) @@ -382,7 +382,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) return err } - resp, err := r.openpitrixClient.Runtime().CreateRuntimeCredential(openpitrix.SystemContext(), &pb.CreateRuntimeCredentialRequest{ + resp, err := r.openpitrixClient.CreateRuntimeCredential(openpitrix.SystemContext(), &pb.CreateRuntimeCredentialRequest{ Name: &wrappers.StringValue{Value: adminKubeConfigName}, Provider: &wrappers.StringValue{Value: "kubernetes"}, Description: &wrappers.StringValue{Value: "kubeconfig"}, @@ -399,7 +399,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) } // TODO runtime id is invalid when recreate runtime - runtimeId, err := r.openpitrixClient.Runtime().CreateRuntime(openpitrix.SystemContext(), &pb.CreateRuntimeRequest{ + runtimeId, err := r.openpitrixClient.CreateRuntime(openpitrix.SystemContext(), &pb.CreateRuntimeRequest{ Name: &wrappers.StringValue{Value: namespace.Name}, RuntimeCredentialId: &wrappers.StringValue{Value: kubesphereRuntimeCredentialId}, Provider: &wrappers.StringValue{Value: openpitrix.KubernetesProvider}, @@ -420,7 +420,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error { if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { - _, err := r.openpitrixClient.Runtime().DeleteRuntimes(openpitrix.SystemContext(), &pb.DeleteRuntimesRequest{RuntimeId: []string{runtimeId}, Force: &wrappers.BoolValue{Value: true}}) + _, err := r.openpitrixClient.DeleteRuntimes(openpitrix.SystemContext(), &pb.DeleteRuntimesRequest{RuntimeId: []string{runtimeId}, Force: &wrappers.BoolValue{Value: true}}) if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) { return nil diff --git a/pkg/kapis/iam/group.go b/pkg/kapis/iam/group.go deleted file mode 100644 index cd47ea564..000000000 --- a/pkg/kapis/iam/group.go +++ /dev/null @@ -1,18 +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 contains iam API versions -package iam diff --git a/pkg/kapis/iam/install/install.go b/pkg/kapis/iam/install/install.go deleted file mode 100644 index 4e4df52af..000000000 --- a/pkg/kapis/iam/install/install.go +++ /dev/null @@ -1,33 +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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/iam/v1alpha2/handler.go b/pkg/kapis/iam/v1alpha2/handler.go index aaaf31cd3..67c83579b 100644 --- a/pkg/kapis/iam/v1alpha2/handler.go +++ b/pkg/kapis/iam/v1alpha2/handler.go @@ -1 +1,327 @@ package v1alpha2 + +import ( + "errors" + "github.com/dgrijalva/jwt-go" + "github.com/emicklei/go-restful" + "github.com/go-ldap/ldap" + rbacv1 "k8s.io/api/rbac/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/iam" + "kubesphere.io/kubesphere/pkg/models/iam/policy" + kserr "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/iputil" + "kubesphere.io/kubesphere/pkg/utils/jwtutil" + "net/http" + "sort" +) + +type iamHandler struct { + amOperator iam.AccessManagementInterface + imOperator iam.IdentityManagementInterface +} + +func newIAMHandler() *iamHandler { + return &iamHandler{} +} + +// k8s token review +func (h *iamHandler) TokenReviewHandler(req *restful.Request, resp *restful.Response) { + var tokenReview iamv1alpha2.TokenReview + + err := req.ReadEntity(&tokenReview) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + if tokenReview.Spec == nil { + api.HandleBadRequest(resp, errors.New("token must not be null")) + return + } + + uToken := tokenReview.Spec.Token + + token, err := jwtutil.ValidateToken(uToken) + + if err != nil { + failed := iamv1alpha2.TokenReview{APIVersion: tokenReview.APIVersion, + Kind: iam.KindTokenReview, + Status: &iamv1alpha2.Status{ + Authenticated: false, + }, + } + resp.WriteEntity(failed) + return + } + + claims := token.Claims.(jwt.MapClaims) + + username, ok := claims["username"].(string) + + if !ok { + api.HandleBadRequest(resp, errors.New("username not found")) + return + } + + user, err := h.imOperator.DescribeUser(username) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + success := iamv1alpha2.TokenReview{APIVersion: tokenReview.APIVersion, + Kind: iam.KindTokenReview, + Status: &iamv1alpha2.Status{ + Authenticated: true, + User: map[string]interface{}{"username": user.Username, "uid": user.Username, "groups": user.Groups}, + }, + } + + resp.WriteEntity(success) +} + +func (h *iamHandler) Login(req *restful.Request, resp *restful.Response) { + var loginRequest iamv1alpha2.LoginRequest + + err := req.ReadEntity(&loginRequest) + + if err != nil || loginRequest.Username == "" || loginRequest.Password == "" { + resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password")) + return + } + + ip := iputil.RemoteIp(req.Request) + + token, err := h.imOperator.Login(loginRequest.Username, loginRequest.Password, ip) + + if err != nil { + if serviceError, ok := err.(restful.ServiceError); ok { + resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message)) + return + } + resp.WriteHeaderAndEntity(http.StatusUnauthorized, err) + return + } + + resp.WriteEntity(token) +} + +func (h *iamHandler) CreateUser(req *restful.Request, resp *restful.Response) { + var createRequest iamv1alpha2.UserCreateRequest + err := req.ReadEntity(&createRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + if err := createRequest.Validate(); err != nil { + api.HandleBadRequest(resp, err) + return + } + + created, err := h.imOperator.CreateUser(createRequest.User) + + if err != nil { + if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { + resp.WriteHeaderAndEntity(http.StatusConflict, kserr.Wrap(err)) + return + } + api.HandleInternalError(resp, err) + return + } + + err := h.amOperator.CreateClusterRoleBinding(created.Username, createRequest.ClusterRole) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + resp.WriteEntity(created) +} + +func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) { + role := req.PathParameter("role") + namespace := req.PathParameter("namespace") + + roleBindings, err := h.amOperator.ListRoleBindings(namespace, role) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + result := make([]*iam.User, 0) + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == rbacv1.UserKind { + user, err := h.imOperator.GetUserInfo(subject.Name) + // skip if user not exist + if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { + continue + } + if err != nil { + api.HandleInternalError(resp, err) + return + } + result = append(result, user) + } + } + } + + resp.WriteEntity(result) +} + +func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := iam.ListClusterRoles(conditions, orderBy, reverse, limit, offset) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteAsJson(result) + +} + +func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := iam.ListRoles(namespace, conditions, orderBy, reverse, limit, offset) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteAsJson(result) + +} + +// List users by namespace +func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) { + + namespace := req.PathParameter("namespace") + + users, err := iam.NamespaceUsers(namespace) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + // sort by time by default + sort.Slice(users, func(i, j int) bool { + return users[i].RoleBindTime.After(*users[j].RoleBindTime) + }) + + resp.WriteAsJson(users) +} + +func (h *iamHandler) ListUserRoles(req *restful.Request, resp *restful.Response) { + + username := req.PathParameter("user") + + roles, err := iam.GetUserRoles("", username) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + _, clusterRoles, err := iam.GetUserClusterRoles(username) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + roleList := RoleList{} + roleList.Roles = roles + roleList.ClusterRoles = clusterRoles + + resp.WriteAsJson(roleList) +} + +func (h *iamHandler) RulesMapping(req *restful.Request, resp *restful.Response) { + rules := policy.RoleRuleMapping + resp.WriteAsJson(rules) +} + +func (h *iamHandler) ClusterRulesMapping(req *restful.Request, resp *restful.Response) { + rules := policy.ClusterRoleRuleMapping + resp.WriteAsJson(rules) +} + +func (h *iamHandler) ListClusterRoleRules(req *restful.Request, resp *restful.Response) { + clusterRoleName := req.PathParameter("clusterrole") + rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName) + if err != nil { + resp.WriteError(http.StatusInternalServerError, err) + return + } + resp.WriteAsJson(rules) +} + +func (h *iamHandler) ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { + clusterRoleName := req.PathParameter("clusterrole") + conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) + orderBy := req.QueryParameter(params.OrderByParam) + limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) + reverse := params.ParseReverse(req) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := iam.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset) + + if err != nil { + if k8serr.IsNotFound(err) { + resp.WriteError(http.StatusNotFound, err) + } else { + resp.WriteError(http.StatusInternalServerError, err) + } + return + } + + resp.WriteAsJson(result) +} + +func (h *iamHandler) ListRoleRules(req *restful.Request, resp *restful.Response) { + namespaceName := req.PathParameter("namespace") + roleName := req.PathParameter("role") + + rules, err := iam.GetRoleSimpleRules(namespaceName, roleName) + + if err != nil { + resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + resp.WriteAsJson(rules) +} diff --git a/pkg/kapis/iam/v1alpha2/register.go b/pkg/kapis/iam/v1alpha2/register.go index 3bbd13af8..4c4c477df 100644 --- a/pkg/kapis/iam/v1alpha2/register.go +++ b/pkg/kapis/iam/v1alpha2/register.go @@ -22,10 +22,13 @@ import ( "github.com/emicklei/go-restful-openapi" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/api/iam/v1alpha2" "kubesphere.io/kubesphere/pkg/apiserver/iam" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" + iam2 "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam/policy" "kubesphere.io/kubesphere/pkg/server/errors" "net/http" @@ -118,174 +121,169 @@ type DescribeWorkspaceUserResponse struct { func addWebService(c *restful.Container) error { ws := runtime.NewWebService(GroupVersion) - ok := "ok" + handler := newIAMHandler() ws.Route(ws.POST("/authenticate"). - To(iam.TokenReviewHandler). + To(handler.TokenReviewHandler). Doc("TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver."). - Reads(iam.TokenReview{}). - Returns(http.StatusOK, ok, iam.TokenReview{}). + Reads(iamv1alpha2.TokenReview{}). + Returns(http.StatusOK, api.StatusOK, iamv1alpha2.TokenReview{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.POST("/login"). - To(iam.Login). + To(handler.Login). Doc("KubeSphere APIs support token-based authentication via the Authtoken request header. The POST Login API is used to retrieve the authentication token. After the authentication token is obtained, it must be inserted into the Authtoken header for all requests."). - Reads(iam.LoginRequest{}). - Returns(http.StatusOK, ok, models.AuthGrantResponse{}). + Reads(iamv1alpha2.LoginRequest{}). + Returns(http.StatusOK, api.StatusOK, models.AuthGrantResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.POST("/token"). - To(iam.OAuth). - Doc("OAuth API,only support resource owner password credentials grant"). - Reads(iam.LoginRequest{}). - Returns(http.StatusOK, ok, models.AuthGrantResponse{}). + ws.Route(ws.POST("/users"). + To(handler.CreateUser). + Doc("Create a user account."). + Reads(CreateUserRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.GET("/users/{user}"). To(iam.DescribeUser). Doc("Describe the specified user."). Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, models.User{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.POST("/users"). - To(iam.CreateUser). - Doc("Create a user account."). - Reads(CreateUserRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, iam2.User{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) + ws.Route(ws.DELETE("/users/{user}"). To(iam.DeleteUser). Doc("Delete the specified user."). Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.PUT("/users/{user}"). To(iam.UpdateUser). Doc("Update information about the specified user."). Param(ws.PathParameter("user", "username")). Reads(UserUpdateRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.GET("/users/{user}/logs"). To(iam.UserLoginLogs). Doc("Retrieve the \"login logs\" for the specified user."). Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, LoginLog{}). + Returns(http.StatusOK, api.StatusOK, LoginLog{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.GET("/users"). To(iam.ListUsers). Doc("List all users."). - Returns(http.StatusOK, ok, UserList{}). + Returns(http.StatusOK, api.StatusOK, UserList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) ws.Route(ws.GET("/users/{user}/roles"). To(iam.ListUserRoles). Doc("Retrieve all the roles that are assigned to the specified user."). Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, iam.RoleList{}). + Returns(http.StatusOK, api.StatusOK, iam.RoleList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/namespaces/{namespace}/roles"). To(iam.ListRoles). Doc("Retrieve the roles that are assigned to the user in the specified namespace."). Param(ws.PathParameter("namespace", "kubernetes namespace")). - Returns(http.StatusOK, ok, RoleList{}). + Returns(http.StatusOK, api.StatusOK, RoleList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/clusterroles"). To(iam.ListClusterRoles). Doc("List all cluster roles."). - Returns(http.StatusOK, ok, ClusterRoleList{}). + Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users"). - To(iam.ListRoleUsers). + To(handler.ListRoleUsers). Doc("Retrieve the users that are bound to the role in the specified namespace."). Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("role", "role name")). - Returns(http.StatusOK, ok, []NamespacedUser{}). + Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/namespaces/{namespace}/users"). To(iam.ListNamespaceUsers). Doc("List all users in the specified namespace."). Param(ws.PathParameter("namespace", "kubernetes namespace")). - Returns(http.StatusOK, ok, []NamespacedUser{}). + Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). To(iam.ListClusterRoleUsers). Doc("List all users that are bound to the specified cluster role."). Param(ws.PathParameter("clusterrole", "cluster role name")). - Returns(http.StatusOK, ok, UserList{}). + Returns(http.StatusOK, api.StatusOK, UserList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/clusterroles/{clusterrole}/rules"). To(iam.ListClusterRoleRules). Doc("List all policy rules of the specified cluster role."). Param(ws.PathParameter("clusterrole", "cluster role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules"). To(iam.ListRoleRules). Doc("List all policy rules of the specified role in the given namespace."). Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("role", "role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules"). To(iam.ListDevopsRoleRules). Doc("List all policy rules of the specified role in the given devops project."). Param(ws.PathParameter("devops", "devops project ID")). Param(ws.PathParameter("role", "devops role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/rulesmapping/clusterroles"). To(iam.ClusterRulesMapping). Doc("Get the mapping relationships between cluster roles and policy rules."). - Returns(http.StatusOK, ok, policy.ClusterRoleRuleMapping). + Returns(http.StatusOK, api.StatusOK, policy.ClusterRoleRuleMapping). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/rulesmapping/roles"). To(iam.RulesMapping). Doc("Get the mapping relationships between namespaced roles and policy rules."). - Returns(http.StatusOK, ok, policy.RoleRuleMapping). + Returns(http.StatusOK, api.StatusOK, policy.RoleRuleMapping). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/workspaces/{workspace}/roles"). To(iam.ListWorkspaceRoles). Doc("List all workspace roles."). Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, ClusterRoleList{}). + Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}"). To(iam.DescribeWorkspaceRole). Doc("Describe the workspace role."). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("role", "workspace role name")). - Returns(http.StatusOK, ok, rbacv1.ClusterRole{}). + Returns(http.StatusOK, api.StatusOK, rbacv1.ClusterRole{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules"). To(iam.ListWorkspaceRoleRules). Doc("List all policy rules of the specified workspace role."). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("role", "workspace role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, []iam2.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/workspaces/{workspace}/members"). To(iam.ListWorkspaceUsers). Doc("List all members in the specified workspace."). Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, UserList{}). + Returns(http.StatusOK, api.StatusOK, UserList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.POST("/workspaces/{workspace}/members"). To(iam.InviteUser). Doc("Invite a member to the specified workspace."). Param(ws.PathParameter("workspace", "workspace name")). Reads(InviteUserRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). To(iam.RemoveUser). Doc("Remove the specified member from the workspace."). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) ws.Route(ws.GET("/workspaces/{workspace}/members/{member}"). To(iam.DescribeWorkspaceUser). Doc("Describe the specified user in the given workspace."). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, ok, DescribeWorkspaceUserResponse{}). + Returns(http.StatusOK, api.StatusOK, DescribeWorkspaceUserResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) c.Add(ws) return nil diff --git a/pkg/kapis/kapis.go b/pkg/kapis/kapis.go index 8a36366cd..aefd76c7f 100644 --- a/pkg/kapis/kapis.go +++ b/pkg/kapis/kapis.go @@ -26,6 +26,7 @@ func InstallAPIs(container *restful.Container, client k8s.Client) { urlruntime.Must(resourcesv1alpha2.AddToContainer(container, client)) urlruntime.Must(tenantv1alpha2.AddToContainer(container)) urlruntime.Must(terminalv1alpha2.AddToContainer(container)) + urlruntime.Must(tenantv1alpha2.AddToContainer(container)) } func InstallAuthorizationAPIs(container *restful.Container) { diff --git a/pkg/kapis/openpitrix/group.go b/pkg/kapis/openpitrix/group.go deleted file mode 100644 index f6b0abd4a..000000000 --- a/pkg/kapis/openpitrix/group.go +++ /dev/null @@ -1,18 +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 operations contains operations API versions -package openpitrix diff --git a/pkg/kapis/openpitrix/install/install.go b/pkg/kapis/openpitrix/install/install.go deleted file mode 100644 index 6193817d8..000000000 --- a/pkg/kapis/openpitrix/install/install.go +++ /dev/null @@ -1,33 +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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1.AddToContainer(container)) -} diff --git a/pkg/kapis/openpitrix/v1/handler.go b/pkg/kapis/openpitrix/v1/handler.go index 4c3d16421..d3675c052 100644 --- a/pkg/kapis/openpitrix/v1/handler.go +++ b/pkg/kapis/openpitrix/v1/handler.go @@ -1,76 +1,925 @@ package v1 import ( + "fmt" "github.com/emicklei/go-restful" - v1 "k8s.io/api/core/v1" - "k8s.io/klog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + k8sinformers "k8s.io/client-go/informers" "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/application" - "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" - "openpitrix.io/openpitrix/pkg/pb" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + op "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "net/http" + "strconv" + "strings" ) type openpitrixHandler struct { - namespacesGetter *resource.NamespacedResourceGetter - applicationOperator application.Interface + openpitrix openpitrix.Interface + informers k8sinformers.SharedInformerFactory } -func newOpenpitrixHandler(factory informers.InformerFactory, client pb.ClusterManagerClient) *openpitrixHandler { +func newOpenpitrixHandler(k8sClient k8s.Client, opClient op.Client) *openpitrixHandler { + + factory := informers.NewInformerFactories(k8sClient.Kubernetes(), k8sClient.KubeSphere(), k8sClient.S2i(), k8sClient.Application()) + return &openpitrixHandler{ - namespacesGetter: resource.New(factory), - applicationOperator: application.NewApplicaitonOperator(factory.KubernetesSharedInformerFactory(), client), + openpitrix: openpitrix.NewOpenpitrixOperator(factory.KubernetesSharedInformerFactory(), opClient), + informers: factory.KubernetesSharedInformerFactory(), } } -func (h *openpitrixHandler) handleListApplications(request *restful.Request, response *restful.Response) { - limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam)) - namespaceName := request.PathParameter("namespace") - conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam)) - orderBy := request.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(request) - - if orderBy == "" { - orderBy = "create_time" - reverse = true - } +func (h *openpitrixHandler) ListApplications(request *restful.Request, response *restful.Response) { + limit, offset := params.ParsePaging(request) + namespace := request.PathParameter("namespace") + orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, true) + conditions, err := params.ParseConditions(request) if err != nil { api.HandleBadRequest(response, err) return } - if namespaceName != "" { - namespace, err := h.namespacesGetter.Get(api.ResourceKindNamespace, "", namespaceName) + // filter namespaced applications by runtime_id + if namespace != "" { + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) if err != nil { api.HandleInternalError(response, err) return } - var runtimeId string - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] if runtimeId == "" { + // runtime id not exist,return empty response response.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) return } else { - conditions.Match["runtime_id"] = runtimeId + // filter by runtime id + conditions.Match[openpitrix.RuntimeId] = runtimeId } } - result, err := h.applicationOperator.List(conditions, limit, offset, orderBy, reverse) + result, err := h.openpitrix.ListApplications(conditions, limit, offset, orderBy, reverse) if err != nil { - klog.Errorln(err) api.HandleInternalError(response, err) return } - resp.WriteAsJson(result) + response.WriteAsJson(result) +} + +func (h *openpitrixHandler) DescribeApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + api.HandleForbidden(resp, err) + return + } + + resp.WriteEntity(app) + return +} + +func (h *openpitrixHandler) CreateApplication(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + var createClusterRequest openpitrix.CreateClusterRequest + err := req.ReadEntity(&createClusterRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + createClusterRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + err = h.openpitrix.CreateApplication(namespace, createClusterRequest) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) ModifyApplication(req *restful.Request, resp *restful.Response) { + var modifyClusterAttributesRequest openpitrix.ModifyClusterAttributesRequest + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + err := req.ReadEntity(&modifyClusterAttributesRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + api.HandleForbidden(resp, err) + return + } + + err = h.openpitrix.ModifyApplication(modifyClusterAttributesRequest) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + api.HandleForbidden(resp, err) + return + } + + err = h.openpitrix.DeleteApplication(clusterId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) GetAppVersionPackage(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + + result, err := h.openpitrix.GetAppVersionPackage(appId, versionId) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DoAppAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + appId := req.PathParameter("app") + + err = h.openpitrix.DoAppAction(appId, &doActionRequest) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DoAppVersionAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + doActionRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + versionId := req.PathParameter("version") + + err = h.openpitrix.DoAppVersionAction(versionId, &doActionRequest) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) GetAppVersionFiles(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + getAppVersionFilesRequest := &openpitrix.GetAppVersionFilesRequest{} + if f := req.QueryParameter("files"); f != "" { + getAppVersionFilesRequest.Files = strings.Split(f, ",") + } + + result, err := h.openpitrix.GetAppVersionFiles(versionId, getAppVersionFilesRequest) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListAppVersionAudits(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + conditions.Match[openpitrix.AppId] = appId + if versionId != "" { + conditions.Match[openpitrix.VersionId] = versionId + } + + result, err := h.openpitrix.ListAppVersionAudits(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListReviews(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.ListAppVersionReviews(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListAppVersions(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + appId := req.PathParameter("app") + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + conditions.Match[openpitrix.AppId] = appId + + result, err := h.openpitrix.ListAppVersions(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + if statistics { + for _, item := range result.Items { + if version, ok := item.(*openpitrix.AppVersion); ok { + statisticsResult, err := h.openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": version.AppId, "version_id": version.VersionId}}, 0, 0, "", false) + if err != nil { + api.HandleInternalError(resp, err) + return + } + version.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListApps(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.ListApps(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + if statistics { + for _, item := range result.Items { + if app, ok := item.(*openpitrix.App); ok { + statuses := "active|used|enabled|stopped|pending|creating|upgrading|updating|rollbacking|stopping|starting|recovering|resizing|scaling|deleting" + statisticsResult, err := h.openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{openpitrix.AppId: app.AppId, openpitrix.Status: statuses}}, 0, 0, "", false) + if err != nil { + api.HandleInternalError(resp, err) + return + } + app.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ModifyApp(req *restful.Request, resp *restful.Response) { + + var patchAppRequest openpitrix.ModifyAppRequest + err := req.ReadEntity(&patchAppRequest) + appId := req.PathParameter("app") + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + err = h.openpitrix.ModifyApp(appId, &patchAppRequest) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + result, err := h.openpitrix.DescribeApp(appId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DeleteApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + err := h.openpitrix.DeleteApp(appId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) CreateApp(req *restful.Request, resp *restful.Response) { + createAppRequest := &openpitrix.CreateAppRequest{} + err := req.ReadEntity(createAppRequest) + if err != nil { + resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + createAppRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppRequest.VersionPackage, + VersionType: createAppRequest.VersionType, + } + result, err = h.openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = h.openpitrix.CreateApp(createAppRequest) + } + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateAppVersion(req *restful.Request, resp *restful.Response) { + var createAppVersionRequest openpitrix.CreateAppVersionRequest + err := req.ReadEntity(&createAppVersionRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + // override app id + createAppVersionRequest.AppId = req.PathParameter("app") + createAppVersionRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppVersionRequest.Package, + VersionType: createAppVersionRequest.Type, + } + result, err = h.openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = h.openpitrix.CreateAppVersion(&createAppVersionRequest) + } + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ModifyAppVersion(req *restful.Request, resp *restful.Response) { + + var patchAppVersionRequest openpitrix.ModifyAppVersionRequest + err := req.ReadEntity(&patchAppVersionRequest) + versionId := req.PathParameter("version") + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + err = h.openpitrix.ModifyAppVersion(versionId, &patchAppVersionRequest) + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + err := h.openpitrix.DeleteAppVersion(versionId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + result, err := h.openpitrix.DescribeAppVersion(versionId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DescribeAttachment(req *restful.Request, resp *restful.Response) { + attachmentId := req.PathParameter("attachment") + fileName := req.QueryParameter("filename") + result, err := h.openpitrix.DescribeAttachment(attachmentId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + // file raw + if fileName != "" { + data := result.AttachmentContent[fileName] + resp.Write(data) + resp.Header().Set("Content-Type", "text/plain") + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateCategory(req *restful.Request, resp *restful.Response) { + createCategoryRequest := &openpitrix.CreateCategoryRequest{} + err := req.ReadEntity(createCategoryRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.CreateCategory(createCategoryRequest) + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) DeleteCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + err := h.openpitrix.DeleteCategory(categoryId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} +func (h *openpitrixHandler) ModifyCategory(req *restful.Request, resp *restful.Response) { + var modifyCategoryRequest openpitrix.ModifyCategoryRequest + categoryId := req.PathParameter("category") + err := req.ReadEntity(&modifyCategoryRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + err = h.openpitrix.ModifyCategory(categoryId, &modifyCategoryRequest) + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} +func (h *openpitrixHandler) DescribeCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + result, err := h.openpitrix.DescribeCategory(categoryId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) ListCategories(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.ListCategories(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + if statistics { + for _, item := range result.Items { + if category, ok := item.(*openpitrix.Category); ok { + statisticsResult, err := h.openpitrix.ListApps(¶ms.Conditions{Match: map[string]string{"category_id": category.CategoryID, "status": openpitrix.StatusActive, "repo": openpitrix.BuiltinRepoId}}, "", false, 0, 0) + if err != nil { + api.HandleInternalError(resp, err) + return + } + category.AppTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateRepo(req *restful.Request, resp *restful.Response) { + createRepoRequest := &openpitrix.CreateRepoRequest{} + err := req.ReadEntity(createRepoRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validateRepoRequest := &openpitrix.ValidateRepoRequest{ + Type: createRepoRequest.Type, + Url: createRepoRequest.URL, + Credential: createRepoRequest.Credential, + } + result, err = h.openpitrix.ValidateRepo(validateRepoRequest) + } else { + result, err = h.openpitrix.CreateRepo(createRepoRequest) + } + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DoRepoAction(req *restful.Request, resp *restful.Response) { + repoActionRequest := &openpitrix.RepoActionRequest{} + repoId := req.PathParameter("repo") + err := req.ReadEntity(repoActionRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + err = h.openpitrix.DoRepoAction(repoId, repoActionRequest) + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + err := h.openpitrix.DeleteRepo(repoId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) ModifyRepo(req *restful.Request, resp *restful.Response) { + var updateRepoRequest openpitrix.ModifyRepoRequest + repoId := req.PathParameter("repo") + err := req.ReadEntity(&updateRepoRequest) + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + err = h.openpitrix.ModifyRepo(repoId, &updateRepoRequest) + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + result, err := h.openpitrix.DescribeRepo(repoId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, err) + return + } + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) ListRepos(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.ListRepos(conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListRepoEvents(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + limit, offset := params.ParsePaging(req) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.openpitrix.ListRepoEvents(repoId, conditions, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteEntity(result) } diff --git a/pkg/kapis/openpitrix/v1/register.go b/pkg/kapis/openpitrix/v1/register.go index 8b113ea7f..8098af267 100644 --- a/pkg/kapis/openpitrix/v1/register.go +++ b/pkg/kapis/openpitrix/v1/register.go @@ -21,35 +21,33 @@ import ( "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/openpitrix" + "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/application" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" + openpitrix2 "kubesphere.io/kubesphere/pkg/models/openpitrix" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + op "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "net/http" ) -const GroupName = "openpitrix.io" +const ( + GroupName = "openpitrix.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) +func AddToContainer(c *restful.Container, k8s k8s.Client, op op.Client) error { -func addWebService(c *restful.Container) error { - - ok := "ok" mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson} webservice := runtime.NewWebService(GroupVersion) + handler := newOpenpitrixHandler(k8s, op) webservice.Route(webservice.GET("/applications"). - To(openpitrix.ListApplications). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListApplications). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("List all applications"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). @@ -63,8 +61,8 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1"))) webservice.Route(webservice.GET("/namespaces/{namespace}/applications"). - To(openpitrix.ListApplications). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListApplications). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("List all applications within the specified namespace"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). @@ -78,72 +76,72 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1"))) webservice.Route(webservice.GET("/namespaces/{namespace}/applications/{application}"). - To(openpitrix.DescribeApplication). - Returns(http.StatusOK, ok, application.Application{}). + To(handler.DescribeApplication). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Application{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Describe the specified application of the namespace"). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "application ID"))) webservice.Route(webservice.POST("/namespaces/{namespace}/applications"). - To(openpitrix.CreateApplication). + To(handler.CreateApplication). Doc("Deploy a new application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Reads(types.CreateClusterRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.CreateClusterRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.PATCH("/namespaces/{namespace}/applications/{application}"). Consumes(mimePatch...). - To(openpitrix.ModifyApplication). + To(handler.ModifyApplication). Doc("Modify application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Reads(types.ModifyClusterAttributesRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyClusterAttributesRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "the id of the application cluster"))) webservice.Route(webservice.DELETE("/namespaces/{namespace}/applications/{application}"). - To(openpitrix.DeleteApplication). + To(handler.DeleteApplication). Doc("Delete the specified application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "the id of the application cluster"))) webservice.Route(webservice.POST("/apps/{app}/versions"). - To(openpitrix.CreateAppVersion). + To(handler.CreateAppVersion). Doc("Create a new app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.CreateAppVersionRequest{}). + Reads(openpitrix2.CreateAppVersionRequest{}). Param(webservice.QueryParameter("validate", "Validate format of package(pack by op tool)")). - Returns(http.StatusOK, ok, types.CreateAppVersionResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateAppVersionResponse{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/apps/{app}/versions/{version}"). - To(openpitrix.DeleteAppVersion). + To(handler.DeleteAppVersion). Doc("Delete the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.PATCH("/apps/{app}/versions/{version}"). Consumes(mimePatch...). - To(openpitrix.ModifyAppVersion). + To(handler.ModifyAppVersion). Doc("Patch the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.ModifyAppVersionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyAppVersionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}"). - To(openpitrix.DescribeAppVersion). + To(handler.DescribeAppVersion). Doc("Describe the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, types.AppVersion{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersion{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions"). - To(openpitrix.ListAppVersions). + To(handler.ListAppVersions). Doc("Get active versions of app, can filter with these fields(version_id, app_id, name, owner, description, package_name, status, type), default return all active app versions"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -155,33 +153,33 @@ func addWebService(c *restful.Container) error { Param(webservice.PathParameter("app", "app template id")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/audits"). - To(openpitrix.ListAppVersionAudits). + To(handler.ListAppVersionAudits). Doc("List audits information of version-specific app template"). - Returns(http.StatusOK, ok, types.AppVersionAudit{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionAudit{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/package"). - To(openpitrix.GetAppVersionPackage). + To(handler.GetAppVersionPackage). Doc("Get packages of version-specific app"). - Returns(http.StatusOK, ok, types.GetAppVersionPackageResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.GetAppVersionPackageResponse{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.POST("/apps/{app}/versions/{version}/action"). - To(openpitrix.DoAppVersionAction). + To(handler.DoAppVersionAction). Doc("Perform submit or other operations on app"). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/files"). - To(openpitrix.GetAppVersionFiles). + To(handler.GetAppVersionFiles). Doc("Get app template package files"). - Returns(http.StatusOK, ok, types.GetAppVersionPackageFilesResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.GetAppVersionPackageFilesResponse{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/reviews"). - To(openpitrix.ListReviews). + To(handler.ListReviews). Doc("Get reviews of version-specific app"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -190,47 +188,47 @@ func addWebService(c *restful.Container) error { Required(false). DataFormat("limit=%d,page=%d"). DefaultValue("limit=10,page=1")). - Returns(http.StatusOK, ok, types.AppVersionReview{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionReview{})) webservice.Route(webservice.GET("/apps/{app}/audits"). - To(openpitrix.ListAppVersionAudits). + To(handler.ListAppVersionAudits). Doc("List audits information of the specific app template"). Param(webservice.PathParameter("app", "app template id")). - Returns(http.StatusOK, ok, types.AppVersionAudit{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionAudit{})) webservice.Route(webservice.POST("/apps"). - To(openpitrix.CreateApp). + To(handler.CreateApp). Doc("Create a new app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, types.CreateAppResponse{}). - Reads(types.CreateAppRequest{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateAppResponse{}). + Reads(openpitrix2.CreateAppRequest{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/apps/{app}"). - To(openpitrix.DeleteApp). + To(handler.DeleteApp). Doc("Delete the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.PATCH("/apps/{app}"). Consumes(mimePatch...). - To(openpitrix.ModifyApp). + To(handler.ModifyApp). Doc("Patch the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.ModifyAppVersionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyAppVersionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}"). - To(openpitrix.DescribeApp). + To(handler.DescribeApp). Doc("Describe the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, types.AppVersion{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersion{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.POST("/apps/{app}/action"). - To(openpitrix.DoAppAction). + To(handler.DoAppAction). Doc("Perform recover or suspend operation on app"). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps"). - To(openpitrix.ListApps). + To(handler.ListApps). Doc("List app templates"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -241,36 +239,36 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.POST("/categories"). - To(openpitrix.CreateCategory). + To(handler.CreateCategory). Doc("Create app template category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.CreateCategoryRequest{}). - Returns(http.StatusOK, ok, types.CreateCategoryResponse{}). + Reads(openpitrix2.CreateCategoryRequest{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateCategoryResponse{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/categories/{category}"). - To(openpitrix.DeleteCategory). + To(handler.DeleteCategory). Doc("Delete the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.PATCH("/categories/{category}"). Consumes(mimePatch...). - To(openpitrix.ModifyCategory). + To(handler.ModifyCategory). Doc("Patch the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.ModifyCategoryRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyCategoryRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.GET("/categories/{category}"). - To(openpitrix.DescribeCategory). + To(handler.DescribeCategory). Doc("Describe the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, types.Category{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Category{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.GET("/categories"). - To(openpitrix.ListCategories). + To(handler.ListCategories). Doc("List categories"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -281,43 +279,43 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.GET("/attachments/{attachment}"). - To(openpitrix.DescribeAttachment). + To(handler.DescribeAttachment). Doc("Get attachment by attachment id"). Param(webservice.PathParameter("attachment", "attachment id")). - Returns(http.StatusOK, ok, types.Attachment{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.Attachment{})) webservice.Route(webservice.POST("/repos"). - To(openpitrix.CreateRepo). + To(handler.CreateRepo). Doc("Create repository, repository used to store package of app"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). Param(webservice.QueryParameter("validate", "Validate repository")). - Returns(http.StatusOK, ok, types.CreateRepoResponse{}). - Reads(types.CreateRepoRequest{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateRepoResponse{}). + Reads(openpitrix2.CreateRepoRequest{})) webservice.Route(webservice.DELETE("/repos/{repo}"). - To(openpitrix.DeleteRepo). + To(handler.DeleteRepo). Doc("Delete the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.PATCH("/repos/{repo}"). Consumes(mimePatch...). - To(openpitrix.ModifyRepo). + To(handler.ModifyRepo). Doc("Patch the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(types.ModifyRepoRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyRepoRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos/{repo}"). - To(openpitrix.DescribeRepo). + To(handler.DescribeRepo). Doc("Describe the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, types.Repo{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Repo{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos"). - To(openpitrix.ListRepos). + To(handler.ListRepos). Doc("List repositories"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -328,17 +326,17 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.POST("/repos/{repo}/action"). - To(openpitrix.DoRepoAction). + To(handler.DoRepoAction). Doc("Start index repository event"). - Reads(types.RepoActionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.RepoActionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos/{repo}/events"). - To(openpitrix.ListRepoEvents). + To(handler.ListRepoEvents). Doc("Get repository events"). - Returns(http.StatusOK, ok, models.PageableResponse{}). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Param(webservice.PathParameter("repo", "repo id"))) c.Add(webservice) diff --git a/pkg/kapis/operations/group.go b/pkg/kapis/operations/group.go deleted file mode 100644 index 12ee8cbf8..000000000 --- a/pkg/kapis/operations/group.go +++ /dev/null @@ -1,18 +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 operations contains operations API versions -package operations diff --git a/pkg/kapis/operations/install/install.go b/pkg/kapis/operations/install/install.go deleted file mode 100644 index c2091aa5b..000000000 --- a/pkg/kapis/operations/install/install.go +++ /dev/null @@ -1,33 +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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container, nil)) -} diff --git a/pkg/kapis/operations/v1alpha2/register.go b/pkg/kapis/operations/v1alpha2/register.go index fdcbc4b4b..0494eba03 100644 --- a/pkg/kapis/operations/v1alpha2/register.go +++ b/pkg/kapis/operations/v1alpha2/register.go @@ -20,19 +20,21 @@ package v1alpha2 import ( "github.com/emicklei/go-restful" "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "net/http" ) -const GroupName = "operations.kubesphere.io" +const ( + GroupName = "operations.kubesphere.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} func AddToContainer(c *restful.Container, client k8s.Client) error { - ok := "ok" webservice := runtime.NewWebService(GroupVersion) handler := newOperationHandler(client) @@ -45,7 +47,7 @@ func AddToContainer(c *restful.Container, client k8s.Client) error { Param(webservice.PathParameter("namespace", "the name of the namespace where the job runs in")). Param(webservice.QueryParameter("action", "action must be \"rerun\"")). Param(webservice.QueryParameter("resourceVersion", "version of job, rerun when the version matches").Required(true)). - Returns(http.StatusOK, ok, errors.Error{})) + Returns(http.StatusOK, api.StatusOK, errors.Error{})) c.Add(webservice) diff --git a/pkg/kapis/resources/v1alpha2/handler.go b/pkg/kapis/resources/v1alpha2/handler.go index c8ec18080..29be473fa 100644 --- a/pkg/kapis/resources/v1alpha2/handler.go +++ b/pkg/kapis/resources/v1alpha2/handler.go @@ -54,18 +54,18 @@ func (r *resourceHandler) handleGetNamespacedResources(request *restful.Request, func (r *resourceHandler) handleListNamespaceResources(request *restful.Request, response *restful.Response) { namespace := request.PathParameter("namespace") - resourceName := request.PathParameter("resources") - conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam)) + resource := request.PathParameter("resources") orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, v1alpha2.CreateTime) - limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(request) + limit, offset := params.ParsePaging(request) + reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, false) + conditions, err := params.ParseConditions(request) if err != nil { response.WriteHeaderAndEntity(http.StatusBadRequest, err) return } - result, err := r.resourcesGetter.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset) + result, err := r.resourcesGetter.ListResources(namespace, resource, conditions, orderBy, reverse, limit, offset) if err != nil { api.HandleInternalError(response, err) diff --git a/pkg/kapis/tenant/group.go b/pkg/kapis/tenant/group.go deleted file mode 100644 index 2f29dcfcc..000000000 --- a/pkg/kapis/tenant/group.go +++ /dev/null @@ -1,17 +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 tenant diff --git a/pkg/kapis/tenant/install/install.go b/pkg/kapis/tenant/install/install.go deleted file mode 100644 index d164f7ca2..000000000 --- a/pkg/kapis/tenant/install/install.go +++ /dev/null @@ -1,33 +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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(tenantv1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go index aaaf31cd3..e068fb146 100644 --- a/pkg/kapis/tenant/v1alpha2/handler.go +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -1 +1,400 @@ package v1alpha2 + +import ( + "github.com/emicklei/go-restful" + v1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/net" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" + loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/logging" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/iam" + "kubesphere.io/kubesphere/pkg/models/metrics" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/tenant" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "net/http" + "strings" +) + +type tenantHandler struct { + tenant tenant.Interface +} + +func newTenantHandler() *tenantHandler { + return &tenantHandler{} +} + +func (h *tenantHandler) ListWorkspaceRules(req *restful.Request, resp *restful.Response) { + workspace := req.PathParameter("workspace") + username := req.HeaderParameter(constants.UserNameHeader) + + rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(rules) +} + +func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) { + username := req.HeaderParameter(constants.UserNameHeader) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime) + limit, offset := params.ParsePaging(req) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := h.tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(result) +} + +func (h *tenantHandler) DescribeWorkspace(req *restful.Request, resp *restful.Response) { + username := req.HeaderParameter(constants.UserNameHeader) + workspaceName := req.PathParameter("workspace") + + result, err := h.tenant.DescribeWorkspace(username, workspaceName) + + if err != nil { + if k8serr.IsNotFound(err) { + api.HandleNotFound(resp, err) + } else { + api.HandleInternalError(resp, err) + } + return + } + + resp.WriteAsJson(result) +} + +func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) { + workspace := req.PathParameter("workspace") + username := req.PathParameter("member") + // /workspaces/{workspace}/members/{username}/namespaces + if username == "" { + // /workspaces/{workspace}/namespaces + username = req.HeaderParameter(constants.UserNameHeader) + } + + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime) + limit, offset := params.ParsePaging(req) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + conditions.Match[constants.WorkspaceLabelKey] = workspace + + result, err := h.tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + namespaces := make([]*v1.Namespace, 0) + + for _, item := range result.Items { + namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy()) + } + + namespaces = metrics.GetNamespacesWithMetrics(namespaces) + + items := make([]interface{}, 0) + + for _, item := range namespaces { + items = append(items, item) + } + + result.Items = items + + resp.WriteAsJson(result) +} + +func (h *tenantHandler) CreateNamespace(req *restful.Request, resp *restful.Response) { + workspace := req.PathParameter("workspace") + username := req.HeaderParameter(constants.UserNameHeader) + var namespace v1.Namespace + err := req.ReadEntity(&namespace) + if err != nil { + api.HandleNotFound(resp, err) + return + } + + _, err = h.tenant.DescribeWorkspace("", workspace) + + if err != nil { + if k8serr.IsNotFound(err) { + api.HandleForbidden(resp, err) + } else { + api.HandleInternalError(resp, err) + } + return + } + + created, err := h.tenant.CreateNamespace(workspace, &namespace, username) + + if err != nil { + if k8serr.IsAlreadyExists(err) { + resp.WriteHeaderAndEntity(http.StatusConflict, err) + } else { + api.HandleInternalError(resp, err) + } + return + } + resp.WriteAsJson(created) +} + +func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Response) { + workspace := req.PathParameter("workspace") + namespace := req.PathParameter("namespace") + + err := h.tenant.DeleteNamespace(workspace, namespace) + + if err != nil { + if k8serr.IsNotFound(err) { + api.HandleNotFound(resp, err) + } else { + api.HandleInternalError(resp, err) + } + return + } + + resp.WriteAsJson(errors.None) +} + +func (h *tenantHandler) ListDevopsProjects(req *restful.Request, resp *restful.Response) { + + workspace := req.PathParameter("workspace") + username := req.PathParameter("member") + if username == "" { + username = req.HeaderParameter(constants.UserNameHeader) + } + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime) + limit, offset := params.ParsePaging(req) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true) + conditions, err := params.ParseConditions(req) + + if err != nil { + api.HandleBadRequest(resp, err) + return + } + + result, err := tenant.ListDevopsProjects(workspace, username, conditions, orderBy, reverse, limit, offset) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(result) +} + +func (h *tenantHandler) GetDevOpsProjectsCount(req *restful.Request, resp *restful.Response) { + username := req.HeaderParameter(constants.UserNameHeader) + + result, err := tenant.GetDevOpsProjectsCount(username) + if err != nil { + api.HandleInternalError(resp, err) + return + } + resp.WriteAsJson(struct { + Count uint32 `json:"count"` + }{Count: result}) +} +func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.Response) { + projectId := req.PathParameter("devops") + workspace := req.PathParameter("workspace") + username := req.HeaderParameter(constants.UserNameHeader) + + _, err := h.tenant.DescribeWorkspace("", workspace) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + err = tenant.DeleteDevOpsProject(projectId, username) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(errors.None) +} + +func (h *tenantHandler) CreateDevopsProject(req *restful.Request, resp *restful.Response) { + + workspaceName := req.PathParameter("workspace") + username := req.HeaderParameter(constants.UserNameHeader) + + var devops devopsv1alpha2.DevOpsProject + + err := req.ReadEntity(&devops) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + project, err := tenant.CreateDevopsProject(username, workspaceName, &devops) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(project) +} + +func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + username := req.HeaderParameter(constants.UserNameHeader) + + rules, err := iam.GetUserNamespaceSimpleRules(namespace, username) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(rules) +} + +func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) { + + devops := req.PathParameter("devops") + username := req.HeaderParameter(constants.UserNameHeader) + + rules, err := tenant.GetUserDevopsSimpleRules(username, devops) + + if err != nil { + api.HandleInternalError(resp, err) + return + } + + resp.WriteAsJson(rules) +} + +func (h *tenantHandler) LogQuery(req *restful.Request, resp *restful.Response) { + operation := req.QueryParameter("operation") + req, err := h.regenerateLoggingRequest(req) + switch { + case err != nil: + api.HandleInternalError(resp, err) + case req != nil: + logging.LoggingQueryCluster(req, resp) + default: + if operation == "export" { + resp.Header().Set(restful.HEADER_ContentType, "text/plain") + resp.Header().Set("Content-Disposition", "attachment") + resp.Write(nil) + } else { + resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)}) + } + } +} + +// override namespace query conditions +func (h *tenantHandler) regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) { + + username := req.HeaderParameter(constants.UserNameHeader) + + // regenerate the request for log query + newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster") + values := req.Request.URL.Query() + + clusterRules, err := iam.GetUserClusterRules(username) + if err != nil { + klog.Errorln(err) + return nil, err + } + + hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) + // if the user is not a cluster admin + if !hasClusterLogAccess { + queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",") + // then the user can only view logs of namespaces he belongs to + namespaces := make([]string, 0) + roles, err := iam.GetUserRoles("", username) + if err != nil { + klog.Errorln(err) + return nil, err + } + for _, role := range roles { + if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) { + namespaces = append(namespaces, role.Namespace) + } + } + + // if the user belongs to no namespace + // then no log visible + if len(namespaces) == 0 { + return nil, nil + } else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" { + values.Set("namespaces", strings.Join(namespaces, ",")) + } else { + inter := intersection(queryNamespaces, namespaces) + if len(inter) == 0 { + return nil, nil + } + values.Set("namespaces", strings.Join(inter, ",")) + } + } + + newUrl.RawQuery = values.Encode() + + // forward the request to logging model + newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil) + return restful.NewRequest(newHttpRequest), nil +} + +func intersection(s1, s2 []string) (inter []string) { + hash := make(map[string]bool) + for _, e := range s1 { + hash[e] = true + } + for _, e := range s2 { + // If elements present in the hashmap then append intersection list. + if hash[e] { + inter = append(inter, e) + } + } + //Remove dups from slice. + inter = removeDups(inter) + return +} + +//Remove dups from slice. +func removeDups(elements []string) (nodups []string) { + encountered := make(map[string]bool) + for _, element := range elements { + if !encountered[element] { + nodups = append(nodups, element) + encountered[element] = true + } + } + return +} diff --git a/pkg/kapis/tenant/v1alpha2/register.go b/pkg/kapis/tenant/v1alpha2/register.go index f4a916e17..bca9d087c 100644 --- a/pkg/kapis/tenant/v1alpha2/register.go +++ b/pkg/kapis/tenant/v1alpha2/register.go @@ -22,13 +22,14 @@ import ( "github.com/emicklei/go-restful-openapi" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/api" devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/tenant" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" @@ -37,78 +38,72 @@ import ( const ( GroupName = "tenant.kubesphere.io" - RespOK = "ok" ) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { - ok := "ok" +func AddToContainer(c *restful.Container) error { ws := runtime.NewWebService(GroupVersion) + handler := newTenantHandler() ws.Route(ws.GET("/workspaces"). - To(tenant.ListWorkspaces). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListWorkspaces). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Doc("List all workspaces that belongs to the current user"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}"). - To(tenant.DescribeWorkspace). + To(handler.DescribeWorkspace). Doc("Describe the specified workspace"). Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, v1alpha1.Workspace{}). + Returns(http.StatusOK, api.StatusOK, v1alpha1.Workspace{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/rules"). - To(tenant.ListWorkspaceRules). + To(handler.ListWorkspaceRules). Param(ws.PathParameter("workspace", "workspace name")). Doc("List the rules of the specified workspace for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, iam.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/namespaces/{namespace}/rules"). - To(tenant.ListNamespaceRules). + To(handler.ListNamespaceRules). Param(ws.PathParameter("namespace", "the name of the namespace")). Doc("List the rules of the specified namespace for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, iam.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/devops/{devops}/rules"). - To(tenant.ListDevopsRules). + To(handler.ListDevopsRules). Param(ws.PathParameter("devops", "devops project ID")). Doc("List the rules of the specified DevOps project for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). + Returns(http.StatusOK, api.StatusOK, iam.SimpleRule{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/namespaces"). - To(tenant.ListNamespaces). + To(handler.ListNamespaces). Param(ws.PathParameter("workspace", "workspace name")). Doc("List the namespaces of the specified workspace for the current user"). - Returns(http.StatusOK, ok, []v1.Namespace{}). + Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/namespaces"). - To(tenant.ListNamespacesByUsername). + To(handler.ListNamespaces). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("member", "workspace member's username")). Doc("List the namespaces for the workspace member"). - Returns(http.StatusOK, ok, []v1.Namespace{}). + Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.POST("/workspaces/{workspace}/namespaces"). - To(tenant.CreateNamespace). + To(handler.CreateNamespace). Param(ws.PathParameter("workspace", "workspace name")). Doc("Create a namespace in the specified workspace"). - Returns(http.StatusOK, ok, []v1.Namespace{}). + Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}"). - To(tenant.DeleteNamespace). + To(handler.DeleteNamespace). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("namespace", "the name of the namespace")). Doc("Delete the specified namespace from the workspace"). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/devops"). - To(tenant.ListDevopsProjects). + To(handler.ListDevopsProjects). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.QueryParameter(params.PagingParam, "page"). Required(false). @@ -120,7 +115,7 @@ func addWebService(c *restful.Container) error { Doc("List devops projects for the current user"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). - To(tenant.ListDevopsProjectsByUsername). + To(handler.ListDevopsProjects). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("member", "workspace member's username")). Param(ws.QueryParameter(params.PagingParam, "page"). @@ -130,32 +125,32 @@ func addWebService(c *restful.Container) error { Param(ws.QueryParameter(params.ConditionsParam, "query conditions"). Required(false). DataFormat("key=%s,key~%s")). - Returns(http.StatusOK, ok, models.PageableResponse{}). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Doc("List the devops projects for the workspace member"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/devopscount"). - To(tenant.GetDevOpsProjectsCount). - Returns(http.StatusOK, ok, struct { + To(handler.GetDevOpsProjectsCount). + Returns(http.StatusOK, api.StatusOK, struct { Count uint32 `json:"count"` }{}). Doc("Get the devops projects count for the member"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.POST("/workspaces/{workspace}/devops"). - To(tenant.CreateDevopsProject). + To(handler.CreateDevopsProject). Param(ws.PathParameter("workspace", "workspace name")). Doc("Create a devops project in the specified workspace"). Reads(devopsv1alpha2.DevOpsProject{}). - Returns(http.StatusOK, RespOK, devopsv1alpha2.DevOpsProject{}). + Returns(http.StatusOK, api.StatusOK, devopsv1alpha2.DevOpsProject{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{devops}"). - To(tenant.DeleteDevopsProject). + To(handler.DeleteDevopsProject). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("devops", "devops project ID")). Doc("Delete the specified devops project from the workspace"). - Returns(http.StatusOK, RespOK, devopsv1alpha2.DevOpsProject{}). + Returns(http.StatusOK, api.StatusOK, devopsv1alpha2.DevOpsProject{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/logs"). - To(tenant.LogQuery). + To(handler.LogQuery). Doc("Query cluster-level logs in a multi-tenants environment"). Param(ws.QueryParameter("operation", "Operation type. This can be one of four types: query (for querying logs), statistics (for retrieving statistical data), histogram (for displaying log count by time interval) and export (for exporting logs). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). Param(ws.QueryParameter("workspaces", "A comma-separated list of workspaces. This field restricts the query to specified workspaces. For example, the following filter matches the workspace my-ws and demo-ws: `my-ws,demo-ws`").DataType("string").Required(false)). @@ -177,7 +172,7 @@ func addWebService(c *restful.Container) error { Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}). Writes(v1alpha2.Response{}). - Returns(http.StatusOK, RespOK, v1alpha2.Response{})). + Returns(http.StatusOK, api.StatusOK, v1alpha2.Response{})). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON, "text/plain") diff --git a/pkg/kapis/terminal/group.go b/pkg/kapis/terminal/group.go deleted file mode 100644 index ced53f44b..000000000 --- a/pkg/kapis/terminal/group.go +++ /dev/null @@ -1,18 +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 terminal contains terminal API versions -package terminal diff --git a/pkg/kapis/terminal/install/install.go b/pkg/kapis/terminal/install/install.go deleted file mode 100644 index 6e172a0f0..000000000 --- a/pkg/kapis/terminal/install/install.go +++ /dev/null @@ -1,33 +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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(c *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(c, nil, nil)) -} diff --git a/pkg/kapis/terminal/v1alpha2/register.go b/pkg/kapis/terminal/v1alpha2/register.go index c5bc3278e..7997ccfae 100644 --- a/pkg/kapis/terminal/v1alpha2/register.go +++ b/pkg/kapis/terminal/v1alpha2/register.go @@ -24,12 +24,12 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" ) const ( GroupName = "terminal.kubesphere.io" - tag = "Terminal" ) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} @@ -43,7 +43,7 @@ func AddToContainer(c *restful.Container, client kubernetes.Interface, config *r webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pod}"). To(handler.handleTerminalSession). Doc("create terminal session"). - Metadata(restfulspec.KeyOpenAPITags, []string{tag}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.TerminalTag}). Writes(models.PodInfo{})) c.Add(webservice) diff --git a/pkg/models/iam/am.go b/pkg/models/iam/am.go index 8867e3415..1ce4e0035 100644 --- a/pkg/models/iam/am.go +++ b/pkg/models/iam/am.go @@ -25,14 +25,16 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/apiserver/resources" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/iam/policy" "kubesphere.io/kubesphere/pkg/models/kubectl" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/clusterrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/role" "kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/utils/k8sutil" @@ -47,12 +49,30 @@ const ( NamespaceViewerRoleBindName = "viewer" ) -func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { - var rules []models.SimpleRule +type AccessManagementInterface interface { + GetDevopsRoleSimpleRules(role string) []SimpleRule + ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error) + CreateClusterRoleBinding(username string, clusterRole string) error +} + +type amOperator struct { + informers informers.SharedInformerFactory + resources resource.ResourceGetter +} + +func newAMOperator(informers informers.SharedInformerFactory) AccessManagementInterface { + resourceGetter := resource.ResourceGetter{} + resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers)) + resourceGetter.Add(v1alpha2.ClusterRoles, clusterrole.NewClusterRoleSearcher(informers)) + return &amOperator{informers: informers, resources: resourceGetter} +} + +func (am *amOperator) GetDevopsRoleSimpleRules(role string) []SimpleRule { + var rules []SimpleRule switch role { case "developer": - rules = []models.SimpleRule{ + rules = []SimpleRule{ {Name: "pipelines", Actions: []string{"view", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, @@ -60,7 +80,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { } break case "owner": - rules = []models.SimpleRule{ + rules = []SimpleRule{ {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"create", "edit", "view", "delete"}}, @@ -69,7 +89,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { } break case "maintainer": - rules = []models.SimpleRule{ + rules = []SimpleRule{ {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, @@ -80,7 +100,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { case "reporter": fallthrough default: - rules = []models.SimpleRule{ + rules = []SimpleRule{ {Name: "pipelines", Actions: []string{"view"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, @@ -92,14 +112,14 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { } // Get user roles in namespace -func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() - roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister() +func (am *amOperator) GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { + clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister() + roleBindingLister := am.informers.Rbac().V1().RoleBindings().Lister() + roleLister := am.informers.Rbac().V1().Roles().Lister() roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything()) if err != nil { - klog.Errorln("get role bindings", namespace, err) + klog.Errorln(err) return nil, err } @@ -114,7 +134,7 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { klog.Warningf("cluster role %s not found but bind user %s in namespace %s", roleBinding.RoleRef.Name, username, namespace) continue } else { - klog.Errorln("get cluster role", roleBinding.RoleRef.Name, err) + klog.Errorln(err) return nil, err } } @@ -132,7 +152,7 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { klog.Warningf("namespace %s role %s not found, but bind user %s", namespace, roleBinding.RoleRef.Name, username) continue } else { - klog.Errorln("get role", roleBinding.Namespace, roleBinding.RoleRef.Name, err) + klog.Errorln(err) return nil, err } } @@ -144,13 +164,13 @@ func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { return roles, nil } -func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.ClusterRole, error) { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() +func (am *amOperator) GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.ClusterRole, error) { + clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister() + clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister() clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything()) if err != nil { - klog.Errorln("get cluster role bindings", err) + klog.Errorln(err) return nil, nil, err } @@ -164,7 +184,7 @@ func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.Cluste klog.Warningf("cluster role %s not found but bind user %s", clusterRoleBinding.RoleRef.Name, username) continue } else { - klog.Errorln("get cluster role", clusterRoleBinding.RoleRef.Name, err) + klog.Errorln(err) return nil, nil, err } } @@ -178,16 +198,16 @@ func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.Cluste return userFacingClusterRole, clusterRoles, nil } -func GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) { - userFacingClusterRole, _, err := GetUserClusterRoles(username) +func (am *amOperator) GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) { + userFacingClusterRole, _, err := am.GetUserClusterRoles(username) if err != nil { return nil, err } return userFacingClusterRole, nil } -func GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) { - _, clusterRoles, err := GetUserClusterRoles(username) +func (am *amOperator) GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) { + _, clusterRoles, err := am.GetUserClusterRoles(username) if err != nil { return nil, err @@ -201,8 +221,8 @@ func GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) { return rules, nil } -func GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) { - roles, err := GetUserRoles(namespace, username) +func (am *amOperator) GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) { + roles, err := am.GetUserRoles(namespace, username) if err != nil { return nil, err @@ -216,9 +236,9 @@ func GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) { return rules, nil } -func GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, error) { +func (am *amOperator) GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, error) { - clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) if err != nil { klog.Errorln("get cluster role bindings", err) @@ -236,17 +256,17 @@ func GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, e return result, nil } -func GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) { +func (am *amOperator) GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) { if !sliceutil.HasString(constants.WorkSpaceRoles, role) { return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role) } role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) - return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(role) + return am.informers.Rbac().V1().ClusterRoles().Lister().Get(role) } -func GetUserWorkspaceRoleMap(username string) (map[string]string, error) { +func (am *amOperator) GetUserWorkspaceRoleMap(username string) (map[string]string, error) { - clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + clusterRoleBindings, err := am.informers.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) if err != nil { klog.Errorln("get cluster role bindings", err) @@ -265,26 +285,26 @@ func GetUserWorkspaceRoleMap(username string) (map[string]string, error) { return result, nil } -func GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) { - workspaceRoleMap, err := GetUserWorkspaceRoleMap(username) +func (am *amOperator) GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) { + workspaceRoleMap, err := am.GetUserWorkspaceRoleMap(username) if err != nil { return nil, err } if workspaceRole := workspaceRoleMap[workspace]; workspaceRole != "" { - return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(workspaceRole) + return am.informers.Rbac().V1().ClusterRoles().Lister().Get(workspaceRole) } return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace user"}, username) } -func GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, error) { - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() +func (am *amOperator) GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, error) { + roleBindingLister := am.informers.Rbac().V1().RoleBindings().Lister() roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything()) if err != nil { - klog.Errorln("get role bindings", namespace, err) + klog.Errorln(err) return nil, err } @@ -301,12 +321,12 @@ func GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, return items, nil } -func GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBinding, error) { - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() +func (am *amOperator) GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBinding, error) { + clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister() roleBindings, err := clusterRoleBindingLister.List(labels.Everything()) if err != nil { - klog.Errorln("get cluster role bindings", err) + klog.Errorln(err) return nil, err } @@ -321,14 +341,14 @@ func GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBindin return items, nil } -func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { +func (am *amOperator) ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - roleBindings, err := GetClusterRoleBindings(clusterRoleName) + roleBindings, err := am.GetClusterRoleBindings(clusterRoleName) if err != nil { return nil, err } - users := make([]*models.User, 0) + users := make([]*User, 0) for _, roleBinding := range roleBindings { for _, subject := range roleBinding.Subjects { if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { @@ -337,7 +357,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, continue } if err != nil { - klog.Errorln("get user info", subject.Name, err) + klog.Errorln(err) return nil, err } users = append(users, user) @@ -348,9 +368,7 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, // order & reverse sort.Slice(users, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } switch orderBy { default: @@ -372,43 +390,28 @@ func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, } -func RoleUsers(namespace string, roleName string) ([]*models.User, error) { - roleBindings, err := GetRoleBindings(namespace, roleName) +func (am *amOperator) ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + return am.resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset) +} +func (am *amOperator) ListRoleBindings(namespace string, role string) ([]*rbacv1.RoleBinding, error) { + rbs, err := am.informers.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything()) if err != nil { return nil, err } - - users := make([]*models.User, 0) - for _, roleBinding := range roleBindings { - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - user, err := GetUserInfo(subject.Name) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - continue - } - return nil, err - } - - user.Role = roleBinding.RoleRef.Name - - users = append(users, user) - } + result := make([]*rbacv1.RoleBinding, 0) + for _, rb := range rbs { + if rb.RoleRef.Name == role { + result = append(result, rb.DeepCopy()) } } - return users, nil + return result, nil } -func ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - return resources.ListResources(namespace, v1alpha2.Roles, conditions, orderBy, reverse, limit, offset) -} - -func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { +func (am *amOperator) ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { conditions.Match[v1alpha2.OwnerName] = workspace conditions.Match[v1alpha2.OwnerKind] = "Workspace" - result, err := resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset) + result, err := am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset) if err != nil { return nil, err @@ -424,23 +427,23 @@ func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy return result, nil } -func ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - return resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset) +func (am *amOperator) ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + return am.resources.ListResources("", v1alpha2.ClusterRoles, conditions, orderBy, reverse, limit, offset) } -func NamespaceUsers(namespaceName string) ([]*models.User, error) { - namespace, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespaceName) +func (am *amOperator) NamespaceUsers(namespaceName string) ([]*User, error) { + namespace, err := am.informers.Core().V1().Namespaces().Lister().Get(namespaceName) if err != nil { - klog.Errorln("get namespace", namespaceName, err) + klog.Errorln(err) return nil, err } - roleBindings, err := GetRoleBindings(namespaceName, "") + roleBindings, err := am.GetRoleBindings(namespaceName, "") if err != nil { return nil, err } - users := make([]*models.User, 0) + users := make([]*User, 0) for _, roleBinding := range roleBindings { // controlled by ks-controller-manager @@ -475,8 +478,8 @@ func NamespaceUsers(namespaceName string) ([]*models.User, error) { return users, nil } -func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) +func (am *amOperator) GetUserWorkspaceSimpleRules(workspace, username string) ([]SimpleRule, error) { + clusterRules, err := am.GetUserClusterRules(username) if err != nil { return nil, err } @@ -487,10 +490,10 @@ func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRul APIGroups: []string{"*"}, Resources: []string{"*"}, }) { - return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil + return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil } - workspaceRole, err := GetUserWorkspaceRole(workspace, username) + workspaceRole, err := am.GetUserWorkspaceRole(workspace, username) if err != nil { if apierrors.IsNotFound(err) { @@ -501,26 +504,26 @@ func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRul APIGroups: []string{"*"}, Resources: []string{"workspaces", "workspaces/*"}, }) { - return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil + return am.GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil } - return []models.SimpleRule{}, nil + return []SimpleRule{}, nil } klog.Error(err) return nil, err } - return GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil + return am.GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil } -func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule { +func (am *amOperator) GetWorkspaceRoleSimpleRules(workspace, roleName string) []SimpleRule { - workspaceRules := make([]models.SimpleRule, 0) + workspaceRules := make([]SimpleRule, 0) switch roleName { case constants.WorkspaceAdmin: - workspaceRules = []models.SimpleRule{ + workspaceRules = []SimpleRule{ {Name: "workspaces", Actions: []string{"edit", "delete", "view"}}, {Name: "members", Actions: []string{"edit", "delete", "create", "view"}}, {Name: "devops", Actions: []string{"edit", "delete", "create", "view"}}, @@ -530,7 +533,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule {Name: "repos", Actions: []string{"view", "manage"}}, } case constants.WorkspaceRegular: - workspaceRules = []models.SimpleRule{ + workspaceRules = []SimpleRule{ {Name: "members", Actions: []string{"view"}}, {Name: "devops", Actions: []string{"view", "create"}}, {Name: "projects", Actions: []string{"view", "create"}}, @@ -538,7 +541,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule {Name: "repos", Actions: []string{"view"}}, } case constants.WorkspaceViewer: - workspaceRules = []models.SimpleRule{ + workspaceRules = []SimpleRule{ {Name: "workspaces", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, {Name: "devops", Actions: []string{"view"}}, @@ -548,7 +551,7 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule {Name: "repos", Actions: []string{"view"}}, } case constants.WorkspacesManager: - workspaceRules = []models.SimpleRule{ + workspaceRules = []SimpleRule{ {Name: "workspaces", Actions: []string{"edit", "delete", "view"}}, {Name: "members", Actions: []string{"edit", "delete", "create", "view"}}, {Name: "roles", Actions: []string{"view"}}, @@ -559,33 +562,33 @@ func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule } // Convert cluster role to rules -func GetClusterRoleSimpleRules(clusterRoleName string) ([]models.SimpleRule, error) { +func (am *amOperator) GetClusterRoleSimpleRules(clusterRoleName string) ([]SimpleRule, error) { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() + clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister() clusterRole, err := clusterRoleLister.Get(clusterRoleName) if err != nil { - klog.Errorln("get cluster role", clusterRoleName, clusterRoleName) + klog.Errorln(err) return nil, err } return getClusterSimpleRule(clusterRole.Rules), nil } -func GetUserClusterSimpleRules(username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) +func (am *amOperator) GetUserClusterSimpleRules(username string) ([]SimpleRule, error) { + clusterRules, err := am.GetUserClusterRules(username) if err != nil { return nil, err } return getClusterSimpleRule(clusterRules), nil } -func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) +func (am *amOperator) GetUserNamespaceSimpleRules(namespace, username string) ([]SimpleRule, error) { + clusterRules, err := am.GetUserClusterRules(username) if err != nil { return nil, err } - rules, err := GetUserRules(namespace, username) + rules, err := am.GetUserRules(namespace, username) if err != nil { return nil, err } @@ -595,21 +598,21 @@ func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRul } // Convert roles to rules -func GetRoleSimpleRules(namespace string, roleName string) ([]models.SimpleRule, error) { +func (am *amOperator) GetRoleSimpleRules(namespace string, roleName string) ([]SimpleRule, error) { - roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister() + roleLister := am.informers.Rbac().V1().Roles().Lister() role, err := roleLister.Roles(namespace).Get(roleName) if err != nil { - klog.Errorln("get role", namespace, roleName, err) + klog.Errorln(err) return nil, err } return getSimpleRule(role.Rules), nil } -func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { - rules := make([]models.SimpleRule, 0) +func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule { + rules := make([]SimpleRule, 0) for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ { validActions := make([]string, 0) @@ -619,17 +622,17 @@ func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { } } if len(validActions) > 0 { - rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions}) + rules = append(rules, SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions}) } } return rules } -func getSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { - simpleRules := make([]models.SimpleRule, 0) +func getSimpleRule(policyRules []rbacv1.PolicyRule) []SimpleRule { + simpleRules := make([]SimpleRule, 0) for i := 0; i < len(policy.RoleRuleMapping); i++ { - rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name} + rule := SimpleRule{Name: policy.RoleRuleMapping[i].Name} rule.Actions = make([]string, 0) for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ { if rulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) { @@ -643,13 +646,13 @@ func getSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { return simpleRules } -func CreateClusterRoleBinding(username string, clusterRoleName string) error { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() +func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName string) error { + clusterRoleLister := am.informers.Rbac().V1().ClusterRoles().Lister() _, err := clusterRoleLister.Get(clusterRoleName) if err != nil { - klog.Errorln("get cluster role", clusterRoleName, err) + klog.Errorln(err) return err } @@ -671,7 +674,7 @@ func CreateClusterRoleBinding(username string, clusterRoleName string) error { clusterRoleBinding.RoleRef = rbacv1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind} clusterRoleBinding.Subjects = []rbacv1.Subject{{Kind: rbacv1.UserKind, Name: username}} - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() + clusterRoleBindingLister := am.informers.Rbac().V1().ClusterRoleBindings().Lister() found, err := clusterRoleBindingLister.Get(username) if apierrors.IsNotFound(err) { @@ -714,159 +717,3 @@ func CreateClusterRoleBinding(username string, clusterRoleName string) error { return nil } - -func RulesMatchesRequired(rules []rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { - for _, rule := range rules { - if ruleMatchesRequired(rule, required) { - return true - } - } - return false -} - -func rulesMatchesAction(rules []rbacv1.PolicyRule, action models.Action) bool { - - for _, required := range action.Rules { - if !RulesMatchesRequired(rules, required) { - return false - } - } - - return true -} - -func ruleMatchesRequired(rule rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { - - if len(required.NonResourceURLs) == 0 { - for _, apiGroup := range required.APIGroups { - for _, resource := range required.Resources { - resources := strings.Split(resource, "/") - resource = resources[0] - var subsource string - if len(resources) > 1 { - subsource = resources[1] - } - - if len(required.ResourceNames) == 0 { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) { - return false - } - } - } else { - for _, resourceName := range required.ResourceNames { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) { - return false - } - } - } - } - } - } - } else { - for _, apiGroup := range required.APIGroups { - for _, nonResourceURL := range required.NonResourceURLs { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) { - return false - } - } - } - } - } - return true -} - -func ruleMatchesResources(rule rbacv1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool { - - if resource == "" { - return false - } - - if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, rbacv1.ResourceAll) { - return false - } - - if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) { - return false - } - - combinedResource := resource - - if subresource != "" { - combinedResource = combinedResource + "/" + subresource - } - - for _, res := range rule.Resources { - - // match "*" - if res == rbacv1.ResourceAll || res == combinedResource { - return true - } - - // match "*/subresource" - if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") { - return true - } - // match "resource/*" - if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") { - return true - } - } - - return false -} - -func ruleMatchesRequest(rule rbacv1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool { - - if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, rbacv1.VerbAll) { - return false - } - - if nonResourceURL == "" { - return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName) - } else { - return ruleMatchesNonResource(rule, nonResourceURL) - } -} - -func ruleMatchesNonResource(rule rbacv1.PolicyRule, nonResourceURL string) bool { - - if nonResourceURL == "" { - return false - } - - for _, spec := range rule.NonResourceURLs { - if pathMatches(nonResourceURL, spec) { - return true - } - } - - return false -} - -func pathMatches(path, spec string) bool { - // Allow wildcard match - if spec == "*" { - return true - } - // Allow exact match - if spec == path { - return true - } - // Allow a trailing * subpath match - if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) { - return true - } - return false -} - -func hasString(slice []string, value string) bool { - for _, s := range slice { - if s == value { - return true - } - } - return false -} diff --git a/pkg/models/iam/im.go b/pkg/models/iam/im.go index 1c35d2a5b..5aa35a6c2 100644 --- a/pkg/models/iam/im.go +++ b/pkg/models/iam/im.go @@ -22,6 +22,9 @@ import ( "errors" "fmt" "github.com/emicklei/go-restful" + "github.com/go-ldap/ldap" + "github.com/go-redis/redis" + "golang.org/x/oauth2" "io/ioutil" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/constants" @@ -32,6 +35,7 @@ import ( "kubesphere.io/kubesphere/pkg/models/kubectl" "kubesphere.io/kubesphere/pkg/server/params" clientset "kubesphere.io/kubesphere/pkg/simple/client" + ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap" "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" "net/http" @@ -42,7 +46,6 @@ import ( "time" "github.com/dgrijalva/jwt-go" - "github.com/go-ldap/ldap" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" @@ -50,52 +53,57 @@ import ( "kubesphere.io/kubesphere/pkg/utils/jwtutil" ) -var ( - adminEmail string - adminPassword string - tokenIdleTimeout time.Duration - maxAuthFailed int - authTimeInterval time.Duration - initUsers []initUser - enableMultiLogin bool - generateKubeConfig bool -) +type IdentityManagementInterface interface { + CreateUser(user *User) (*User, error) + DescribeUser(username string) (*User, error) + Login(username, password, ip string) (*oauth2.Token, error) +} + +type imOperator struct { + config Config + ldap ldappool.Client + redis redis.Client + initUsers []initUser +} type initUser struct { - models.User + User Hidden bool `json:"hidden"` } const ( - userInitFile = "/etc/ks-iam/users.json" - authRateLimitRegex = `(\d+)/(\d+[s|m|h])` - defaultMaxAuthFailed = 5 - defaultAuthTimeInterval = 30 * time.Minute + authRateLimitRegex = `(\d+)/(\d+[s|m|h])` + defaultMaxAuthFailed = 5 + defaultAuthTimeInterval = 30 * time.Minute + mailAttribute = "mail" + uidAttribute = "uid" + descriptionAttribute = "description" + preferredLanguageAttribute = "preferredLanguage" + createTimestampAttribute = "createTimestampAttribute" + dateTimeLayout = "20060102150405Z" ) -func Init(email, password, authRateLimit string, idleTimeout time.Duration, multiLogin bool, isGeneratingKubeConfig bool) error { - adminEmail = email - adminPassword = password - tokenIdleTimeout = idleTimeout - maxAuthFailed, authTimeInterval = parseAuthRateLimit(authRateLimit) - enableMultiLogin = multiLogin - generateKubeConfig = isGeneratingKubeConfig +func IdentityManagementInit(ldap ldappool.Client, config Config) (IdentityManagementInterface, error) { - err := checkAndCreateDefaultUser() + //maxAuthFailed, authTimeInterval := parseAuthRateLimit(authRateLimit) + + imOperator := &imOperator{ldap: ldap, config: config} + + err := imOperator.checkAndCreateDefaultUser() if err != nil { - klog.Errorln("create default users", err) - return err + klog.Errorln(err) + return nil, err } - err = checkAndCreateDefaultGroup() + err = imOperator.checkAndCreateDefaultGroup() if err != nil { - klog.Errorln("create default groups", err) - return err + klog.Errorln(err) + return nil, err } - return nil + return imOperator, nil } func parseAuthRateLimit(authRateLimit string) (int, time.Duration) { @@ -115,20 +123,16 @@ func parseAuthRateLimit(authRateLimit string) (int, time.Duration) { return maxCount, timeInterval } -func checkAndCreateDefaultGroup() error { +func (im *imOperator) checkAndCreateDefaultGroup() error { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return err } defer conn.Close() groupSearchRequest := ldap.NewSearchRequest( - client.GroupSearchBase(), + im.ldap.GroupSearchBase(), ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectClass=posixGroup))", nil, @@ -138,9 +142,9 @@ func checkAndCreateDefaultGroup() error { _, err = conn.Search(groupSearchRequest) if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - err = createGroupsBaseDN() + err = im.createGroupsBaseDN() if err != nil { - return fmt.Errorf("GroupBaseDN %s create failed: %s\n", client.GroupSearchBase(), err) + return fmt.Errorf("GroupBaseDN %s create failed: %s\n", im.ldap.GroupSearchBase(), err) } } @@ -151,20 +155,15 @@ func checkAndCreateDefaultGroup() error { return nil } -func checkAndCreateDefaultUser() error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() +func (im *imOperator) checkAndCreateDefaultUser() error { + conn, err := im.ldap.NewConn() if err != nil { return err } defer conn.Close() userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), + im.ldap.UserSearchBase(), ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(&(objectClass=inetOrgPerson))", []string{"uid"}, @@ -174,9 +173,9 @@ func checkAndCreateDefaultUser() error { result, err := conn.Search(userSearchRequest) if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - err = createUserBaseDN() + err = im.createUserBaseDN() if err != nil { - return fmt.Errorf("UserBaseDN %s create failed: %s\n", client.UserSearchBase(), err) + return fmt.Errorf("UserBaseDN %s create failed: %s\n", im.ldap.UserSearchBase(), err) } } @@ -184,18 +183,20 @@ func checkAndCreateDefaultUser() error { return fmt.Errorf("iam database init failed: %s\n", err) } - data, err := ioutil.ReadFile(userInitFile) - if err == nil { - json.Unmarshal(data, &initUsers) - } - initUsers = append(initUsers, initUser{User: models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default.", ClusterRole: constants.ClusterAdmin}}) + data, err := ioutil.ReadFile(im.config.userInitFile) - for _, user := range initUsers { + if err == nil { + json.Unmarshal(data, &im.initUsers) + } + + im.initUsers = append(im.initUsers, initUser{User: User{Username: constants.AdminUserName, Email: im.config.adminEmail, Password: im.config.adminPassword, Description: "Administrator account that was always created by default.", ClusterRole: constants.ClusterAdmin}}) + + for _, user := range im.initUsers { if result == nil || !containsUser(result.Entries, user) { - _, err = CreateUser(&user.User) + _, err = im.CreateUser(&user.User) if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - klog.Errorln("user init failed", user.Username, err) - return fmt.Errorf("user %s init failed: %s\n", user.Username, err) + klog.Errorln(err) + return err } } } @@ -213,260 +214,82 @@ func containsUser(entries []*ldap.Entry, user initUser) bool { return false } -func createUserBaseDN() error { - client, err := clientset.ClientSets().Ldap() +func (im *imOperator) createUserBaseDN() error { - if err != nil { - return err - } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return err } defer conn.Close() - groupsCreateRequest := ldap.NewAddRequest(client.UserSearchBase(), nil) + groupsCreateRequest := ldap.NewAddRequest(im.ldap.UserSearchBase(), nil) groupsCreateRequest.Attribute("objectClass", []string{"organizationalUnit", "top"}) groupsCreateRequest.Attribute("ou", []string{"Users"}) return conn.Add(groupsCreateRequest) } -func createGroupsBaseDN() error { - client, err := clientset.ClientSets().Ldap() +func (im *imOperator) createGroupsBaseDN() error { - if err != nil { - return err - } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return err } defer conn.Close() - groupsCreateRequest := ldap.NewAddRequest(client.GroupSearchBase(), nil) + groupsCreateRequest := ldap.NewAddRequest(im.ldap.GroupSearchBase(), nil) groupsCreateRequest.Attribute("objectClass", []string{"organizationalUnit", "top"}) groupsCreateRequest.Attribute("ou", []string{"Groups"}) return conn.Add(groupsCreateRequest) } -func RefreshToken(refreshToken string) (*models.AuthGrantResponse, error) { - validRefreshToken, err := jwtutil.ValidateToken(refreshToken) - if err != nil { - klog.Error(err) - return nil, err - } - - payload, ok := validRefreshToken.Claims.(jwt.MapClaims) - - if !ok { - err = errors.New("invalid payload") - klog.Error(err) - return nil, err - } - - claims := jwt.MapClaims{} - - // token with expiration time will not auto sliding - claims["username"] = payload["username"] - claims["email"] = payload["email"] - claims["iat"] = time.Now().Unix() - claims["exp"] = time.Now().Add(tokenIdleTimeout * 4).Unix() - - token := jwtutil.MustSigned(claims) - - claims = jwt.MapClaims{} - claims["username"] = payload["username"] - claims["email"] = payload["email"] - claims["iat"] = time.Now().Unix() - claims["type"] = "refresh_token" - claims["exp"] = time.Now().Add(tokenIdleTimeout * 5).Unix() - - refreshToken = jwtutil.MustSigned(claims) - - return &models.AuthGrantResponse{TokenType: "jwt", Token: token, RefreshToken: refreshToken, ExpiresIn: (tokenIdleTimeout * 4).Seconds()}, nil -} - -func PasswordCredentialGrant(username, password, ip string) (*models.AuthGrantResponse, error) { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result() - - if err != nil { - klog.Error(err) - return nil, err - } - - if len(records) >= maxAuthFailed { - return nil, restful.NewError(http.StatusTooManyRequests, "auth rate limit exceeded") - } - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password")) - } - - uid := result.Entries[0].GetAttributeValue("uid") - email := result.Entries[0].GetAttributeValue("mail") - dn := result.Entries[0].DN - - // bind as the user to verify their password - err = conn.Bind(dn, password) - - if err != nil { - klog.Infoln("auth failed", username, err) - - if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { - loginFailedRecord := fmt.Sprintf("kubesphere:authfailed:%s:%d", uid, time.Now().UnixNano()) - redisClient.Set(loginFailedRecord, "", authTimeInterval) - } - - return nil, err - } - - claims := jwt.MapClaims{} - - // token with expiration time will not auto sliding - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() - claims["exp"] = time.Now().Add(tokenIdleTimeout * 4).Unix() - - token := jwtutil.MustSigned(claims) - - if !enableMultiLogin { - // multi login not allowed, remove the previous token - sessions, err := redisClient.Keys(fmt.Sprintf("kubesphere:users:%s:token:*", uid)).Result() - - if err != nil { - klog.Errorln(err) - return nil, err - } - - if len(sessions) > 0 { - klog.V(4).Infoln("revoke token", sessions) - err = redisClient.Del(sessions...).Err() - if err != nil { - klog.Errorln(err) - return nil, err - } - } - } - - claims = jwt.MapClaims{} - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() - claims["type"] = "refresh_token" - claims["exp"] = time.Now().Add(tokenIdleTimeout * 5).Unix() - - refreshToken := jwtutil.MustSigned(claims) - - loginLog(uid, ip) - - return &models.AuthGrantResponse{TokenType: "jwt", Token: token, RefreshToken: refreshToken, ExpiresIn: (tokenIdleTimeout * 4).Seconds()}, nil -} - // User login -func Login(username, password, ip string) (*models.AuthGrantResponse, error) { +func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error) { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result() + records, err := im.redis.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result() if err != nil { klog.Error(err) return nil, err } - if len(records) >= maxAuthFailed { + if len(records) >= im.config.maxAuthFailed { return nil, restful.NewError(http.StatusTooManyRequests, "auth rate limit exceeded") } - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() + user, err := im.DescribeUser(&User{Username: username, Email: username}) + + conn, err := im.ldap.NewConn() if err != nil { + klog.Error(err) return nil, err } defer conn.Close() - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password")) - } - - uid := result.Entries[0].GetAttributeValue("uid") - email := result.Entries[0].GetAttributeValue("mail") - dn := result.Entries[0].DN + dn := fmt.Sprintf("%s=%s,%s", uidAttribute, user.Username, im.ldap.UserSearchBase()) // bind as the user to verify their password err = conn.Bind(dn, password) if err != nil { - klog.Infoln("auth failed", username, err) - if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { - loginFailedRecord := fmt.Sprintf("kubesphere:authfailed:%s:%d", uid, time.Now().UnixNano()) - redisClient.Set(loginFailedRecord, "", authTimeInterval) + authFailedCacheKey := fmt.Sprintf("kubesphere:authfailed:%s:%d", user.Username, time.Now().UnixNano()) + im.redis.Set(authFailedCacheKey, "", im.config.authTimeInterval) } - return nil, err } claims := jwt.MapClaims{} + loginTime := time.Now() // token without expiration time will auto sliding - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() + claims["username"] = user.Username + claims["email"] = user.Email + claims["iat"] = loginTime.Unix() token := jwtutil.MustSigned(claims) - if !enableMultiLogin { + if !im.config.enableMultiLogin { // multi login not allowed, remove the previous token - sessions, err := redisClient.Keys(fmt.Sprintf("kubesphere:users:%s:token:*", uid)).Result() + sessionCacheKey := fmt.Sprintf("kubesphere:users:%s:token:*", user.Username) + sessions, err := im.redis.Keys(sessionCacheKey).Result() if err != nil { klog.Errorln(err) @@ -475,7 +298,7 @@ func Login(username, password, ip string) (*models.AuthGrantResponse, error) { if len(sessions) > 0 { klog.V(4).Infoln("revoke token", sessions) - err = redisClient.Del(sessions...).Err() + err = im.redis.Del(sessions...).Err() if err != nil { klog.Errorln(err) return nil, err @@ -484,34 +307,26 @@ func Login(username, password, ip string) (*models.AuthGrantResponse, error) { } // cache token with expiration time - if err = redisClient.Set(fmt.Sprintf("kubesphere:users:%s:token:%s", uid, token), token, tokenIdleTimeout).Err(); err != nil { + sessionCacheKey := fmt.Sprintf("kubesphere:users:%s:token:%s", user.Username, token) + if err = im.redis.Set(sessionCacheKey, token, im.config.tokenIdleTimeout).Err(); err != nil { klog.Errorln(err) return nil, err } - loginLog(uid, ip) + im.loginRecord(user.Username, ip, loginTime) - return &models.AuthGrantResponse{Token: token}, nil + return &oauth2.Token{AccessToken: token}, nil } -func loginLog(uid, ip string) { +func (im *imOperator) loginRecord(username, ip string, loginTime time.Time) { if ip != "" { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return - } - redisClient.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", uid), fmt.Sprintf("%s,%s", time.Now().UTC().Format("2006-01-02T15:04:05Z"), ip)) - redisClient.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -10, -1) + im.redis.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", username), fmt.Sprintf("%s,%s", loginTime.UTC().Format("2006-01-02T15:04:05Z"), ip)) + im.redis.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1) } } -func LoginLog(username string) ([]string, error) { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - data, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1).Result() +func (im *imOperator) LoginHistory(username string) ([]string, error) { + data, err := im.redis.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1).Result() if err != nil { return nil, err @@ -520,13 +335,8 @@ func LoginLog(username string) ([]string, error) { return data, nil } -func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() +func (im *imOperator) ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + conn, err := im.ldap.NewConn() if err != nil { return nil, err } @@ -534,7 +344,7 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi pageControl := ldap.NewControlPaging(1000) - users := make([]models.User, 0) + users := make([]User, 0) filter := "(&(objectClass=inetOrgPerson))" @@ -560,7 +370,7 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi for { userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), + im.ldap.UserSearchBase(), ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"}, @@ -582,9 +392,9 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi lang := entry.GetAttributeValue("preferredLanguage") createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp")) - user := models.User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} + user := User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} - if !shouldHidden(user) { + if !im.shouldHidden(user) { users = append(users, user) } } @@ -620,8 +430,8 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi if i >= offset && len(items) < limit { - user.LastLoginTime = getLastLoginTime(user.Username) - clusterRole, err := GetUserClusterRole(user.Username) + user.LastLoginTime = im.GetLastLoginTime(user.Username) + clusterRole, err := im.GetUserClusterRole(user.Username) if err != nil { return nil, err } @@ -633,8 +443,8 @@ func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limi return &models.PageableResponse{Items: items, TotalCount: len(users)}, nil } -func shouldHidden(user models.User) bool { - for _, initUser := range initUsers { +func (im *imOperator) shouldHidden(user User) bool { + for _, initUser := range im.initUsers { if initUser.Username == user.Username { return initUser.Hidden } @@ -642,109 +452,53 @@ func shouldHidden(user models.User) bool { return false } -func DescribeUser(username string) (*models.User, error) { - - user, err := GetUserInfo(username) +func (im *imOperator) DescribeUser(user *User) (*User, error) { + conn, err := im.ldap.NewConn() if err != nil { - return nil, err - } - - groups, err := GetUserGroups(username) - - if err == nil { - user.Groups = groups - } - - return user, nil -} - -// Get user info only included email description & lang -func GetUserInfo(username string) (*models.User, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { + klog.Errorln(err) return nil, err } defer conn.Close() - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username), - []string{"mail", "description", "preferredLanguage", "createTimestamp"}, + filter := fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", user.Username, user.Email) + + searchRequest := ldap.NewSearchRequest( + im.ldap.UserSearchBase(), + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 1, 0, false, + filter, + []string{mailAttribute, descriptionAttribute, preferredLanguageAttribute, createTimestampAttribute}, nil, ) - result, err := conn.Search(userSearchRequest) + result, err := conn.Search(searchRequest) if err != nil { - klog.Errorln("search user", err) + klog.Errorln(err) return nil, err } if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, fmt.Errorf("user %s does not exist", username)) + return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, errors.New("user does not exist")) } - email := result.Entries[0].GetAttributeValue("mail") - 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: email, Description: description, Lang: lang, CreateTime: createTimestamp} + entry := result.Entries[0] - user.LastLoginTime = getLastLoginTime(username) - - return user, nil + return convertLdapEntryToUser(entry), nil } -func GetUserGroups(username string) ([]string, error) { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - groupSearchRequest := ldap.NewSearchRequest( - client.GroupSearchBase(), - ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=posixGroup)(memberUid=%s))", username), - nil, - nil, - ) - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - return nil, err - } - - groups := make([]string, 0) - - for _, group := range result.Entries { - groupName := convertDNToPath(group.DN) - groups = append(groups, groupName) - } - - return groups, nil +func convertLdapEntryToUser(entry *ldap.Entry) *User { + username := entry.GetAttributeValue(uidAttribute) + email := entry.GetAttributeValue(mailAttribute) + description := entry.GetAttributeValue(descriptionAttribute) + lang := entry.GetAttributeValue(preferredLanguageAttribute) + createTimestamp, _ := time.Parse(dateTimeLayout, entry.GetAttributeValue(createTimestampAttribute)) + return &User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} } -func getLastLoginTime(username string) string { - redis, err := clientset.ClientSets().Redis() - if err != nil { - return "" - } - - lastLogin, err := redis.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result() - +func (im *imOperator) GetLastLoginTime(username string) string { + cacheKey := fmt.Sprintf("kubesphere:users:%s:login-log", username) + lastLogin, err := im.redis.LRange(cacheKey, -1, -1).Result() if err != nil { return "" } @@ -756,26 +510,21 @@ func getLastLoginTime(username string) string { return "" } -func DeleteUser(username string) error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() +func (im *imOperator) DeleteUser(username string) error { + conn, err := im.ldap.NewConn() if err != nil { return err } defer conn.Close() - deleteRequest := ldap.NewDelRequest(fmt.Sprintf("uid=%s,%s", username, client.UserSearchBase()), nil) + deleteRequest := ldap.NewDelRequest(fmt.Sprintf("uid=%s,%s", username, im.ldap.UserSearchBase()), nil) if err = conn.Del(deleteRequest); err != nil { klog.Errorln("delete user", err) return err } - if err = deleteRoleBindings(username); err != nil { + if err = im.deleteRoleBindings(username); err != nil { klog.Errorln("delete user role bindings failed", username, err) } @@ -787,7 +536,7 @@ func DeleteUser(username string) error { klog.Errorln("delete user terminal pod failed", username, err) } - if err := deleteUserInDevOps(username); err != nil { + if err := im.deleteUserInDevOps(username); err != nil { klog.Errorln("delete user in devops failed", username, err) } return nil @@ -795,7 +544,7 @@ func DeleteUser(username string) error { } // deleteUserInDevOps is used to clean up user data of devops, such as permission rules -func deleteUserInDevOps(username string) error { +func (im *imOperator) deleteUserInDevOps(username string) error { devopsDb, err := clientset.ClientSets().MySQL() if err != nil { @@ -834,7 +583,7 @@ func deleteUserInDevOps(username string) error { return nil } -func deleteRoleBindings(username string) error { +func (im *imOperator) deleteRoleBindings(username string) error { roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() roleBindings, err := roleBindingLister.List(labels.Everything()) @@ -910,224 +659,80 @@ func deleteRoleBindings(username string) error { return nil } -func isWorkspaceRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) bool { +func (im *imOperator) isWorkspaceRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) bool { return k8sutil.IsControlledBy(clusterRoleBinding.OwnerReferences, "Workspace", "") } -func UserCreateCheck(check string) (exist bool, err error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return false, err - } - conn, err := client.NewConn() - if err != nil { - return false, err - } - defer conn.Close() - - // search for the given username - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", check, check), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - klog.Errorln("search user", err) - return false, err - } - - return len(result.Entries) > 0, nil -} - -func CreateUser(user *models.User) (*models.User, error) { +func (im *imOperator) CreateUser(user *User) (*User, error) { user.Username = strings.TrimSpace(user.Username) user.Email = strings.TrimSpace(user.Email) user.Password = strings.TrimSpace(user.Password) user.Description = strings.TrimSpace(user.Description) - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", user.Username, user.Email), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) + existed, err := im.DescribeUser(user) if err != nil { - klog.Errorln("search user", err) + klog.Errorln(err) return nil, err } - if len(result.Entries) > 0 { - return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists")) + if existed != nil { + return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, errors.New("username or email already exists")) } - maxUid, err := getMaxUid() + uidNumber := im.uidNumberNext() - if err != nil { - klog.Errorln("get max uid", err) - return nil, err - } - - maxUid += 1 - - userCreateRequest := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, client.UserSearchBase()), nil) - userCreateRequest.Attribute("objectClass", []string{"inetOrgPerson", "posixAccount", "top"}) - userCreateRequest.Attribute("cn", []string{user.Username}) // RFC4519: common name(s) for which the entity is known by - userCreateRequest.Attribute("sn", []string{" "}) // RFC2256: last (family) name(s) for which the entity is known by - userCreateRequest.Attribute("gidNumber", []string{"500"}) // RFC2307: An integer uniquely identifying a group in an administrative domain - userCreateRequest.Attribute("homeDirectory", []string{"/home/" + user.Username}) // The absolute path to the home directory - userCreateRequest.Attribute("uid", []string{user.Username}) // RFC4519: user identifier - userCreateRequest.Attribute("uidNumber", []string{strconv.Itoa(maxUid)}) // RFC2307: An integer uniquely identifying a user in an administrative domain - userCreateRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox - userCreateRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user + createRequest := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, im.ldap.UserSearchBase()), nil) + createRequest.Attribute("objectClass", []string{"inetOrgPerson", "posixAccount", "top"}) + createRequest.Attribute("cn", []string{user.Username}) // RFC4519: common name(s) for which the entity is known by + createRequest.Attribute("sn", []string{" "}) // RFC2256: last (family) name(s) for which the entity is known by + createRequest.Attribute("gidNumber", []string{"500"}) // RFC2307: An integer uniquely identifying a group in an administrative domain + createRequest.Attribute("homeDirectory", []string{"/home/" + user.Username}) // The absolute path to the home directory + createRequest.Attribute("uid", []string{user.Username}) // RFC4519: user identifier + createRequest.Attribute("uidNumber", []string{strconv.Itoa(uidNumber)}) // RFC2307: An integer uniquely identifying a user in an administrative domain + createRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox + createRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user if user.Lang != "" { - userCreateRequest.Attribute("preferredLanguage", []string{user.Lang}) + createRequest.Attribute("preferredLanguage", []string{user.Lang}) } if user.Description != "" { - userCreateRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information + createRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information } - if generateKubeConfig { - if err = kubeconfig.CreateKubeConfig(user.Username); err != nil { - klog.Errorln("create user kubeconfig failed", user.Username, err) - return nil, err - } - } - - err = conn.Add(userCreateRequest) + conn, err := im.ldap.NewConn() if err != nil { - klog.Errorln("create user", err) + klog.Errorln(err) return nil, err } - if user.ClusterRole != "" { - err := CreateClusterRoleBinding(user.Username, user.ClusterRole) + err = conn.Add(createRequest) - if err != nil { - klog.Errorln("create cluster role binding filed", err) - return nil, err - } + if err != nil { + klog.Errorln(err) + return nil, err } - return DescribeUser(user.Username) + return user, nil } -func getMaxUid() (int, error) { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return 0, err - } - conn, err := client.NewConn() - if err != nil { - return 0, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest(client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=inetOrgPerson))", - []string{"uidNumber"}, - nil) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return 0, err - } - - var maxUid int - - if len(result.Entries) == 0 { - maxUid = 1000 - } else { - for _, usr := range result.Entries { - uid, _ := strconv.Atoi(usr.GetAttributeValue("uidNumber")) - if uid > maxUid { - maxUid = uid - } - } - } - - return maxUid, nil -} - -func getMaxGid() (int, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return 0, err - } - conn, err := client.NewConn() - if err != nil { - return 0, err - } - defer conn.Close() - - groupSearchRequest := ldap.NewSearchRequest(client.GroupSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - []string{"gidNumber"}, - nil) - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - return 0, err - } - - var maxGid int - - if len(result.Entries) == 0 { - maxGid = 500 - } else { - for _, group := range result.Entries { - gid, _ := strconv.Atoi(group.GetAttributeValue("gidNumber")) - if gid > maxGid { - maxGid = gid - } - } - } - - return maxGid, nil -} - -func UpdateUser(user *models.User) (*models.User, error) { +func (im *imOperator) UpdateUser(user *User) (*User, error) { client, err := clientset.ClientSets().Ldap() if err != nil { return nil, err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return nil, err } defer conn.Close() - dn := fmt.Sprintf("uid=%s,%s", user.Username, client.UserSearchBase()) + dn := fmt.Sprintf("uid=%s,%s", user.Username, im.ldap.UserSearchBase()) userModifyRequest := ldap.NewModifyRequest(dn, nil) if user.Email != "" { userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), + im.ldap.UserSearchBase(), ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, fmt.Sprintf("(&(objectClass=inetOrgPerson)(mail=%s))", user.Email), []string{"uid", "mail"}, @@ -1185,22 +790,22 @@ func UpdateUser(user *models.User) (*models.User, error) { return nil, err } - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", user.Username)).Result() + records, err := im.redis.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", user.Username)).Result() if err == nil { - redisClient.Del(records...) + im.redis.Del(records...) } } return GetUserInfo(user.Username) } -func DeleteGroup(path string) error { +func (im *imOperator) DeleteGroup(path string) error { client, err := clientset.ClientSets().Ldap() if err != nil { return err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return err } @@ -1219,13 +824,13 @@ func DeleteGroup(path string) error { return nil } -func CreateGroup(group *models.Group) (*models.Group, error) { +func (im *imOperator) CreateGroup(group *models.Group) (*models.Group, error) { client, err := clientset.ClientSets().Ldap() if err != nil { return nil, err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return nil, err } @@ -1271,13 +876,13 @@ func CreateGroup(group *models.Group) (*models.Group, error) { return DescribeGroup(group.Path) } -func UpdateGroup(group *models.Group) (*models.Group, error) { +func (im *imOperator) UpdateGroup(group *models.Group) (*models.Group, error) { client, err := clientset.ClientSets().Ldap() if err != nil { return nil, err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return nil, err } @@ -1319,13 +924,13 @@ func UpdateGroup(group *models.Group) (*models.Group, error) { return group, nil } -func ChildList(path string) ([]models.Group, error) { +func (im *imOperator) ChildList(path string) ([]models.Group, error) { client, err := clientset.ClientSets().Ldap() if err != nil { return nil, err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return nil, err } @@ -1391,12 +996,12 @@ func ChildList(path string) ([]models.Group, error) { return groups, nil } -func DescribeGroup(path string) (*models.Group, error) { +func (im *imOperator) DescribeGroup(path string) (*models.Group, error) { client, err := clientset.ClientSets().Ldap() if err != nil { return nil, err } - conn, err := client.NewConn() + conn, err := im.ldap.NewConn() if err != nil { return nil, err } @@ -1437,7 +1042,7 @@ func DescribeGroup(path string) (*models.Group, error) { } -func WorkspaceUsersTotalCount(workspace string) (int, error) { +func (im *imOperator) WorkspaceUsersTotalCount(workspace string) (int, error) { workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace) if err != nil { @@ -1457,7 +1062,7 @@ func WorkspaceUsersTotalCount(workspace string) (int, error) { return len(users), nil } -func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { +func (im *imOperator) ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace) @@ -1465,7 +1070,7 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy return nil, err } - users := make([]*models.User, 0) + users := make([]*User, 0) for _, roleBinding := range workspaceRoleBindings { for _, subject := range roleBinding.Subjects { @@ -1509,7 +1114,11 @@ func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil } -func matchConditions(conditions *params.Conditions, user *models.User) bool { +func (im *imOperator) uidNumberNext() int { + return 0 +} + +func matchConditions(conditions *params.Conditions, user *User) bool { for k, v := range conditions.Match { switch k { case "keyword": @@ -1536,3 +1145,13 @@ func matchConditions(conditions *params.Conditions, user *models.User) bool { } return true } + +type User struct { + 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"` +} diff --git a/pkg/models/iam/policy/policy.go b/pkg/models/iam/policy/policy.go index bce92c8f6..6574244f1 100644 --- a/pkg/models/iam/policy/policy.go +++ b/pkg/models/iam/policy/policy.go @@ -21,22 +21,21 @@ package policy import ( "encoding/json" "io/ioutil" - - "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/iam" "k8s.io/api/rbac/v1" ) const ( - rulesConfigPath = "/etc/kubesphere/rules/rules.json" - clusterRulesConfigPath = "/etc/kubesphere/rules/clusterrules.json" + rulesConfigPath = iam.ConfigPath + "/rules.json" + clusterRulesConfigPath = iam.ConfigPath + "/clusterrules.json" ) func init() { rulesConfig, err := ioutil.ReadFile(rulesConfigPath) if err == nil { - config := &[]models.Rule{} + config := &[]iam.Rule{} json.Unmarshal(rulesConfig, config) if len(*config) > 0 { RoleRuleMapping = *config @@ -46,7 +45,7 @@ func init() { clusterRulesConfig, err := ioutil.ReadFile(clusterRulesConfigPath) if err == nil { - config := &[]models.Rule{} + config := &[]iam.Rule{} json.Unmarshal(clusterRulesConfig, config) if len(*config) > 0 { ClusterRoleRuleMapping = *config @@ -55,9 +54,9 @@ func init() { } var ( - ClusterRoleRuleMapping = []models.Rule{ + ClusterRoleRuleMapping = []iam.Rule{ {Name: "workspaces", - Actions: []models.Action{ + Actions: []iam.Action{ { Name: "manage", Rules: []v1.PolicyRule{ @@ -72,7 +71,7 @@ var ( }, { Name: "monitoring", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{{ Verbs: []string{"get", "list"}, @@ -88,7 +87,7 @@ var ( }, { Name: "alerting", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{{ Verbs: []string{"get", "list"}, @@ -114,7 +113,7 @@ var ( }, { Name: "logging", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{{ Verbs: []string{"get", "list"}, @@ -126,7 +125,7 @@ var ( }, { Name: "accounts", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -193,7 +192,7 @@ var ( }, }, { Name: "roles", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -239,7 +238,7 @@ var ( }, }, { Name: "storageclasses", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -283,7 +282,7 @@ var ( }, }, { Name: "nodes", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -314,7 +313,7 @@ var ( }, }, { Name: "repos", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -354,7 +353,7 @@ var ( }, }, { Name: "apps", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -367,7 +366,7 @@ var ( }, }, { Name: "components", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -380,9 +379,9 @@ var ( }, }} - RoleRuleMapping = []models.Rule{{ + RoleRuleMapping = []iam.Rule{{ Name: "projects", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -419,7 +418,7 @@ var ( }, { Name: "monitoring", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{{ Verbs: []string{"get", "list"}, @@ -436,7 +435,7 @@ var ( { Name: "alerting", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{{ Verbs: []string{"get", "list"}, @@ -462,7 +461,7 @@ var ( }, { Name: "members", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -508,7 +507,7 @@ var ( }, { Name: "roles", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -549,7 +548,7 @@ var ( }, { Name: "deployments", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -605,7 +604,7 @@ var ( }, }, { Name: "statefulsets", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -659,7 +658,7 @@ var ( }, }, { Name: "daemonsets", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -704,7 +703,7 @@ var ( }, }, { Name: "pods", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "terminal", Rules: []v1.PolicyRule{ { @@ -736,7 +735,7 @@ var ( }, { Name: "services", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -778,7 +777,7 @@ var ( }, { Name: "internet", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -820,7 +819,7 @@ var ( { Name: "routes", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -860,7 +859,7 @@ var ( }, }, { Name: "volumes", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -900,7 +899,7 @@ var ( }, }, { Name: "applications", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -954,7 +953,7 @@ var ( }, { Name: "jobs", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "list"}, @@ -987,7 +986,7 @@ var ( }, { Name: "cronjobs", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "list"}, @@ -1020,7 +1019,7 @@ var ( }, { Name: "secrets", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "list"}, @@ -1053,7 +1052,7 @@ var ( }, { Name: "configmaps", - Actions: []models.Action{ + Actions: []iam.Action{ {Name: "view", Rules: []v1.PolicyRule{ { Verbs: []string{"get", "list"}, diff --git a/pkg/models/iam/types.go b/pkg/models/iam/types.go new file mode 100644 index 000000000..d70f8b31c --- /dev/null +++ b/pkg/models/iam/types.go @@ -0,0 +1,61 @@ +/* + * + * Copyright 2020 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 ( + "k8s.io/api/rbac/v1" + "time" +) + +const ( + ConfigPath = "/etc/kubesphere/iam" + KindTokenReview = "TokenReview" +) + +type Action struct { + Name string `json:"name"` + Rules []v1.PolicyRule `json:"rules"` +} + +type Rule struct { + Name string `json:"name"` + Actions []Action `json:"actions"` +} + +type SimpleRule struct { + Name string `json:"name" description:"rule name"` + Actions []string `json:"actions" description:"actions"` +} + +type RoleList struct { + ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"` + Roles []*v1.Role `json:"roles" description:"role list"` +} + +type Config struct { + adminEmail string + adminPassword string + authRateLimit string + maxAuthFailed int + authTimeInterval time.Duration + tokenIdleTimeout time.Duration + userInitFile string + enableMultiLogin bool + generateKubeConfig bool +} diff --git a/pkg/models/iam/utils.go b/pkg/models/iam/utils.go new file mode 100644 index 000000000..508e69683 --- /dev/null +++ b/pkg/models/iam/utils.go @@ -0,0 +1,180 @@ +/* + * + * Copyright 2020 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 ( + rbacv1 "k8s.io/api/rbac/v1" + "strings" +) + +func RulesMatchesRequired(rules []rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { + for _, rule := range rules { + if ruleMatchesRequired(rule, required) { + return true + } + } + return false +} + +func rulesMatchesAction(rules []rbacv1.PolicyRule, action Action) bool { + + for _, required := range action.Rules { + if !RulesMatchesRequired(rules, required) { + return false + } + } + + return true +} + +func ruleMatchesRequired(rule rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { + + if len(required.NonResourceURLs) == 0 { + for _, apiGroup := range required.APIGroups { + for _, resource := range required.Resources { + resources := strings.Split(resource, "/") + resource = resources[0] + var subsource string + if len(resources) > 1 { + subsource = resources[1] + } + + if len(required.ResourceNames) == 0 { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) { + return false + } + } + } else { + for _, resourceName := range required.ResourceNames { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) { + return false + } + } + } + } + } + } + } else { + for _, apiGroup := range required.APIGroups { + for _, nonResourceURL := range required.NonResourceURLs { + for _, verb := range required.Verbs { + if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) { + return false + } + } + } + } + } + return true +} + +func ruleMatchesResources(rule rbacv1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool { + + if resource == "" { + return false + } + + if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, rbacv1.ResourceAll) { + return false + } + + if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) { + return false + } + + combinedResource := resource + + if subresource != "" { + combinedResource = combinedResource + "/" + subresource + } + + for _, res := range rule.Resources { + + // match "*" + if res == rbacv1.ResourceAll || res == combinedResource { + return true + } + + // match "*/subresource" + if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") { + return true + } + // match "resource/*" + if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") { + return true + } + } + + return false +} + +func ruleMatchesRequest(rule rbacv1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool { + + if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, rbacv1.VerbAll) { + return false + } + + if nonResourceURL == "" { + return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName) + } else { + return ruleMatchesNonResource(rule, nonResourceURL) + } +} + +func ruleMatchesNonResource(rule rbacv1.PolicyRule, nonResourceURL string) bool { + + if nonResourceURL == "" { + return false + } + + for _, spec := range rule.NonResourceURLs { + if pathMatches(nonResourceURL, spec) { + return true + } + } + + return false +} + +func pathMatches(path, spec string) bool { + // Allow wildcard match + if spec == "*" { + return true + } + // Allow exact match + if spec == path { + return true + } + // Allow a trailing * subpath match + if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) { + return true + } + return false +} + +func hasString(slice []string, value string) bool { + for _, s := range slice { + if s == value { + return true + } + } + return false +} diff --git a/pkg/models/openpitrix/application/applications.go b/pkg/models/openpitrix/applications.go similarity index 69% rename from pkg/models/openpitrix/application/applications.go rename to pkg/models/openpitrix/applications.go index 02e68097f..7eb726b63 100644 --- a/pkg/models/openpitrix/application/applications.go +++ b/pkg/models/openpitrix/applications.go @@ -1,21 +1,21 @@ /* - - 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 application + * + * Copyright 2020 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 openpitrix import ( "fmt" @@ -26,46 +26,39 @@ import ( "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/informers" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/openpitrix/utils" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) -type Interface interface { - List(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) - Get(namespace, clusterID string) (*Application, error) - Create(namespace string, request types.CreateClusterRequest) error - Patch(request types.ModifyClusterAttributesRequest) error - Delete(id string) error +type ApplicationInterface interface { + ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) + DescribeApplication(namespace, clusterId string) (*Application, error) + CreateApplication(namespace string, request CreateClusterRequest) error + ModifyApplication(request ModifyClusterAttributesRequest) error + DeleteApplication(id string) error } type applicationOperator struct { informers informers.SharedInformerFactory - opClient pb.ClusterManagerClient + opClient openpitrix.Client } -func NewApplicaitonOperator(informers informers.SharedInformerFactory, client pb.ClusterManagerClient) Interface { - return &applicationOperator{ - informers: informers, - opClient: client, - } +func newApplicationOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) ApplicationInterface { + return &applicationOperator{informers: informers, opClient: opClient} } type Application struct { Name string `json:"name" description:"application name"` - Cluster *types.Cluster `json:"cluster,omitempty" description:"application cluster info"` - Version *types.AppVersion `json:"version,omitempty" description:"application template version info"` - App *types.App `json:"app,omitempty" description:"application template info"` + Cluster *Cluster `json:"cluster,omitempty" description:"application cluster info"` + Version *AppVersion `json:"version,omitempty" description:"application template version info"` + App *App `json:"app,omitempty" description:"application template info"` WorkLoads *workLoads `json:"workloads,omitempty" description:"application workloads"` Services []v1.Service `json:"services,omitempty" description:"application services"` Ingresses []v1beta1.Ingress `json:"ingresses,omitempty" description:"application ingresses"` @@ -77,35 +70,30 @@ type workLoads struct { Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty" description:"daemonset list"` } -func (c *applicationOperator) List(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *applicationOperator) ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) { describeClustersRequest := &pb.DescribeClustersRequest{ Limit: uint32(limit), Offset: uint32(offset)} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword} } - if runtimeId := conditions.Match["runtime_id"]; runtimeId != "" { + if runtimeId := conditions.Match[RuntimeId]; runtimeId != "" { describeClustersRequest.RuntimeId = []string{runtimeId} } - if appId := conditions.Match["app_id"]; appId != "" { + if appId := conditions.Match[AppId]; appId != "" { describeClustersRequest.AppId = []string{appId} } - if versionId := conditions.Match["version_id"]; versionId != "" { + if versionId := conditions.Match[VersionId]; versionId != "" { describeClustersRequest.VersionId = []string{versionId} } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { describeClustersRequest.Status = strings.Split(status, "|") } if orderBy != "" { describeClustersRequest.SortKey = &wrappers.StringValue{Value: orderBy} } - describeClustersRequest.Reverse = &wrappers.BoolValue{Value: !reverse} - resp, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), describeClustersRequest) + describeClustersRequest.Reverse = &wrappers.BoolValue{Value: reverse} + resp, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), describeClustersRequest) if err != nil { klog.Errorln(err) return nil, err @@ -125,34 +113,29 @@ func (c *applicationOperator) List(conditions *params.Conditions, limit, offset } func (c *applicationOperator) describeApplication(cluster *pb.Cluster) (*Application, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } var app Application app.Name = cluster.Name.Value - app.Cluster = utils.ConvertCluster(cluster) - versionInfo, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}}) + app.Cluster = convertCluster(cluster) + versionInfo, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}}) if err != nil { klog.Errorln(err) return nil, err } if len(versionInfo.AppVersionSet) > 0 { - app.Version = utils.ConvertAppVersion(versionInfo.AppVersionSet[0]) + app.Version = convertAppVersion(versionInfo.AppVersionSet[0]) } - appInfo, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1}) + appInfo, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1}) if err != nil { klog.Errorln(err) return nil, err } if len(appInfo.AppSet) > 0 { - app.App = utils.ConvertApp(appInfo.GetAppSet()[0]) + app.App = convertApp(appInfo.GetAppSet()[0]) } return &app, nil } -func (c *applicationOperator) Get(namespace string, clusterId string) (*Application, error) { +func (c *applicationOperator) DescribeApplication(namespace string, clusterId string) (*Application, error) { clusters, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1}) @@ -280,7 +263,7 @@ func (c *applicationOperator) getLabels(namespace string, workloads *workLoads) return &workloadLabels } -func (c *applicationOperator) isExist(svcs []v1.Service, svc v1.Service) bool { +func (c *applicationOperator) isExist(svcs []v1.Service, svc *v1.Service) bool { for _, item := range svcs { if item.Name == svc.Name && item.Namespace == svc.Namespace { return true @@ -293,17 +276,16 @@ func (c *applicationOperator) getSvcs(namespace string, workLoadLabels *[]map[st if len(*workLoadLabels) == 0 { return nil } - k8sClient := cs.ClientSets().K8s().Kubernetes() var services []v1.Service for _, label := range *workLoadLabels { - labelSelector := labels.Set(label).AsSelector().String() - svcs, err := k8sClient.CoreV1().Services(namespace).List(metav1.ListOptions{LabelSelector: labelSelector}) + labelSelector := labels.Set(label).AsSelector() + svcs, err := c.informers.Core().V1().Services().Lister().Services(namespace).List(labelSelector) if err != nil { klog.Errorf("get app's svc failed, reason: %v", err) } - for _, item := range svcs.Items { + for _, item := range svcs { if !c.isExist(services, item) { - services = append(services, item) + services = append(services, *item) } } } @@ -352,7 +334,7 @@ func (c *applicationOperator) getIng(namespace string, services []v1.Service) [] return ings } -func (c *applicationOperator) Create(namespace string, request types.CreateClusterRequest) error { +func (c *applicationOperator) CreateApplication(namespace string, request CreateClusterRequest) error { ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace) if err != nil { klog.Error(err) @@ -365,14 +347,7 @@ func (c *applicationOperator) Create(namespace string, request types.CreateClust return fmt.Errorf("runtime not init: namespace %s", namespace) } - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - - _, err = client.Cluster().CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{ + _, err = c.opClient.CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{ AppId: &wrappers.StringValue{Value: request.AppId}, VersionId: &wrappers.StringValue{Value: request.VersionId}, RuntimeId: &wrappers.StringValue{Value: request.RuntimeId}, @@ -387,13 +362,7 @@ func (c *applicationOperator) Create(namespace string, request types.CreateClust return nil } -func (c *applicationOperator) Patch(request types.ModifyClusterAttributesRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } +func (c *applicationOperator) ModifyApplication(request ModifyClusterAttributesRequest) error { modifyClusterAttributesRequest := &pb.ModifyClusterAttributesRequest{ClusterId: &wrappers.StringValue{Value: request.ClusterID}} if request.Name != nil { @@ -403,16 +372,17 @@ func (c *applicationOperator) Patch(request types.ModifyClusterAttributesRequest modifyClusterAttributesRequest.Description = &wrappers.StringValue{Value: *request.Description} } - _, err = op.Cluster().ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest) + _, err := c.opClient.ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest) if err != nil { klog.Errorln(err) return err } + return nil } -func (c *applicationOperator) Delete(clusterId string) error { +func (c *applicationOperator) DeleteApplication(clusterId string) error { _, err := c.opClient.DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}}) if err != nil { diff --git a/pkg/models/openpitrix/applications_test.go b/pkg/models/openpitrix/applications_test.go new file mode 100644 index 000000000..37dae83f0 --- /dev/null +++ b/pkg/models/openpitrix/applications_test.go @@ -0,0 +1,114 @@ +/* + * + * Copyright 2020 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 openpitrix + +import ( + "github.com/golang/mock/gomock" + "github.com/golang/protobuf/ptypes/wrappers" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" + "testing" +) + +func namespacesToRuntimeObjects(namespaces ...*v1.Namespace) []runtime.Object { + var objs []runtime.Object + for _, deploy := range namespaces { + objs = append(objs, deploy) + } + + return objs +} + +func TestApplicationOperator_CreateApplication(t *testing.T) { + tests := []struct { + description string + existNamespaces []*v1.Namespace + targetNamespace string + createClusterRequest CreateClusterRequest + expected error + }{ + { + description: "create application test", + existNamespaces: []*v1.Namespace{{ + ObjectMeta: metav1.ObjectMeta{Name: "test", Annotations: map[string]string{openpitrix.RuntimeAnnotationKey: "runtime-ncafface"}}, + }}, + targetNamespace: "test", + createClusterRequest: CreateClusterRequest{ + Conf: "app-agwerl", + RuntimeId: "runtime-ncafface", + VersionId: "version-acklmalkds", + Username: "system", + }, + expected: nil, + }, + { + description: "create application test2", + existNamespaces: []*v1.Namespace{}, + targetNamespace: "test2", + createClusterRequest: CreateClusterRequest{ + Conf: "app-agwerl", + RuntimeId: "runtime-ncafface", + VersionId: "version-acklmalkds", + Username: "system", + }, + expected: errors.NewNotFound(schema.GroupResource{Group: "", Resource: "namespace"}, "test2"), + }, + } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + for _, test := range tests { + + op := openpitrix.NewMockClient(ctrl) + objs := namespacesToRuntimeObjects(test.existNamespaces...) + k8s := fake.NewSimpleClientset(objs...) + informer := informers.NewSharedInformerFactory(k8s, 0) + stopChan := make(chan struct{}, 0) + informer.Core().V1().Namespaces().Lister() + informer.Start(stopChan) + informer.WaitForCacheSync(stopChan) + + applicationOperator := newApplicationOperator(informer, op) + + // setup expect response + // op.EXPECT().CreateCluster(gomock.Any(), gomock.Any()).Return(&pb.CreateClusterResponse{}, nil).AnyTimes() + op.EXPECT().CreateCluster(openpitrix.ContextWithUsername(test.createClusterRequest.Username), &pb.CreateClusterRequest{ + AppId: &wrappers.StringValue{Value: test.createClusterRequest.AppId}, + VersionId: &wrappers.StringValue{Value: test.createClusterRequest.VersionId}, + RuntimeId: &wrappers.StringValue{Value: test.createClusterRequest.RuntimeId}, + Conf: &wrappers.StringValue{Value: test.createClusterRequest.Conf}, + }).Return(&pb.CreateClusterResponse{}, nil).AnyTimes() + + t.Run(test.description, func(t *testing.T) { + + err := applicationOperator.CreateApplication(test.targetNamespace, test.createClusterRequest) + + if err != nil && err.Error() != test.expected.Error() { + t.Error(err) + } + }) + } +} diff --git a/pkg/models/openpitrix/app/apps.go b/pkg/models/openpitrix/apps.go similarity index 61% rename from pkg/models/openpitrix/app/apps.go rename to pkg/models/openpitrix/apps.go index 6d961d8c3..d96bc5370 100644 --- a/pkg/models/openpitrix/app/apps.go +++ b/pkg/models/openpitrix/apps.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 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. @@ -16,7 +16,7 @@ * / */ -package app +package openpitrix import ( "github.com/go-openapi/strfmt" @@ -25,41 +25,58 @@ import ( "google.golang.org/grpc/status" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/openpitrix/utils" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) -const ( - BuiltinRepoId = "repo-helm" - StatusActive = "active" -) +type AppTemplateInterface interface { + ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + DescribeApp(id string) (*App, error) + DeleteApp(id string) error + CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) + ModifyApp(appId string, request *ModifyAppRequest) error + DeleteAppVersion(id string) error + ModifyAppVersion(id string, request *ModifyAppVersionRequest) error + DescribeAppVersion(id string) (*AppVersion, error) + CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) + ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) + GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) + DoAppAction(appId string, request *ActionRequest) error + DoAppVersionAction(versionId string, request *ActionRequest) error + GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) + ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) +} -func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type appTemplateOperator struct { + opClient openpitrix.Client +} + +func newAppTemplateOperator(opClient openpitrix.Client) AppTemplateInterface { + return &appTemplateOperator{ + opClient: opClient, } +} + +func (c *appTemplateOperator) ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppsRequest := &pb.DescribeAppsRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { describeAppsRequest.SearchWord = &wrappers.StringValue{Value: keyword} } - if appId := conditions.Match["app_id"]; appId != "" { + if appId := conditions.Match[AppId]; appId != "" { describeAppsRequest.AppId = strings.Split(appId, "|") } - if isv := conditions.Match["isv"]; isv != "" { + if isv := conditions.Match[ISV]; isv != "" { describeAppsRequest.Isv = strings.Split(isv, "|") } - if categoryId := conditions.Match["category_id"]; categoryId != "" { + if categoryId := conditions.Match[CategoryId]; categoryId != "" { describeAppsRequest.CategoryId = strings.Split(categoryId, "|") } - if repoId := conditions.Match["repo"]; repoId != "" { + if repoId := conditions.Match[RepoId]; repoId != "" { // hard code, app template in built-in repo has no repo_id attribute if repoId == BuiltinRepoId { describeAppsRequest.RepoId = []string{"\u0000"} @@ -67,7 +84,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit describeAppsRequest.RepoId = strings.Split(repoId, "|") } } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { describeAppsRequest.Status = strings.Split(status, "|") } if orderBy != "" { @@ -76,7 +93,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit describeAppsRequest.Reverse = &wrappers.BoolValue{Value: reverse} describeAppsRequest.Limit = uint32(limit) describeAppsRequest.Offset = uint32(offset) - resp, err := client.App().DescribeApps(openpitrix.SystemContext(), describeAppsRequest) + resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), describeAppsRequest) if err != nil { klog.Error(err) return nil, err @@ -85,19 +102,14 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit items := make([]interface{}, 0) for _, item := range resp.AppSet { - items = append(items, utils.ConvertApp(item)) + items = append(items, convertApp(item)) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func DescribeApp(id string) (*types.App, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{ +func (c *appTemplateOperator) DescribeApp(id string) (*App, error) { + resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{ AppId: []string{id}, Limit: 1, }) @@ -106,10 +118,10 @@ func DescribeApp(id string) (*types.App, error) { return nil, err } - var app *types.App + var app *App if len(resp.AppSet) > 0 { - app = utils.ConvertApp(resp.AppSet[0]) + app = convertApp(resp.AppSet[0]) return app, nil } else { err := status.New(codes.NotFound, "resource not found").Err() @@ -118,13 +130,8 @@ func DescribeApp(id string) (*types.App, error) { } } -func DeleteApp(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.App().DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{ +func (c *appTemplateOperator) DeleteApp(id string) error { + _, err := c.opClient.DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{ AppId: []string{id}, }) if err != nil { @@ -134,12 +141,7 @@ func DeleteApp(id string) error { return nil } -func CreateApp(request *types.CreateAppRequest) (*types.CreateAppResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { createAppRequest := &pb.CreateAppRequest{ Name: &wrappers.StringValue{Value: request.Name}, VersionType: &wrappers.StringValue{Value: request.VersionType}, @@ -154,25 +156,18 @@ func CreateApp(request *types.CreateAppRequest) (*types.CreateAppResponse, error if request.Isv != "" { createAppRequest.Isv = &wrappers.StringValue{Value: request.Isv} } - resp, err := op.App().CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest) + resp, err := c.opClient.CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest) if err != nil { klog.Error(err) return nil, err } - return &types.CreateAppResponse{ + return &CreateAppResponse{ AppID: resp.GetAppId().GetValue(), VersionID: resp.GetVersionId().GetValue(), }, nil } -func PatchApp(appId string, request *types.ModifyAppRequest) error { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) ModifyApp(appId string, request *ModifyAppRequest) error { // upload app attachment if request.AttachmentContent != nil { uploadAttachmentRequest := &pb.UploadAppAttachmentRequest{ @@ -186,7 +181,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error { uploadAttachmentRequest.Sequence = &wrappers.UInt32Value{Value: uint32(*request.Sequence)} } - _, err := client.App().UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest) + _, err := c.opClient.UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest) if err != nil { klog.Error(err) @@ -229,7 +224,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error { patchAppRequest.Tos = &wrappers.StringValue{Value: *request.Tos} } - _, err = client.App().ModifyApp(openpitrix.SystemContext(), patchAppRequest) + _, err := c.opClient.ModifyApp(openpitrix.SystemContext(), patchAppRequest) if err != nil { klog.Error(err) @@ -239,12 +234,7 @@ func PatchApp(appId string, request *types.ModifyAppRequest) error { return nil } -func CreateAppVersion(request *types.CreateAppVersionRequest) (*types.CreateAppVersionResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) { createAppVersionRequest := &pb.CreateAppVersionRequest{ AppId: &wrappers.StringValue{Value: request.AppId}, Name: &wrappers.StringValue{Value: request.Name}, @@ -256,24 +246,17 @@ func CreateAppVersion(request *types.CreateAppVersionRequest) (*types.CreateAppV createAppVersionRequest.Package = &wrappers.BytesValue{Value: request.Package} } - resp, err := op.App().CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest) + resp, err := c.opClient.CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest) if err != nil { klog.Error(err) return nil, err } - return &types.CreateAppVersionResponse{ + return &CreateAppVersionResponse{ VersionId: resp.GetVersionId().GetValue(), }, nil } -func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePackageResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) { r := &pb.ValidatePackageRequest{} if request.VersionPackage != nil { @@ -283,14 +266,14 @@ func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePack r.VersionType = request.VersionType } - resp, err := client.App().ValidatePackage(openpitrix.SystemContext(), r) + resp, err := c.opClient.ValidatePackage(openpitrix.SystemContext(), r) if err != nil { klog.Error(err) return nil, err } - result := &types.ValidatePackageResponse{} + result := &ValidatePackageResponse{} if resp.Error != nil { result.Error = resp.Error.Value @@ -316,13 +299,8 @@ func ValidatePackage(request *types.ValidatePackageRequest) (*types.ValidatePack return result, nil } -func DeleteAppVersion(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.App().DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{ +func (c *appTemplateOperator) DeleteAppVersion(id string) error { + _, err := c.opClient.DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{ VersionId: &wrappers.StringValue{Value: id}, }) if err != nil { @@ -332,12 +310,7 @@ func DeleteAppVersion(id string) error { return nil } -func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *appTemplateOperator) ModifyAppVersion(id string, request *ModifyAppVersionRequest) error { modifyAppVersionRequest := &pb.ModifyAppVersionRequest{ VersionId: &wrappers.StringValue{Value: id}, } @@ -355,7 +328,7 @@ func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error { modifyAppVersionRequest.PackageFiles = request.PackageFiles } - _, err = op.App().ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest) + _, err := c.opClient.ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest) if err != nil { klog.Error(err) return err @@ -363,13 +336,8 @@ func PatchAppVersion(id string, request *types.ModifyAppVersionRequest) error { return nil } -func DescribeAppVersion(id string) (*types.AppVersion, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ +func (c *appTemplateOperator) DescribeAppVersion(id string) (*AppVersion, error) { + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ VersionId: []string{id}, Limit: 1, }) @@ -378,10 +346,10 @@ func DescribeAppVersion(id string) (*types.AppVersion, error) { return nil, err } - var app *types.AppVersion + var app *AppVersion if len(resp.AppVersionSet) > 0 { - app = utils.ConvertAppVersion(resp.AppVersionSet[0]) + app = convertAppVersion(resp.AppVersionSet[0]) return app, nil } else { err := status.New(codes.NotFound, "resource not found").Err() @@ -390,13 +358,8 @@ func DescribeAppVersion(id string) (*types.AppVersion, error) { } } -func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{ +func (c *appTemplateOperator) GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) { + resp, err := c.opClient.GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) if err != nil { @@ -404,7 +367,7 @@ func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageR return nil, err } - app := &types.GetAppVersionPackageResponse{ + app := &GetAppVersionPackageResponse{ AppId: appId, VersionId: versionId, } @@ -416,21 +379,14 @@ func GetAppVersionPackage(appId, versionId string) (*types.GetAppVersionPackageR return app, nil } -func DoAppAction(appId string, request *types.ActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) DoAppAction(appId string, request *ActionRequest) error { switch request.Action { - case "recover": + case ActionRecover: // TODO openpitrix need to implement app recover interface - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ AppId: []string{appId}, - Status: []string{"suspended"}, + Status: []string{StatusSuspended}, Limit: 200, Offset: 0, }) @@ -440,7 +396,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error { } for _, version := range resp.AppVersionSet { - _, err = op.App().RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{ + _, err = c.opClient.RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{ VersionId: version.VersionId, }) if err != nil { @@ -449,11 +405,11 @@ func DoAppAction(appId string, request *types.ActionRequest) error { } } - case "suspend": + case ActionSuspend: // TODO openpitrix need to implement app suspend interface - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ AppId: []string{appId}, - Status: []string{"active"}, + Status: []string{StatusActive}, Limit: 200, Offset: 0, }) @@ -462,7 +418,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error { return err } for _, version := range resp.AppVersionSet { - _, err = op.App().SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{ + _, err = c.opClient.SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{ VersionId: version.VersionId, }) @@ -473,7 +429,7 @@ func DoAppAction(appId string, request *types.ActionRequest) error { } default: - err = status.New(codes.InvalidArgument, "action not support").Err() + err := status.New(codes.InvalidArgument, "action not support").Err() klog.Error(err) return err } @@ -481,42 +437,36 @@ func DoAppAction(appId string, request *types.ActionRequest) error { return nil } -func DoAppVersionAction(versionId string, request *types.ActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) DoAppVersionAction(versionId string, request *ActionRequest) error { + var err error switch request.Action { - case "cancel": - _, err = op.App().CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{ + case ActionCancel: + _, err = c.opClient.CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "pass": - _, err = op.App().AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{ + case ActionPass: + _, err = c.opClient.AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "recover": - _, err = op.App().RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{ + case ActionRecover: + _, err = c.opClient.RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "reject": - _, err = op.App().AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{ + case ActionReject: + _, err = c.opClient.AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, Message: &wrappers.StringValue{Value: request.Message}, }) - case "submit": - _, err = op.App().SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{ + case ActionSubmit: + _, err = c.opClient.SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "suspend": - _, err = op.App().SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{ + case ActionSuspend: + _, err = c.opClient.SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "release": - _, err = op.App().ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{ + case ActionRelease: + _, err = c.opClient.ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) default: @@ -531,12 +481,7 @@ func DoAppVersionAction(versionId string, request *types.ActionRequest) error { return nil } -func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesRequest) (*types.GetAppVersionPackageFilesResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) { getAppVersionPackageFilesRequest := &pb.GetAppVersionPackageFilesRequest{ VersionId: &wrappers.StringValue{Value: versionId}, } @@ -544,13 +489,13 @@ func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesReque getAppVersionPackageFilesRequest.Files = request.Files } - resp, err := op.App().GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest) + resp, err := c.opClient.GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest) if err != nil { klog.Error(err) return nil, err } - version := &types.GetAppVersionPackageFilesResponse{ + version := &GetAppVersionPackageFilesResponse{ VersionId: versionId, } @@ -564,14 +509,7 @@ func GetAppVersionFiles(versionId string, request *types.GetAppVersionFilesReque return version, nil } -func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionAudits := &pb.DescribeAppVersionAuditsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -589,10 +527,10 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse if orderBy != "" { describeAppVersionAudits.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionAudits.Limit = uint32(limit) describeAppVersionAudits.Offset = uint32(offset) - resp, err := client.App().DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits) + resp, err := c.opClient.DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits) if err != nil { klog.Error(err) @@ -602,21 +540,14 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse items := make([]interface{}, 0) for _, item := range resp.AppVersionAuditSet { - appVersion := utils.ConvertAppVersionAudit(item) + appVersion := convertAppVersionAudit(item) items = append(items, appVersion) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionReviews := &pb.DescribeAppVersionReviewsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -628,11 +559,11 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers if orderBy != "" { describeAppVersionReviews.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionReviews.Limit = uint32(limit) describeAppVersionReviews.Offset = uint32(offset) // TODO icon is needed - resp, err := client.App().DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews) + resp, err := c.opClient.DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews) if err != nil { klog.Error(err) @@ -642,21 +573,14 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers items := make([]interface{}, 0) for _, item := range resp.AppVersionReviewSet { - appVersion := utils.ConvertAppVersionReview(item) + appVersion := convertAppVersionReview(item) items = append(items, appVersion) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionsRequest := &pb.DescribeAppVersionsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -671,10 +595,10 @@ func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool if orderBy != "" { describeAppVersionsRequest.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionsRequest.Limit = uint32(limit) describeAppVersionsRequest.Offset = uint32(offset) - resp, err := client.App().DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest) + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest) if err != nil { klog.Error(err) @@ -684,7 +608,7 @@ func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool items := make([]interface{}, 0) for _, item := range resp.AppVersionSet { - appVersion := utils.ConvertAppVersion(item) + appVersion := convertAppVersion(item) items = append(items, appVersion) } diff --git a/pkg/models/openpitrix/attachment/attachments.go b/pkg/models/openpitrix/attachments.go similarity index 65% rename from pkg/models/openpitrix/attachment/attachments.go rename to pkg/models/openpitrix/attachments.go index 0d1c25c6f..aec779ca0 100644 --- a/pkg/models/openpitrix/attachment/attachments.go +++ b/pkg/models/openpitrix/attachments.go @@ -16,26 +16,32 @@ * / */ -package attachment +package openpitrix import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/openpitrix/utils" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" ) -func DescribeAttachment(id string) (*types.Attachment, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type AttachmentInterface interface { + DescribeAttachment(id string) (*Attachment, error) +} + +type attachmentOperator struct { + opClient openpitrix.Client +} + +func newAttachmentOperator(opClient openpitrix.Client) AttachmentInterface { + return &attachmentOperator{ + opClient: opClient, } - resp, err := op.Attachment().GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{ +} + +func (c *attachmentOperator) DescribeAttachment(id string) (*Attachment, error) { + resp, err := c.opClient.GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{ AttachmentId: []string{id}, }) if err != nil { @@ -43,7 +49,7 @@ func DescribeAttachment(id string) (*types.Attachment, error) { return nil, err } if len(resp.Attachments) > 0 { - return utils.ConvertAttachment(resp.Attachments[id]), nil + return convertAttachment(resp.Attachments[id]), nil } else { err := status.New(codes.NotFound, "resource not found").Err() klog.Error(err) diff --git a/pkg/models/openpitrix/category/categories.go b/pkg/models/openpitrix/categories.go similarity index 59% rename from pkg/models/openpitrix/category/categories.go rename to pkg/models/openpitrix/categories.go index 908233855..0a238e1c0 100644 --- a/pkg/models/openpitrix/category/categories.go +++ b/pkg/models/openpitrix/categories.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 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. @@ -16,7 +16,7 @@ * / */ -package category +package openpitrix import ( "github.com/golang/protobuf/ptypes/wrappers" @@ -24,20 +24,30 @@ import ( "google.golang.org/grpc/status" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/openpitrix/utils" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" ) -func CreateCategory(request *types.CreateCategoryRequest) (*types.CreateCategoryResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type CategoryInterface interface { + CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) + DeleteCategory(id string) error + ModifyCategory(id string, request *ModifyCategoryRequest) error + ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + DescribeCategory(id string) (*Category, error) +} + +type categoryOperator struct { + opClient openpitrix.Client +} + +func newCategoryOperator(opClient openpitrix.Client) CategoryInterface { + return &categoryOperator{ + opClient: opClient, } +} + +func (c *categoryOperator) CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) { r := &pb.CreateCategoryRequest{ Name: &wrappers.StringValue{Value: request.Name}, Locale: &wrappers.StringValue{Value: request.Locale}, @@ -47,23 +57,18 @@ func CreateCategory(request *types.CreateCategoryRequest) (*types.CreateCategory r.Icon = &wrappers.BytesValue{Value: request.Icon} } - resp, err := op.Category().CreateCategory(openpitrix.SystemContext(), r) + resp, err := c.opClient.CreateCategory(openpitrix.SystemContext(), r) if err != nil { klog.Error(err) return nil, err } - return &types.CreateCategoryResponse{ + return &CreateCategoryResponse{ CategoryId: resp.GetCategoryId().GetValue(), }, nil } -func DeleteCategory(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.Category().DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{ +func (c *categoryOperator) DeleteCategory(id string) error { + _, err := c.opClient.DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{ CategoryId: []string{id}, }) if err != nil { @@ -73,12 +78,7 @@ func DeleteCategory(id string) error { return nil } -func PatchCategory(id string, request *types.ModifyCategoryRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *categoryOperator) ModifyCategory(id string, request *ModifyCategoryRequest) error { modifyCategoryRequest := &pb.ModifyCategoryRequest{ CategoryId: &wrappers.StringValue{Value: id}, } @@ -95,7 +95,7 @@ func PatchCategory(id string, request *types.ModifyCategoryRequest) error { modifyCategoryRequest.Icon = &wrappers.BytesValue{Value: request.Icon} } - _, err = op.Category().ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest) + _, err := c.opClient.ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest) if err != nil { klog.Error(err) return err @@ -103,13 +103,8 @@ func PatchCategory(id string, request *types.ModifyCategoryRequest) error { return nil } -func DescribeCategory(id string) (*types.Category, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.Category().DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{ +func (c *categoryOperator) DescribeCategory(id string) (*Category, error) { + resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{ CategoryId: []string{id}, Limit: 1, }) @@ -118,10 +113,10 @@ func DescribeCategory(id string) (*types.Category, error) { return nil, err } - var category *types.Category + var category *Category if len(resp.CategorySet) > 0 { - category = utils.ConvertCategory(resp.CategorySet[0]) + category = convertCategory(resp.CategorySet[0]) return category, nil } else { err := status.New(codes.NotFound, "resource not found").Err() @@ -130,26 +125,19 @@ func DescribeCategory(id string) (*types.Category, error) { } } -func ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *categoryOperator) ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { req := &pb.DescribeCategoriesRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { req.SearchWord = &wrappers.StringValue{Value: keyword} } if orderBy != "" { req.SortKey = &wrappers.StringValue{Value: orderBy} } - req.Reverse = &wrappers.BoolValue{Value: !reverse} + req.Reverse = &wrappers.BoolValue{Value: reverse} req.Limit = uint32(limit) req.Offset = uint32(offset) - resp, err := client.Category().DescribeCategories(openpitrix.SystemContext(), req) + resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), req) if err != nil { klog.Error(err) return nil, err @@ -158,7 +146,7 @@ func ListCategories(conditions *params.Conditions, orderBy string, reverse bool, items := make([]interface{}, 0) for _, item := range resp.CategorySet { - items = append(items, utils.ConvertCategory(item)) + items = append(items, convertCategory(item)) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil diff --git a/pkg/models/openpitrix/interface.go b/pkg/models/openpitrix/interface.go new file mode 100644 index 000000000..15b133a47 --- /dev/null +++ b/pkg/models/openpitrix/interface.go @@ -0,0 +1,49 @@ +/* + * + * Copyright 2020 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 openpitrix + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" +) + +type Interface interface { + ApplicationInterface + AppTemplateInterface + AttachmentInterface + CategoryInterface + RepoInterface +} +type openpitrixOperator struct { + ApplicationInterface + AppTemplateInterface + AttachmentInterface + CategoryInterface + RepoInterface +} + +func NewOpenpitrixOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) Interface { + return &openpitrixOperator{ + ApplicationInterface: newApplicationOperator(informers, opClient), + AppTemplateInterface: newAppTemplateOperator(opClient), + AttachmentInterface: newAttachmentOperator(opClient), + CategoryInterface: newCategoryOperator(opClient), + RepoInterface: newRepoOperator(opClient), + } +} diff --git a/pkg/models/openpitrix/repo/repos.go b/pkg/models/openpitrix/repos.go similarity index 64% rename from pkg/models/openpitrix/repo/repos.go rename to pkg/models/openpitrix/repos.go index 07b27cbb1..43900df91 100644 --- a/pkg/models/openpitrix/repo/repos.go +++ b/pkg/models/openpitrix/repos.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 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. @@ -16,7 +16,7 @@ * / */ -package repo +package openpitrix import ( "fmt" @@ -25,21 +25,34 @@ import ( "google.golang.org/grpc/status" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" - "kubesphere.io/kubesphere/pkg/models/openpitrix/utils" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) -func CreateRepo(request *types.CreateRepoRequest) (*types.CreateRepoResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type RepoInterface interface { + CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) + DeleteRepo(id string) error + ModifyRepo(id string, request *ModifyRepoRequest) error + DescribeRepo(id string) (*Repo, error) + ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) + DoRepoAction(repoId string, request *RepoActionRequest) error + ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) +} + +type repoOperator struct { + opClient openpitrix.Client +} + +func newRepoOperator(opClient openpitrix.Client) RepoInterface { + return &repoOperator{ + opClient: opClient, } +} + +func (c *repoOperator) CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { createRepoRequest := &pb.CreateRepoRequest{ Name: &wrappers.StringValue{Value: request.Name}, Description: &wrappers.StringValue{Value: request.Description}, @@ -58,23 +71,18 @@ func CreateRepo(request *types.CreateRepoRequest) (*types.CreateRepoResponse, er createRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} } - resp, err := op.Repo().CreateRepo(openpitrix.SystemContext(), createRepoRequest) + resp, err := c.opClient.CreateRepo(openpitrix.SystemContext(), createRepoRequest) if err != nil { klog.Error(err) return nil, err } - return &types.CreateRepoResponse{ + return &CreateRepoResponse{ RepoID: resp.GetRepoId().GetValue(), }, nil } -func DeleteRepo(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.Repo().DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{ +func (c *repoOperator) DeleteRepo(id string) error { + _, err := c.opClient.DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{ RepoId: []string{id}, }) if err != nil { @@ -84,12 +92,7 @@ func DeleteRepo(id string) error { return nil } -func PatchRepo(id string, request *types.ModifyRepoRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *repoOperator) ModifyRepo(id string, request *ModifyRepoRequest) error { modifyRepoRequest := &pb.ModifyRepoRequest{ RepoId: &wrappers.StringValue{Value: id}, } @@ -126,7 +129,7 @@ func PatchRepo(id string, request *types.ModifyRepoRequest) error { modifyRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} } - _, err = op.Repo().ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest) + _, err := c.opClient.ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest) if err != nil { klog.Error(err) return err @@ -134,13 +137,8 @@ func PatchRepo(id string, request *types.ModifyRepoRequest) error { return nil } -func DescribeRepo(id string) (*types.Repo, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.Repo().DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{ +func (c *repoOperator) DescribeRepo(id string) (*Repo, error) { + resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{ RepoId: []string{id}, Limit: 1, }) @@ -149,10 +147,10 @@ func DescribeRepo(id string) (*types.Repo, error) { return nil, err } - var repo *types.Repo + var repo *Repo if len(resp.RepoSet) > 0 { - repo = utils.ConvertRepo(resp.RepoSet[0]) + repo = convertRepo(resp.RepoSet[0]) return repo, nil } else { err := status.New(codes.NotFound, "resource not found").Err() @@ -161,41 +159,34 @@ func DescribeRepo(id string) (*types.Repo, error) { } } -func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *repoOperator) ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { req := &pb.DescribeReposRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { req.SearchWord = &wrappers.StringValue{Value: keyword} } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { req.Status = strings.Split(status, "|") } - if typeStr := conditions.Match["type"]; typeStr != "" { + if typeStr := conditions.Match[Type]; typeStr != "" { req.Type = strings.Split(typeStr, "|") } - if visibility := conditions.Match["visibility"]; visibility != "" { + if visibility := conditions.Match[Visibility]; visibility != "" { req.Visibility = strings.Split(visibility, "|") } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { req.Status = strings.Split(status, "|") } - if workspace := conditions.Match["workspace"]; workspace != "" { + if workspace := conditions.Match[WorkspaceLabel]; workspace != "" { req.Label = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", workspace)} } if orderBy != "" { req.SortKey = &wrappers.StringValue{Value: orderBy} } - req.Reverse = &wrappers.BoolValue{Value: !reverse} + req.Reverse = &wrappers.BoolValue{Value: reverse} req.Limit = uint32(limit) req.Offset = uint32(offset) - resp, err := client.Repo().DescribeRepos(openpitrix.SystemContext(), req) + resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), req) if err != nil { klog.Error(err) return nil, err @@ -204,21 +195,14 @@ func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limi items := make([]interface{}, 0) for _, item := range resp.RepoSet { - items = append(items, utils.ConvertRepo(item)) + items = append(items, convertRepo(item)) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ValidateRepo(request *types.ValidateRepoRequest) (*types.ValidateRepoResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - - resp, err := client.Repo().ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{ +func (c *repoOperator) ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) { + resp, err := c.opClient.ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{ Type: &wrappers.StringValue{Value: request.Type}, Credential: &wrappers.StringValue{Value: request.Credential}, Url: &wrappers.StringValue{Value: request.Url}, @@ -229,25 +213,20 @@ func ValidateRepo(request *types.ValidateRepoRequest) (*types.ValidateRepoRespon return nil, err } - return &types.ValidateRepoResponse{ + return &ValidateRepoResponse{ ErrorCode: int64(resp.ErrorCode), Ok: resp.Ok.Value, }, nil } -func DoRepoAction(repoId string, request *types.RepoActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - +func (c *repoOperator) DoRepoAction(repoId string, request *RepoActionRequest) error { + var err error switch request.Action { - case "index": + case ActionIndex: indexRepoRequest := &pb.IndexRepoRequest{ RepoId: &wrappers.StringValue{Value: repoId}, } - _, err := op.RepoIndexer().IndexRepo(openpitrix.SystemContext(), indexRepoRequest) + _, err := c.opClient.IndexRepo(openpitrix.SystemContext(), indexRepoRequest) if err != nil { klog.Error(err) @@ -262,13 +241,7 @@ func DoRepoAction(repoId string, request *types.RepoActionRequest) error { } } -func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *repoOperator) ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { describeRepoEventsRequest := &pb.DescribeRepoEventsRequest{ RepoId: []string{repoId}, } @@ -281,7 +254,7 @@ func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset describeRepoEventsRequest.Limit = uint32(limit) describeRepoEventsRequest.Offset = uint32(offset) - resp, err := op.RepoIndexer().DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest) + resp, err := c.opClient.DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest) if err != nil { klog.Error(err) @@ -291,7 +264,7 @@ func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset items := make([]interface{}, 0) for _, item := range resp.RepoEventSet { - items = append(items, utils.ConvertRepoEvent(item)) + items = append(items, convertRepoEvent(item)) } return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil diff --git a/pkg/models/openpitrix/types/types.go b/pkg/models/openpitrix/types.go similarity index 96% rename from pkg/models/openpitrix/types/types.go rename to pkg/models/openpitrix/types.go index 0cd8c1139..92f1e021e 100644 --- a/pkg/models/openpitrix/types/types.go +++ b/pkg/models/openpitrix/types.go @@ -1,4 +1,4 @@ -package types +package openpitrix import ( "github.com/go-openapi/strfmt" @@ -835,3 +835,30 @@ type ModifyClusterAttributesRequest struct { // cluster name Name *string `json:"name,omitempty"` } + +const ( + CreateTime = "create_time" + StatusTime = "status_time" + RuntimeId = "runtime_id" + VersionId = "version_id" + RepoId = "repo_id" + CategoryId = "category_id" + Status = "status" + Type = "type" + Visibility = "visibility" + AppId = "app_id" + Keyword = "keyword" + ISV = "isv" + WorkspaceLabel = "workspace" + BuiltinRepoId = "repo-helm" + StatusActive = "active" + StatusSuspended = "suspended" + ActionRecover = "recover" + ActionSuspend = "suspend" + ActionCancel = "cancel" + ActionPass = "pass" + ActionReject = "reject" + ActionSubmit = "submit" + ActionRelease = "release" + ActionIndex = "index" +) diff --git a/pkg/models/openpitrix/utils/convert.go b/pkg/models/openpitrix/utils.go similarity index 87% rename from pkg/models/openpitrix/utils/convert.go rename to pkg/models/openpitrix/utils.go index c63879e23..d3c8c049d 100644 --- a/pkg/models/openpitrix/utils/convert.go +++ b/pkg/models/openpitrix/utils.go @@ -16,29 +16,28 @@ * / */ -package utils +package openpitrix import ( "github.com/go-openapi/strfmt" - "kubesphere.io/kubesphere/pkg/models/openpitrix/type" "openpitrix.io/openpitrix/pkg/pb" "time" ) -func ConvertApp(in *pb.App) *types.App { +func convertApp(in *pb.App) *App { if in == nil { return nil } - categorySet := make(types.AppCategorySet, 0) + categorySet := make(AppCategorySet, 0) for _, item := range in.CategorySet { - category := ConvertResourceCategory(item) + category := convertResourceCategory(item) categorySet = append(categorySet, category) } - out := types.App{ + out := App{ CategorySet: categorySet, } @@ -93,7 +92,7 @@ func ConvertApp(in *pb.App) *types.App { out.Keywords = in.Keywords.Value } if in.LatestAppVersion != nil { - out.LatestAppVersion = ConvertAppVersion(in.LatestAppVersion) + out.LatestAppVersion = convertAppVersion(in.LatestAppVersion) } if in.Name != nil { out.Name = in.Name.Value @@ -131,11 +130,11 @@ func ConvertApp(in *pb.App) *types.App { return &out } -func ConvertAppVersion(in *pb.AppVersion) *types.AppVersion { +func convertAppVersion(in *pb.AppVersion) *AppVersion { if in == nil { return nil } - out := types.AppVersion{} + out := AppVersion{} if in.AppId != nil { out.AppId = in.AppId.Value } @@ -210,11 +209,11 @@ func ConvertAppVersion(in *pb.AppVersion) *types.AppVersion { } -func ConvertResourceCategory(in *pb.ResourceCategory) *types.ResourceCategory { +func convertResourceCategory(in *pb.ResourceCategory) *ResourceCategory { if in == nil { return nil } - out := types.ResourceCategory{} + out := ResourceCategory{} if in.CategoryId != nil { out.CategoryId = in.CategoryId.Value @@ -240,11 +239,11 @@ func ConvertResourceCategory(in *pb.ResourceCategory) *types.ResourceCategory { return &out } -func ConvertCategory(in *pb.Category) *types.Category { +func convertCategory(in *pb.Category) *Category { if in == nil { return nil } - out := types.Category{} + out := Category{} if in.CategoryId != nil { out.CategoryID = in.CategoryId.Value @@ -276,11 +275,11 @@ func ConvertCategory(in *pb.Category) *types.Category { return &out } -func ConvertAttachment(in *pb.Attachment) *types.Attachment { +func convertAttachment(in *pb.Attachment) *Attachment { if in == nil { return nil } - out := types.Attachment{} + out := Attachment{} out.AttachmentID = in.AttachmentId @@ -298,11 +297,11 @@ func ConvertAttachment(in *pb.Attachment) *types.Attachment { return &out } -func ConvertRepo(in *pb.Repo) *types.Repo { +func convertRepo(in *pb.Repo) *Repo { if in == nil { return nil } - out := types.Repo{} + out := Repo{} if in.RepoId != nil { out.RepoId = in.RepoId.Value @@ -317,10 +316,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo { out.Credential = in.Credential.Value } - categorySet := make(types.RepoCategorySet, 0) + categorySet := make(RepoCategorySet, 0) for _, item := range in.CategorySet { - category := ConvertResourceCategory(item) + category := convertResourceCategory(item) categorySet = append(categorySet, category) } @@ -339,10 +338,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo { out.Description = in.Description.Value } - labelSet := make(types.RepoLabels, 0) + labelSet := make(RepoLabels, 0) for _, item := range in.Labels { - label := ConvertRepoLabel(item) + label := convertRepoLabel(item) labelSet = append(labelSet, label) } @@ -358,10 +357,10 @@ func ConvertRepo(in *pb.Repo) *types.Repo { out.RepoId = in.RepoId.Value } - selectorSet := make(types.RepoSelectors, 0) + selectorSet := make(RepoSelectors, 0) for _, item := range in.Selectors { - selector := ConvertRepoSelector(item) + selector := convertRepoSelector(item) selectorSet = append(selectorSet, selector) } @@ -385,11 +384,11 @@ func ConvertRepo(in *pb.Repo) *types.Repo { return &out } -func ConvertRepoLabel(in *pb.RepoLabel) *types.RepoLabel { +func convertRepoLabel(in *pb.RepoLabel) *RepoLabel { if in == nil { return nil } - out := types.RepoLabel{} + out := RepoLabel{} if in.CreateTime != nil { date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) out.CreateTime = &date @@ -403,11 +402,11 @@ func ConvertRepoLabel(in *pb.RepoLabel) *types.RepoLabel { return &out } -func ConvertRepoSelector(in *pb.RepoSelector) *types.RepoSelector { +func convertRepoSelector(in *pb.RepoSelector) *RepoSelector { if in == nil { return nil } - out := types.RepoSelector{} + out := RepoSelector{} if in.CreateTime != nil { date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) out.CreateTime = &date @@ -421,11 +420,11 @@ func ConvertRepoSelector(in *pb.RepoSelector) *types.RepoSelector { return &out } -func ConvertAppVersionAudit(in *pb.AppVersionAudit) *types.AppVersionAudit { +func convertAppVersionAudit(in *pb.AppVersionAudit) *AppVersionAudit { if in == nil { return nil } - out := types.AppVersionAudit{} + out := AppVersionAudit{} if in.AppId != nil { out.AppId = in.AppId.Value } @@ -463,11 +462,11 @@ func ConvertAppVersionAudit(in *pb.AppVersionAudit) *types.AppVersionAudit { return &out } -func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview { +func convertAppVersionReview(in *pb.AppVersionReview) *AppVersionReview { if in == nil { return nil } - out := types.AppVersionReview{} + out := AppVersionReview{} if in.AppId != nil { out.AppId = in.AppId.Value } @@ -475,9 +474,9 @@ func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview { out.AppName = in.AppName.Value } if in.Phase != nil { - out.Phase = make(types.AppVersionReviewPhaseOAIGen) + out.Phase = make(AppVersionReviewPhaseOAIGen) for k, v := range in.Phase { - out.Phase[k] = *ConvertAppVersionReviewPhase(v) + out.Phase[k] = *convertAppVersionReviewPhase(v) } } if in.ReviewId != nil { @@ -504,11 +503,11 @@ func ConvertAppVersionReview(in *pb.AppVersionReview) *types.AppVersionReview { return &out } -func ConvertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *types.AppVersionReviewPhase { +func convertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *AppVersionReviewPhase { if in == nil { return nil } - out := types.AppVersionReviewPhase{} + out := AppVersionReviewPhase{} if in.Message != nil { out.Message = in.Message.Value } @@ -532,11 +531,11 @@ func ConvertAppVersionReviewPhase(in *pb.AppVersionReviewPhase) *types.AppVersio return &out } -func ConvertRepoEvent(in *pb.RepoEvent) *types.RepoEvent { +func convertRepoEvent(in *pb.RepoEvent) *RepoEvent { if in == nil { return nil } - out := types.RepoEvent{} + out := RepoEvent{} if in.CreateTime != nil { date := strfmt.DateTime(time.Unix(in.CreateTime.Seconds, 0)) out.CreateTime = &date @@ -564,11 +563,11 @@ func ConvertRepoEvent(in *pb.RepoEvent) *types.RepoEvent { return &out } -func ConvertCluster(in *pb.Cluster) *types.Cluster { +func convertCluster(in *pb.Cluster) *Cluster { if in == nil { return nil } - out := types.Cluster{} + out := Cluster{} if in.AdditionalInfo != nil { out.AdditionalInfo = in.AdditionalInfo.Value } diff --git a/pkg/models/resources/v1alpha2/resource/resources.go b/pkg/models/resources/v1alpha2/resource/resources.go index c767d9d85..ff8ff7857 100644 --- a/pkg/models/resources/v1alpha2/resource/resources.go +++ b/pkg/models/resources/v1alpha2/resource/resources.go @@ -52,6 +52,10 @@ type ResourceGetter struct { resourcesGetters map[string]v1alpha2.Interface } +func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) { + r.resourcesGetters[resource] = getter +} + func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { resourceGetters := make(map[string]v1alpha2.Interface) diff --git a/pkg/models/tenant/devops.go b/pkg/models/tenant/devops.go index 5860e6a31..f9e36c017 100644 --- a/pkg/models/tenant/devops.go +++ b/pkg/models/tenant/devops.go @@ -28,6 +28,7 @@ import ( "kubesphere.io/kubesphere/pkg/gojenkins/utils" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/server/params" cs "kubesphere.io/kubesphere/pkg/simple/client" "net/http" @@ -317,7 +318,7 @@ func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOps return project, nil } -func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) { +func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) { role, err := devops.GetProjectUserRole(username, projectId) if err != nil { klog.Errorf("%+v", err) @@ -326,12 +327,12 @@ func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, return GetDevopsRoleSimpleRules(role), nil } -func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { - var rules []models.SimpleRule +func GetDevopsRoleSimpleRules(role string) []iam.SimpleRule { + var rules []iam.SimpleRule switch role { case "developer": - rules = []models.SimpleRule{ + rules = []iam.SimpleRule{ {Name: "pipelines", Actions: []string{"view", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, @@ -339,7 +340,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { } break case "owner": - rules = []models.SimpleRule{ + rules = []iam.SimpleRule{ {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"create", "edit", "view", "delete"}}, @@ -348,7 +349,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { } break case "maintainer": - rules = []models.SimpleRule{ + rules = []iam.SimpleRule{ {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, @@ -359,7 +360,7 @@ func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { case "reporter": fallthrough default: - rules = []models.SimpleRule{ + rules = []iam.SimpleRule{ {Name: "pipelines", Actions: []string{"view"}}, {Name: "roles", Actions: []string{"view"}}, {Name: "members", Actions: []string{"view"}}, diff --git a/pkg/models/tenant/namespaces.go b/pkg/models/tenant/namespaces.go index d9161d04d..e49225292 100644 --- a/pkg/models/tenant/namespaces.go +++ b/pkg/models/tenant/namespaces.go @@ -21,9 +21,10 @@ import ( "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/labels" + k8sinformers "k8s.io/client-go/informers" + kubernetes "k8s.io/client-go/kubernetes" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" @@ -32,11 +33,34 @@ import ( "strings" ) -type namespaceSearcher struct { +type NamespaceInterface interface { + Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) + CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) } -// Exactly Match -func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { +type namespaceSearcher struct { + k8s kubernetes.Interface + informers k8sinformers.SharedInformerFactory +} + +func (s *namespaceSearcher) CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) { + if namespace.Labels == nil { + namespace.Labels = make(map[string]string, 0) + } + if username != "" { + namespace.Annotations[constants.CreatorAnnotationKey] = username + } + + namespace.Labels[constants.WorkspaceLabelKey] = workspace + + return s.k8s.CoreV1().Namespaces().Create(namespace) +} + +func newNamespaceOperator(k8s kubernetes.Interface, informers k8sinformers.SharedInformerFactory) NamespaceInterface { + return &namespaceSearcher{k8s: k8s, informers: informers} +} + +func (s *namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { for k, v := range match { switch k { case v1alpha2.Name: @@ -58,7 +82,7 @@ func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) boo return true } -func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { +func (s *namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { for k, v := range fuzzy { switch k { @@ -74,7 +98,7 @@ func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) boo return true } -func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { +func (s *namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { switch orderBy { case "createTime": return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) @@ -85,7 +109,7 @@ func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { } } -func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) { +func (s *namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) { roles, err := iam.GetUserRoles("", username) @@ -93,7 +117,7 @@ func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error return nil, err } namespaces := make([]*v1.Namespace, 0) - namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() + namespaceLister := s.informers.Core().V1().Namespaces().Lister() for _, role := range roles { namespace, err := namespaceLister.Get(role.Namespace) if err != nil { @@ -117,7 +141,7 @@ func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool return false } -func (s *namespaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) { +func (s *namespaceSearcher) Search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) { rules, err := iam.GetUserClusterRules(username) @@ -128,7 +152,7 @@ func (s *namespaceSearcher) search(username string, conditions *params.Condition 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()) + namespaces, err = s.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) } else { namespaces, err = s.GetNamespaces(username) } @@ -148,12 +172,14 @@ func (s *namespaceSearcher) search(username string, conditions *params.Condition // order & reverse sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } return s.compare(result[i], result[j], orderBy) }) return result, nil } + +func CreateNamespace() { + +} diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 45ffd8e03..a68b72c3e 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -19,49 +19,62 @@ package tenant import ( "k8s.io/api/core/v1" + k8sinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - ws "kubesphere.io/kubesphere/pkg/models/workspaces" "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/mysql" "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.Annotations[constants.CreatorAnnotationKey] = username - } - - namespace.Labels[constants.WorkspaceLabelKey] = workspaceName - - return client.ClientSets().K8s().Kubernetes().CoreV1().Namespaces().Create(namespace) +type Interface interface { + CreateNamespace(workspace string, namespace *v1.Namespace, username string) (*v1.Namespace, error) + DeleteNamespace(workspace, namespace string) error + DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error) + ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) } -func DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) { - workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName) +type tenantOperator struct { + workspaces WorkspaceInterface + namespaces NamespaceInterface +} + +func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { + return t.workspaces.DeleteNamespace(workspace, namespace) +} + +func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface { + return &tenantOperator{ + workspaces: newWorkspaceOperator(client, informers, ksinformers, db), + namespaces: newNamespaceOperator(client, informers), + } +} + +func (t *tenantOperator) CreateNamespace(workspaceName string, namespace *v1.Namespace, username string) (*v1.Namespace, error) { + return t.namespaces.CreateNamespace(workspaceName, namespace, username) +} + +func (t *tenantOperator) DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) { + workspace, err := t.workspaces.GetWorkspace(workspaceName) if err != nil { return nil, err } - workspace = appendAnnotations(username, workspace) + if username != "" { + workspace = t.appendAnnotations(username, workspace) + } return workspace, nil } -func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { +func (t *tenantOperator) 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 := t.workspaces.SearchWorkspace(username, conditions, orderBy, reverse) if err != nil { return nil, err @@ -71,7 +84,7 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri result := make([]interface{}, 0) for i, workspace := range workspaces { if len(result) < limit && i >= offset { - workspace := appendAnnotations(username, workspace) + workspace := t.appendAnnotations(username, workspace) result = append(result, workspace) } } @@ -79,12 +92,12 @@ func ListWorkspaces(username string, conditions *params.Conditions, orderBy stri return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil } -func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace { +func (t *tenantOperator) 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, ¶ms.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0) + ns, err := t.ListNamespaces(username, ¶ms.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) } @@ -92,16 +105,18 @@ func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1 if err == nil { workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount) } - userCount, err := ws.WorkspaceUserCount(workspace.Name) + + userCount, err := t.workspaces.CountUsersInWorkspace(workspace.Name) + if err == nil { workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount) } return workspace } -func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { +func (t *tenantOperator) 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 := t.namespaces.Search(username, conditions, orderBy, reverse) if err != nil { return nil, err diff --git a/pkg/models/tenant/workspaces.go b/pkg/models/tenant/workspaces.go index ac98aa0b2..a08f84c84 100644 --- a/pkg/models/tenant/workspaces.go +++ b/pkg/models/tenant/workspaces.go @@ -1,41 +1,241 @@ /* - - 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. - -*/ + * + * Copyright 2020 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 ( + "fmt" + core "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/db" + "kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" "kubesphere.io/kubesphere/pkg/server/params" + clientset "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/mysql" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" "sort" "strings" + + "k8s.io/api/rbac/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" ) -type workspaceSearcher struct { +type WorkspaceInterface interface { + GetWorkspace(workspace string) (*v1alpha1.Workspace, error) + SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) + ListNamespaces(workspace string) ([]*core.Namespace, error) + DeleteNamespace(workspace, namespace string) error + RemoveUser(user, workspace string) error + AddUser(workspace string, user *iam.User) error + CountDevopsProjectsInWorkspace(workspace string) (int, error) + CountUsersInWorkspace(workspace string) (int, error) + CountOrgRoles() (int, error) + CountWorkspaces() (int, error) + CountNamespacesInWorkspace(workspace string) (int, error) } -// Exactly Match -func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspace) bool { +type workspaceOperator struct { + client kubernetes.Interface + informers informers.SharedInformerFactory + ksInformers externalversions.SharedInformerFactory + + // TODO: use db interface instead of mysql client + // we can refactor this after rewrite devops using crd + db *mysql.Database +} + +func newWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, db *mysql.Database) WorkspaceInterface { + return &workspaceOperator{ + client: client, + informers: informers, + ksInformers: ksinformers, + db: db, + } +} + +func (w *workspaceOperator) ListNamespaces(workspace string) ([]*core.Namespace, error) { + namespaces, err := w.informers.Core().V1().Namespaces().Lister().List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace})) + + if err != nil { + return nil, err + } + + return namespaces, nil +} + +func (w *workspaceOperator) DeleteNamespace(workspace string, namespace string) error { + ns, err := w.informers.Core().V1().Namespaces().Lister().Get(namespace) + if err != nil { + return err + } + + if ns.Labels[constants.WorkspaceLabelKey] == workspace { + deletePolicy := metav1.DeletePropagationBackground + return w.client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) + } else { + return apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "workspace"}, workspace) + } +} + +func (w *workspaceOperator) RemoveUser(workspace string, username string) error { + workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username) + if err != nil { + return err + } + + err = w.deleteWorkspaceRoleBinding(workspace, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) + if err != nil { + return err + } + + return nil +} + +func (w *workspaceOperator) AddUser(workspaceName string, user *iam.User) error { + + workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username) + + if err != nil && !apierrors.IsNotFound(err) { + klog.Errorf("get workspace role failed: %+v", err) + return err + } + + workspaceRoleName := fmt.Sprintf("workspace:%s:%s", workspaceName, strings.TrimPrefix(user.WorkspaceRole, "workspace-")) + var currentWorkspaceRoleName string + if workspaceRole != nil { + currentWorkspaceRoleName = workspaceRole.Name + } + + if currentWorkspaceRoleName != workspaceRoleName && currentWorkspaceRoleName != "" { + err := w.deleteWorkspaceRoleBinding(workspaceName, user.Username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) + if err != nil { + klog.Errorf("delete workspace role binding failed: %+v", err) + return err + } + } else if currentWorkspaceRoleName != "" { + return nil + } + + return w.createWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole) +} + +func (w *workspaceOperator) createWorkspaceRoleBinding(workspace, username string, role string) error { + + 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 := w.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) + if err != nil { + return err + } + + if !k8sutil.ContainsUser(workspaceRoleBinding.Subjects, username) { + workspaceRoleBinding = workspaceRoleBinding.DeepCopy() + workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username}) + _, err = w.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) + if err != nil { + klog.Errorf("update workspace role binding failed: %+v", err) + return err + } + } + + return nil +} + +func (w *workspaceOperator) deleteWorkspaceRoleBinding(workspace, username string, role string) error { + + 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 := w.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) + if err != nil { + return err + } + workspaceRoleBinding = workspaceRoleBinding.DeepCopy() + + 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-- + } + } + + workspaceRoleBinding, err = w.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) + + return err +} + +func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) { + if w.db == nil { + return 0, clientset.ErrClientSetNotEnabled + } + + query := w.db.Select(devops.DevOpsProjectIdColumn). + From(devops.DevOpsProjectTableName). + Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName), + db.Eq(devops.StatusColumn, devops.StatusActive))) + + devOpsProjects := make([]string, 0) + + if _, err := query.Load(&devOpsProjects); err != nil { + return 0, err + } + return len(devOpsProjects), nil +} + +func (w *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) { + count, err := iam.WorkspaceUsersTotalCount(workspace) + if err != nil { + return 0, err + } + return count, nil +} + +func (w *workspaceOperator) CountOrgRoles() (int, error) { + return len(constants.WorkSpaceRoles), nil +} + +func (w *workspaceOperator) CountNamespacesInWorkspace(workspace string) (int, error) { + ns, err := w.ListNamespaces(workspace) + if err != nil { + return 0, err + } + + return len(ns), nil +} + +func (*workspaceOperator) match(match map[string]string, item *v1alpha1.Workspace) bool { for k, v := range match { switch k { case v1alpha2.Name: @@ -57,7 +257,7 @@ func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspac return true } -func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool { +func (*workspaceOperator) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool { for k, v := range fuzzy { switch k { @@ -73,7 +273,7 @@ func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspac return true } -func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool { +func (*workspaceOperator) compare(a, b *v1alpha1.Workspace, orderBy string) bool { switch orderBy { case v1alpha2.CreateTime: return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) @@ -84,7 +284,7 @@ func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool } } -func (s *workspaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) { +func (w *workspaceOperator) SearchWorkspace(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) { rules, err := iam.GetUserClusterRules(username) if err != nil { @@ -94,7 +294,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition 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()) + workspaces, err = w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) if err != nil { return nil, err } @@ -104,7 +304,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition return nil, err } for k := range workspaceRoles { - workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(k) + workspace, err := w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().Get(k) if err != nil { return nil, err } @@ -115,7 +315,7 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition result := make([]*v1alpha1.Workspace, 0) for _, workspace := range workspaces { - if s.match(conditions.Match, workspace) && s.fuzzy(conditions.Fuzzy, workspace) { + if w.match(conditions.Match, workspace) && w.fuzzy(conditions.Fuzzy, workspace) { result = append(result, workspace) } } @@ -123,18 +323,16 @@ func (s *workspaceSearcher) search(username string, conditions *params.Condition // order & reverse sort.Slice(result, func(i, j int) bool { if reverse { - tmp := i - i = j - j = tmp + i, j = j, i } - return s.compare(result[i], result[j], orderBy) + return w.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) +func (w *workspaceOperator) GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) { + return w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName) } func contains(m map[string]string, key, value string) bool { @@ -149,3 +347,47 @@ func contains(m map[string]string, key, value string) bool { } return false } + +/* +// TODO: move to metrics package +func GetAllProjectNums() (int, error) { + namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() + list, err := namespaceLister.List(labels.Everything()) + if err != nil { + return 0, err + } + return len(list), nil +} + +func GetAllDevOpsProjectsNums() (int, error) { + _, err := clientset.ClientSets().Devops() + if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled { + return 0, err + } + + dbconn, err := clientset.ClientSets().MySQL() + if err != nil { + return 0, err + } + + query := dbconn.Select(devops.DevOpsProjectIdColumn). + From(devops.DevOpsProjectTableName). + Where(db.Eq(devops.StatusColumn, devops.StatusActive)) + + devOpsProjects := make([]string, 0) + + if _, err := query.Load(&devOpsProjects); err != nil { + return 0, err + } + return len(devOpsProjects), nil +} +*/ + +func (w *workspaceOperator) CountWorkspaces() (int, error) { + ws, err := w.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) + if err != nil { + return 0, err + } + + return len(ws), nil +} diff --git a/pkg/models/types.go b/pkg/models/types.go index 18a410c42..fccff9c87 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -17,12 +17,6 @@ */ package models -import ( - "time" - - "k8s.io/api/rbac/v1" -) - type PageableResponse struct { Items []interface{} `json:"items" description:"paging data"` TotalCount int `json:"total_count" description:"total count"` @@ -35,41 +29,6 @@ type Workspace struct { DevopsProjects []string `json:"devops_projects"` } -type Action struct { - Name string `json:"name"` - Rules []v1.PolicyRule `json:"rules"` -} - -type Rule struct { - Name string `json:"name"` - Actions []Action `json:"actions"` -} - -type SimpleRule struct { - Name string `json:"name" description:"rule name"` - Actions []string `json:"actions" description:"actions"` -} - -type User struct { - 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 { Path string `json:"path"` Name string `json:"name"` diff --git a/pkg/models/workspaces/workspaces.go b/pkg/models/workspaces/workspaces.go deleted file mode 100644 index cbaf3439a..000000000 --- a/pkg/models/workspaces/workspaces.go +++ /dev/null @@ -1,276 +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 workspaces - -import ( - "fmt" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/models/iam" - clientset "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "strings" - - core "k8s.io/api/core/v1" - - "errors" - "k8s.io/api/rbac/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type Interface interface { - ListNamespaces(workspace string) ([]*core.Namespace, error) - DeleteNamespace(workspace, namespace string) error - RemoveUser(user, workspace string) error - AddUser(workspace string, user *models.User) error - CountDevopsProjectsInWorkspace(workspace string) (int, error) - CountUsersInWorkspace(workspace string) (int, error) - CountOrgRoles() (int, error) - CountWorkspaces() (int, error) - CountNamespacesInWorkspace(workspace string) (int, error) -} - -type workspaceOperator struct { - client kubernetes.Interface - informers informers.SharedInformerFactory - ksInformers externalversions.SharedInformerFactory - - // TODO: use db interface instead of mysql client - // we can refactor this after rewrite devops using crd - db *mysql.Database -} - -func NewWorkspaceOperator(client kubernetes.Interface, informers informers.SharedInformerFactory, ksinformers externalversions.SharedInformerFactory, db *mysql.Database) Interface { - return &workspaceOperator{ - client: client, - informers: informers, - ksInformers: ksinformers, - db: db, - } -} - -func (c *workspaceOperator) ListNamespaces(workspace string) ([]*core.Namespace, error) { - namespaces, err := c.informers.Core().V1().Namespaces().Lister().List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspace})) - - if err != nil { - return nil, err - } - - return namespaces, nil -} - -func (c *workspaceOperator) DeleteNamespace(workspace string, namespace string) error { - ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace) - if err != nil { - return err - } - - if ns.Labels[constants.WorkspaceLabelKey] == workspace { - deletePolicy := metav1.DeletePropagationBackground - return c.client.CoreV1().Namespaces().Delete(namespace, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) - } else { - return errors.New("resource not found") - } -} - -func (c *workspaceOperator) RemoveUser(workspace string, username string) error { - workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username) - if err != nil { - return err - } - - err = c.deleteWorkspaceRoleBinding(workspace, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) - if err != nil { - return err - } - - return nil -} - -func (c *workspaceOperator) AddUser(workspaceName string, user *models.User) error { - - workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username) - - if err != nil && !apierrors.IsNotFound(err) { - klog.Errorf("get workspace role failed: %+v", err) - return err - } - - workspaceRoleName := fmt.Sprintf("workspace:%s:%s", workspaceName, strings.TrimPrefix(user.WorkspaceRole, "workspace-")) - var currentWorkspaceRoleName string - if workspaceRole != nil { - currentWorkspaceRoleName = workspaceRole.Name - } - - if currentWorkspaceRoleName != workspaceRoleName && currentWorkspaceRoleName != "" { - err := c.deleteWorkspaceRoleBinding(workspaceName, user.Username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) - if err != nil { - klog.Errorf("delete workspace role binding failed: %+v", err) - return err - } - } else if currentWorkspaceRoleName != "" { - return nil - } - - return c.createWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole) -} - -func (c *workspaceOperator) createWorkspaceRoleBinding(workspace, username string, role string) error { - - 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 := c.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) - if err != nil { - return err - } - - if !k8sutil.ContainsUser(workspaceRoleBinding.Subjects, username) { - workspaceRoleBinding = workspaceRoleBinding.DeepCopy() - workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username}) - _, err = c.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) - if err != nil { - klog.Errorf("update workspace role binding failed: %+v", err) - return err - } - } - - return nil -} - -func (c *workspaceOperator) deleteWorkspaceRoleBinding(workspace, username string, role string) error { - - 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 := c.informers.Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) - if err != nil { - return err - } - workspaceRoleBinding = workspaceRoleBinding.DeepCopy() - - 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-- - } - } - - workspaceRoleBinding, err = c.client.RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) - - return err -} - -func (c *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) { - if c.db == nil { - return 0, clientset.ErrClientSetNotEnabled - } - - query := c.db.Select(devops.DevOpsProjectIdColumn). - From(devops.DevOpsProjectTableName). - Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName), - db.Eq(devops.StatusColumn, devops.StatusActive))) - - devOpsProjects := make([]string, 0) - - if _, err := query.Load(&devOpsProjects); err != nil { - return 0, err - } - return len(devOpsProjects), nil -} - -func (c *workspaceOperator) CountUsersInWorkspace(workspace string) (int, error) { - count, err := iam.WorkspaceUsersTotalCount(workspace) - if err != nil { - return 0, err - } - return count, nil -} - -func (c *workspaceOperator) CountOrgRoles() (int, error) { - return len(constants.WorkSpaceRoles), nil -} - -func (c *workspaceOperator) CountNamespacesInWorkspace(workspace string) (int, error) { - ns, err := c.ListNamespaces(workspace) - if err != nil { - return 0, err - } - - return len(ns), nil -} - -/* -// TODO: move to metrics package -func GetAllProjectNums() (int, error) { - namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - list, err := namespaceLister.List(labels.Everything()) - if err != nil { - return 0, err - } - return len(list), nil -} - -func GetAllDevOpsProjectsNums() (int, error) { - _, err := clientset.ClientSets().Devops() - if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled { - return 0, err - } - - dbconn, err := clientset.ClientSets().MySQL() - if err != nil { - return 0, err - } - - query := dbconn.Select(devops.DevOpsProjectIdColumn). - From(devops.DevOpsProjectTableName). - Where(db.Eq(devops.StatusColumn, devops.StatusActive)) - - devOpsProjects := make([]string, 0) - - if _, err := query.Load(&devOpsProjects); err != nil { - return 0, err - } - return len(devOpsProjects), nil -} -*/ - -func (c *workspaceOperator) CountWorkspaces() (int, error) { - ws, err := c.ksInformers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - if err != nil { - return 0, err - } - - return len(ws), nil -} diff --git a/pkg/server/params/params.go b/pkg/server/params/params.go index 83ace6438..f558ed3ad 100644 --- a/pkg/server/params/params.go +++ b/pkg/server/params/params.go @@ -30,11 +30,10 @@ const ( OrderByParam = "orderBy" ConditionsParam = "conditions" ReverseParam = "reverse" - NameParam = "name" ) -func ParsePaging(paging string) (limit, offset int) { - +func ParsePaging(req *restful.Request) (limit, offset int) { + paging := req.QueryParameter(PagingParam) limit = 10 offset = 0 if groups := regexp.MustCompile(`^limit=(-?\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 { @@ -45,7 +44,9 @@ func ParsePaging(paging string) (limit, offset int) { return } -func ParseConditions(conditionsStr string) (*Conditions, error) { +func ParseConditions(req *restful.Request) (*Conditions, error) { + + conditionsStr := req.QueryParameter(ConditionsParam) conditions := &Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)} @@ -76,20 +77,19 @@ func ParseConditions(conditionsStr string) (*Conditions, error) { return conditions, nil } -func ParseReverse(req *restful.Request) bool { - reverse := req.QueryParameter(ReverseParam) - b, err := strconv.ParseBool(reverse) - if err != nil { - return false - } - return b -} - type Conditions struct { Match map[string]string Fuzzy map[string]string } +func GetBoolValueWithDefault(req *restful.Request, name string, dv bool) bool { + reverse := req.QueryParameter(name) + if v, err := strconv.ParseBool(reverse); err == nil { + return v + } + return dv +} + func GetStringValueWithDefault(req *restful.Request, name string, dv string) string { v := req.QueryParameter(name) if v == "" { diff --git a/pkg/simple/client/factory.go b/pkg/simple/client/factory.go index 55fd9f3ee..223fa6ce0 100644 --- a/pkg/simple/client/factory.go +++ b/pkg/simple/client/factory.go @@ -119,7 +119,7 @@ type ClientSet struct { redisClient cache.Interface s3Client s3.Interface prometheusClient *prometheus.Client - openpitrixClient *openpitrix.Client + openpitrixClient openpitrix.Client kubesphereClient *kubesphere.Client elasticSearchClient *esclient.ElasticSearchClient } @@ -292,7 +292,7 @@ func (cs *ClientSet) S3() (s3.Interface, error) { } } -func (cs *ClientSet) OpenPitrix() (*openpitrix.Client, error) { +func (cs *ClientSet) OpenPitrix() (openpitrix.Client, error) { var err error if cs.csoptions.openPitrixOptions == nil || diff --git a/pkg/simple/client/ldap/channel.go b/pkg/simple/client/ldap/channel.go index 6f91393db..1c49cb826 100644 --- a/pkg/simple/client/ldap/channel.go +++ b/pkg/simple/client/ldap/channel.go @@ -85,7 +85,7 @@ func (c *channelPool) Get() (*PoolConn, error) { return nil, ErrClosed } - // wrap our connections with our ldap.Client implementation (wrapConn + // wrap our connections with our ldap.PoolClient implementation (wrapConn // method) that puts the connection back to the pool if it's closed. select { case conn := <-conns: diff --git a/pkg/simple/client/ldap/conn.go b/pkg/simple/client/ldap/conn.go index c10d3b90c..c393a6a71 100644 --- a/pkg/simple/client/ldap/conn.go +++ b/pkg/simple/client/ldap/conn.go @@ -8,7 +8,7 @@ import ( "github.com/go-ldap/ldap" ) -// PoolConn implements Client to override the Close() method +// PoolConn implements PoolClient to override the Close() method type PoolConn struct { Conn ldap.Client c *channelPool diff --git a/pkg/simple/client/ldap/ldap.go b/pkg/simple/client/ldap/ldap.go index 7a9d29c0c..15ffc7cf2 100644 --- a/pkg/simple/client/ldap/ldap.go +++ b/pkg/simple/client/ldap/ldap.go @@ -23,13 +23,19 @@ import ( "k8s.io/klog" ) -type Client struct { +type Client interface { + NewConn() (ldap.Client, error) + GroupSearchBase() string + UserSearchBase() string +} + +type poolClient struct { pool Pool options *Options } // panic if cannot connect to ldap service -func NewLdapClient(options *Options, stopCh <-chan struct{}) (*Client, error) { +func NewLdapClient(options *Options, stopCh <-chan struct{}) (Client, error) { pool, err := NewChannelPool(8, 64, "kubesphere", func(s string) (ldap.Client, error) { conn, err := ldap.Dial("tcp", options.Host) if err != nil { @@ -44,7 +50,7 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (*Client, error) { return nil, err } - client := &Client{ + client := &poolClient{ pool: pool, options: options, } @@ -59,7 +65,7 @@ func NewLdapClient(options *Options, stopCh <-chan struct{}) (*Client, error) { return client, nil } -func (l *Client) NewConn() (ldap.Client, error) { +func (l *poolClient) NewConn() (ldap.Client, error) { if l.pool == nil { err := fmt.Errorf("ldap connection pool is not initialized") klog.Errorln(err) @@ -81,10 +87,10 @@ func (l *Client) NewConn() (ldap.Client, error) { return conn, nil } -func (l *Client) GroupSearchBase() string { +func (l *poolClient) GroupSearchBase() string { return l.options.GroupSearchBase } -func (l *Client) UserSearchBase() string { +func (l *poolClient) UserSearchBase() string { return l.options.UserSearchBase } diff --git a/pkg/simple/client/openpitrix/mock.go b/pkg/simple/client/openpitrix/mock.go new file mode 100644 index 000000000..dc4d51e50 --- /dev/null +++ b/pkg/simple/client/openpitrix/mock.go @@ -0,0 +1,2017 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/simple/client/openpitrix/openpitrix.go + +// Package openpitrix is a generated GoMock package. +package openpitrix + +import ( + context "context" + gomock "github.com/golang/mock/gomock" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" + pb "openpitrix.io/openpitrix/pkg/pb" + reflect "reflect" +) + +// MockClient is a mock of Client interface +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// CreateRuntime mocks base method +func (m *MockClient) CreateRuntime(ctx context.Context, in *pb.CreateRuntimeRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRuntime", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRuntime indicates an expected call of CreateRuntime +func (mr *MockClientMockRecorder) CreateRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRuntime", reflect.TypeOf((*MockClient)(nil).CreateRuntime), varargs...) +} + +// CreateDebugRuntime mocks base method +func (m *MockClient) CreateDebugRuntime(ctx context.Context, in *pb.CreateRuntimeRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugRuntime", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugRuntime indicates an expected call of CreateDebugRuntime +func (mr *MockClientMockRecorder) CreateDebugRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugRuntime", reflect.TypeOf((*MockClient)(nil).CreateDebugRuntime), varargs...) +} + +// DescribeRuntimeDetails mocks base method +func (m *MockClient) DescribeRuntimeDetails(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeDetailsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeDetails", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeDetailsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeDetails indicates an expected call of DescribeRuntimeDetails +func (mr *MockClientMockRecorder) DescribeRuntimeDetails(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeDetails", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeDetails), varargs...) +} + +// DescribeRuntimes mocks base method +func (m *MockClient) DescribeRuntimes(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimes indicates an expected call of DescribeRuntimes +func (mr *MockClientMockRecorder) DescribeRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimes", reflect.TypeOf((*MockClient)(nil).DescribeRuntimes), varargs...) +} + +// DescribeDebugRuntimes mocks base method +func (m *MockClient) DescribeDebugRuntimes(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugRuntimes indicates an expected call of DescribeDebugRuntimes +func (mr *MockClientMockRecorder) DescribeDebugRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugRuntimes", reflect.TypeOf((*MockClient)(nil).DescribeDebugRuntimes), varargs...) +} + +// ModifyRuntime mocks base method +func (m *MockClient) ModifyRuntime(ctx context.Context, in *pb.ModifyRuntimeRequest, opts ...grpc.CallOption) (*pb.ModifyRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRuntime", varargs...) + ret0, _ := ret[0].(*pb.ModifyRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRuntime indicates an expected call of ModifyRuntime +func (mr *MockClientMockRecorder) ModifyRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRuntime", reflect.TypeOf((*MockClient)(nil).ModifyRuntime), varargs...) +} + +// DeleteRuntimes mocks base method +func (m *MockClient) DeleteRuntimes(ctx context.Context, in *pb.DeleteRuntimesRequest, opts ...grpc.CallOption) (*pb.DeleteRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DeleteRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRuntimes indicates an expected call of DeleteRuntimes +func (mr *MockClientMockRecorder) DeleteRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRuntimes", reflect.TypeOf((*MockClient)(nil).DeleteRuntimes), varargs...) +} + +// CreateRuntimeCredential mocks base method +func (m *MockClient) CreateRuntimeCredential(ctx context.Context, in *pb.CreateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRuntimeCredential indicates an expected call of CreateRuntimeCredential +func (mr *MockClientMockRecorder) CreateRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRuntimeCredential", reflect.TypeOf((*MockClient)(nil).CreateRuntimeCredential), varargs...) +} + +// CreateDebugRuntimeCredential mocks base method +func (m *MockClient) CreateDebugRuntimeCredential(ctx context.Context, in *pb.CreateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugRuntimeCredential indicates an expected call of CreateDebugRuntimeCredential +func (mr *MockClientMockRecorder) CreateDebugRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugRuntimeCredential", reflect.TypeOf((*MockClient)(nil).CreateDebugRuntimeCredential), varargs...) +} + +// DescribeRuntimeCredentials mocks base method +func (m *MockClient) DescribeRuntimeCredentials(ctx context.Context, in *pb.DescribeRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeCredentials indicates an expected call of DescribeRuntimeCredentials +func (mr *MockClientMockRecorder) DescribeRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeCredentials), varargs...) +} + +// DescribeDebugRuntimeCredentials mocks base method +func (m *MockClient) DescribeDebugRuntimeCredentials(ctx context.Context, in *pb.DescribeRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugRuntimeCredentials indicates an expected call of DescribeDebugRuntimeCredentials +func (mr *MockClientMockRecorder) DescribeDebugRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DescribeDebugRuntimeCredentials), varargs...) +} + +// ModifyRuntimeCredential mocks base method +func (m *MockClient) ModifyRuntimeCredential(ctx context.Context, in *pb.ModifyRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.ModifyRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.ModifyRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRuntimeCredential indicates an expected call of ModifyRuntimeCredential +func (mr *MockClientMockRecorder) ModifyRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRuntimeCredential", reflect.TypeOf((*MockClient)(nil).ModifyRuntimeCredential), varargs...) +} + +// DeleteRuntimeCredentials mocks base method +func (m *MockClient) DeleteRuntimeCredentials(ctx context.Context, in *pb.DeleteRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DeleteRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DeleteRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRuntimeCredentials indicates an expected call of DeleteRuntimeCredentials +func (mr *MockClientMockRecorder) DeleteRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DeleteRuntimeCredentials), varargs...) +} + +// ValidateRuntimeCredential mocks base method +func (m *MockClient) ValidateRuntimeCredential(ctx context.Context, in *pb.ValidateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.ValidateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.ValidateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateRuntimeCredential indicates an expected call of ValidateRuntimeCredential +func (mr *MockClientMockRecorder) ValidateRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRuntimeCredential", reflect.TypeOf((*MockClient)(nil).ValidateRuntimeCredential), varargs...) +} + +// DescribeRuntimeProviderZones mocks base method +func (m *MockClient) DescribeRuntimeProviderZones(ctx context.Context, in *pb.DescribeRuntimeProviderZonesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeProviderZonesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeProviderZones", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeProviderZonesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeProviderZones indicates an expected call of DescribeRuntimeProviderZones +func (mr *MockClientMockRecorder) DescribeRuntimeProviderZones(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeProviderZones", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeProviderZones), varargs...) +} + +// GetRuntimeStatistics mocks base method +func (m *MockClient) GetRuntimeStatistics(ctx context.Context, in *pb.GetRuntimeStatisticsRequest, opts ...grpc.CallOption) (*pb.GetRuntimeStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetRuntimeStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetRuntimeStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRuntimeStatistics indicates an expected call of GetRuntimeStatistics +func (mr *MockClientMockRecorder) GetRuntimeStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRuntimeStatistics", reflect.TypeOf((*MockClient)(nil).GetRuntimeStatistics), varargs...) +} + +// AddNodeKeyPairs mocks base method +func (m *MockClient) AddNodeKeyPairs(ctx context.Context, in *pb.AddNodeKeyPairsRequest, opts ...grpc.CallOption) (*pb.AddNodeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddNodeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.AddNodeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddNodeKeyPairs indicates an expected call of AddNodeKeyPairs +func (mr *MockClientMockRecorder) AddNodeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddNodeKeyPairs", reflect.TypeOf((*MockClient)(nil).AddNodeKeyPairs), varargs...) +} + +// DeleteNodeKeyPairs mocks base method +func (m *MockClient) DeleteNodeKeyPairs(ctx context.Context, in *pb.DeleteNodeKeyPairsRequest, opts ...grpc.CallOption) (*pb.DeleteNodeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteNodeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DeleteNodeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteNodeKeyPairs indicates an expected call of DeleteNodeKeyPairs +func (mr *MockClientMockRecorder) DeleteNodeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNodeKeyPairs", reflect.TypeOf((*MockClient)(nil).DeleteNodeKeyPairs), varargs...) +} + +// CreateKeyPair mocks base method +func (m *MockClient) CreateKeyPair(ctx context.Context, in *pb.CreateKeyPairRequest, opts ...grpc.CallOption) (*pb.CreateKeyPairResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateKeyPair", varargs...) + ret0, _ := ret[0].(*pb.CreateKeyPairResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateKeyPair indicates an expected call of CreateKeyPair +func (mr *MockClientMockRecorder) CreateKeyPair(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKeyPair", reflect.TypeOf((*MockClient)(nil).CreateKeyPair), varargs...) +} + +// DescribeKeyPairs mocks base method +func (m *MockClient) DescribeKeyPairs(ctx context.Context, in *pb.DescribeKeyPairsRequest, opts ...grpc.CallOption) (*pb.DescribeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DescribeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeKeyPairs indicates an expected call of DescribeKeyPairs +func (mr *MockClientMockRecorder) DescribeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeKeyPairs", reflect.TypeOf((*MockClient)(nil).DescribeKeyPairs), varargs...) +} + +// DeleteKeyPairs mocks base method +func (m *MockClient) DeleteKeyPairs(ctx context.Context, in *pb.DeleteKeyPairsRequest, opts ...grpc.CallOption) (*pb.DeleteKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DeleteKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteKeyPairs indicates an expected call of DeleteKeyPairs +func (mr *MockClientMockRecorder) DeleteKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteKeyPairs", reflect.TypeOf((*MockClient)(nil).DeleteKeyPairs), varargs...) +} + +// AttachKeyPairs mocks base method +func (m *MockClient) AttachKeyPairs(ctx context.Context, in *pb.AttachKeyPairsRequest, opts ...grpc.CallOption) (*pb.AttachKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AttachKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.AttachKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AttachKeyPairs indicates an expected call of AttachKeyPairs +func (mr *MockClientMockRecorder) AttachKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachKeyPairs", reflect.TypeOf((*MockClient)(nil).AttachKeyPairs), varargs...) +} + +// DetachKeyPairs mocks base method +func (m *MockClient) DetachKeyPairs(ctx context.Context, in *pb.DetachKeyPairsRequest, opts ...grpc.CallOption) (*pb.DetachKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DetachKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DetachKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DetachKeyPairs indicates an expected call of DetachKeyPairs +func (mr *MockClientMockRecorder) DetachKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachKeyPairs", reflect.TypeOf((*MockClient)(nil).DetachKeyPairs), varargs...) +} + +// DescribeSubnets mocks base method +func (m *MockClient) DescribeSubnets(ctx context.Context, in *pb.DescribeSubnetsRequest, opts ...grpc.CallOption) (*pb.DescribeSubnetsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeSubnets", varargs...) + ret0, _ := ret[0].(*pb.DescribeSubnetsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeSubnets indicates an expected call of DescribeSubnets +func (mr *MockClientMockRecorder) DescribeSubnets(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeSubnets", reflect.TypeOf((*MockClient)(nil).DescribeSubnets), varargs...) +} + +// CreateCluster mocks base method +func (m *MockClient) CreateCluster(ctx context.Context, in *pb.CreateClusterRequest, opts ...grpc.CallOption) (*pb.CreateClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateCluster", varargs...) + ret0, _ := ret[0].(*pb.CreateClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCluster indicates an expected call of CreateCluster +func (mr *MockClientMockRecorder) CreateCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCluster", reflect.TypeOf((*MockClient)(nil).CreateCluster), varargs...) +} + +// CreateDebugCluster mocks base method +func (m *MockClient) CreateDebugCluster(ctx context.Context, in *pb.CreateClusterRequest, opts ...grpc.CallOption) (*pb.CreateClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugCluster", varargs...) + ret0, _ := ret[0].(*pb.CreateClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugCluster indicates an expected call of CreateDebugCluster +func (mr *MockClientMockRecorder) CreateDebugCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugCluster", reflect.TypeOf((*MockClient)(nil).CreateDebugCluster), varargs...) +} + +// ModifyCluster mocks base method +func (m *MockClient) ModifyCluster(ctx context.Context, in *pb.ModifyClusterRequest, opts ...grpc.CallOption) (*pb.ModifyClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyCluster", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyCluster indicates an expected call of ModifyCluster +func (mr *MockClientMockRecorder) ModifyCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyCluster", reflect.TypeOf((*MockClient)(nil).ModifyCluster), varargs...) +} + +// ModifyClusterNode mocks base method +func (m *MockClient) ModifyClusterNode(ctx context.Context, in *pb.ModifyClusterNodeRequest, opts ...grpc.CallOption) (*pb.ModifyClusterNodeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterNode", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterNodeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterNode indicates an expected call of ModifyClusterNode +func (mr *MockClientMockRecorder) ModifyClusterNode(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterNode", reflect.TypeOf((*MockClient)(nil).ModifyClusterNode), varargs...) +} + +// ModifyClusterAttributes mocks base method +func (m *MockClient) ModifyClusterAttributes(ctx context.Context, in *pb.ModifyClusterAttributesRequest, opts ...grpc.CallOption) (*pb.ModifyClusterAttributesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterAttributes", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterAttributesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterAttributes indicates an expected call of ModifyClusterAttributes +func (mr *MockClientMockRecorder) ModifyClusterAttributes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterAttributes", reflect.TypeOf((*MockClient)(nil).ModifyClusterAttributes), varargs...) +} + +// ModifyClusterNodeAttributes mocks base method +func (m *MockClient) ModifyClusterNodeAttributes(ctx context.Context, in *pb.ModifyClusterNodeAttributesRequest, opts ...grpc.CallOption) (*pb.ModifyClusterNodeAttributesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterNodeAttributes", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterNodeAttributesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterNodeAttributes indicates an expected call of ModifyClusterNodeAttributes +func (mr *MockClientMockRecorder) ModifyClusterNodeAttributes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterNodeAttributes", reflect.TypeOf((*MockClient)(nil).ModifyClusterNodeAttributes), varargs...) +} + +// AddTableClusterNodes mocks base method +func (m *MockClient) AddTableClusterNodes(ctx context.Context, in *pb.AddTableClusterNodesRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddTableClusterNodes", varargs...) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddTableClusterNodes indicates an expected call of AddTableClusterNodes +func (mr *MockClientMockRecorder) AddTableClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTableClusterNodes", reflect.TypeOf((*MockClient)(nil).AddTableClusterNodes), varargs...) +} + +// DeleteTableClusterNodes mocks base method +func (m *MockClient) DeleteTableClusterNodes(ctx context.Context, in *pb.DeleteTableClusterNodesRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteTableClusterNodes", varargs...) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteTableClusterNodes indicates an expected call of DeleteTableClusterNodes +func (mr *MockClientMockRecorder) DeleteTableClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTableClusterNodes", reflect.TypeOf((*MockClient)(nil).DeleteTableClusterNodes), varargs...) +} + +// DeleteClusters mocks base method +func (m *MockClient) DeleteClusters(ctx context.Context, in *pb.DeleteClustersRequest, opts ...grpc.CallOption) (*pb.DeleteClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteClusters", varargs...) + ret0, _ := ret[0].(*pb.DeleteClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteClusters indicates an expected call of DeleteClusters +func (mr *MockClientMockRecorder) DeleteClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClusters", reflect.TypeOf((*MockClient)(nil).DeleteClusters), varargs...) +} + +// UpgradeCluster mocks base method +func (m *MockClient) UpgradeCluster(ctx context.Context, in *pb.UpgradeClusterRequest, opts ...grpc.CallOption) (*pb.UpgradeClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpgradeCluster", varargs...) + ret0, _ := ret[0].(*pb.UpgradeClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpgradeCluster indicates an expected call of UpgradeCluster +func (mr *MockClientMockRecorder) UpgradeCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpgradeCluster", reflect.TypeOf((*MockClient)(nil).UpgradeCluster), varargs...) +} + +// RollbackCluster mocks base method +func (m *MockClient) RollbackCluster(ctx context.Context, in *pb.RollbackClusterRequest, opts ...grpc.CallOption) (*pb.RollbackClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RollbackCluster", varargs...) + ret0, _ := ret[0].(*pb.RollbackClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RollbackCluster indicates an expected call of RollbackCluster +func (mr *MockClientMockRecorder) RollbackCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackCluster", reflect.TypeOf((*MockClient)(nil).RollbackCluster), varargs...) +} + +// ResizeCluster mocks base method +func (m *MockClient) ResizeCluster(ctx context.Context, in *pb.ResizeClusterRequest, opts ...grpc.CallOption) (*pb.ResizeClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ResizeCluster", varargs...) + ret0, _ := ret[0].(*pb.ResizeClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResizeCluster indicates an expected call of ResizeCluster +func (mr *MockClientMockRecorder) ResizeCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResizeCluster", reflect.TypeOf((*MockClient)(nil).ResizeCluster), varargs...) +} + +// AddClusterNodes mocks base method +func (m *MockClient) AddClusterNodes(ctx context.Context, in *pb.AddClusterNodesRequest, opts ...grpc.CallOption) (*pb.AddClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.AddClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddClusterNodes indicates an expected call of AddClusterNodes +func (mr *MockClientMockRecorder) AddClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClusterNodes", reflect.TypeOf((*MockClient)(nil).AddClusterNodes), varargs...) +} + +// DeleteClusterNodes mocks base method +func (m *MockClient) DeleteClusterNodes(ctx context.Context, in *pb.DeleteClusterNodesRequest, opts ...grpc.CallOption) (*pb.DeleteClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.DeleteClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteClusterNodes indicates an expected call of DeleteClusterNodes +func (mr *MockClientMockRecorder) DeleteClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClusterNodes", reflect.TypeOf((*MockClient)(nil).DeleteClusterNodes), varargs...) +} + +// UpdateClusterEnv mocks base method +func (m *MockClient) UpdateClusterEnv(ctx context.Context, in *pb.UpdateClusterEnvRequest, opts ...grpc.CallOption) (*pb.UpdateClusterEnvResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateClusterEnv", varargs...) + ret0, _ := ret[0].(*pb.UpdateClusterEnvResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateClusterEnv indicates an expected call of UpdateClusterEnv +func (mr *MockClientMockRecorder) UpdateClusterEnv(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClusterEnv", reflect.TypeOf((*MockClient)(nil).UpdateClusterEnv), varargs...) +} + +// DescribeClusters mocks base method +func (m *MockClient) DescribeClusters(ctx context.Context, in *pb.DescribeClustersRequest, opts ...grpc.CallOption) (*pb.DescribeClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeClusters indicates an expected call of DescribeClusters +func (mr *MockClientMockRecorder) DescribeClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeClusters", reflect.TypeOf((*MockClient)(nil).DescribeClusters), varargs...) +} + +// DescribeDebugClusters mocks base method +func (m *MockClient) DescribeDebugClusters(ctx context.Context, in *pb.DescribeClustersRequest, opts ...grpc.CallOption) (*pb.DescribeClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugClusters indicates an expected call of DescribeDebugClusters +func (mr *MockClientMockRecorder) DescribeDebugClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugClusters", reflect.TypeOf((*MockClient)(nil).DescribeDebugClusters), varargs...) +} + +// DescribeAppClusters mocks base method +func (m *MockClient) DescribeAppClusters(ctx context.Context, in *pb.DescribeAppClustersRequest, opts ...grpc.CallOption) (*pb.DescribeAppClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppClusters indicates an expected call of DescribeAppClusters +func (mr *MockClientMockRecorder) DescribeAppClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppClusters", reflect.TypeOf((*MockClient)(nil).DescribeAppClusters), varargs...) +} + +// DescribeDebugAppClusters mocks base method +func (m *MockClient) DescribeDebugAppClusters(ctx context.Context, in *pb.DescribeAppClustersRequest, opts ...grpc.CallOption) (*pb.DescribeAppClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugAppClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugAppClusters indicates an expected call of DescribeDebugAppClusters +func (mr *MockClientMockRecorder) DescribeDebugAppClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugAppClusters", reflect.TypeOf((*MockClient)(nil).DescribeDebugAppClusters), varargs...) +} + +// DescribeClusterNodes mocks base method +func (m *MockClient) DescribeClusterNodes(ctx context.Context, in *pb.DescribeClusterNodesRequest, opts ...grpc.CallOption) (*pb.DescribeClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.DescribeClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeClusterNodes indicates an expected call of DescribeClusterNodes +func (mr *MockClientMockRecorder) DescribeClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeClusterNodes", reflect.TypeOf((*MockClient)(nil).DescribeClusterNodes), varargs...) +} + +// StopClusters mocks base method +func (m *MockClient) StopClusters(ctx context.Context, in *pb.StopClustersRequest, opts ...grpc.CallOption) (*pb.StopClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StopClusters", varargs...) + ret0, _ := ret[0].(*pb.StopClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StopClusters indicates an expected call of StopClusters +func (mr *MockClientMockRecorder) StopClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopClusters", reflect.TypeOf((*MockClient)(nil).StopClusters), varargs...) +} + +// StartClusters mocks base method +func (m *MockClient) StartClusters(ctx context.Context, in *pb.StartClustersRequest, opts ...grpc.CallOption) (*pb.StartClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StartClusters", varargs...) + ret0, _ := ret[0].(*pb.StartClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StartClusters indicates an expected call of StartClusters +func (mr *MockClientMockRecorder) StartClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartClusters", reflect.TypeOf((*MockClient)(nil).StartClusters), varargs...) +} + +// RecoverClusters mocks base method +func (m *MockClient) RecoverClusters(ctx context.Context, in *pb.RecoverClustersRequest, opts ...grpc.CallOption) (*pb.RecoverClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RecoverClusters", varargs...) + ret0, _ := ret[0].(*pb.RecoverClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecoverClusters indicates an expected call of RecoverClusters +func (mr *MockClientMockRecorder) RecoverClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecoverClusters", reflect.TypeOf((*MockClient)(nil).RecoverClusters), varargs...) +} + +// CeaseClusters mocks base method +func (m *MockClient) CeaseClusters(ctx context.Context, in *pb.CeaseClustersRequest, opts ...grpc.CallOption) (*pb.CeaseClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CeaseClusters", varargs...) + ret0, _ := ret[0].(*pb.CeaseClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CeaseClusters indicates an expected call of CeaseClusters +func (mr *MockClientMockRecorder) CeaseClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CeaseClusters", reflect.TypeOf((*MockClient)(nil).CeaseClusters), varargs...) +} + +// GetClusterStatistics mocks base method +func (m *MockClient) GetClusterStatistics(ctx context.Context, in *pb.GetClusterStatisticsRequest, opts ...grpc.CallOption) (*pb.GetClusterStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetClusterStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetClusterStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetClusterStatistics indicates an expected call of GetClusterStatistics +func (mr *MockClientMockRecorder) GetClusterStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterStatistics", reflect.TypeOf((*MockClient)(nil).GetClusterStatistics), varargs...) +} + +// SyncRepo mocks base method +func (m *MockClient) SyncRepo(ctx context.Context, in *pb.SyncRepoRequest, opts ...grpc.CallOption) (*pb.SyncRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SyncRepo", varargs...) + ret0, _ := ret[0].(*pb.SyncRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SyncRepo indicates an expected call of SyncRepo +func (mr *MockClientMockRecorder) SyncRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncRepo", reflect.TypeOf((*MockClient)(nil).SyncRepo), varargs...) +} + +// CreateApp mocks base method +func (m *MockClient) CreateApp(ctx context.Context, in *pb.CreateAppRequest, opts ...grpc.CallOption) (*pb.CreateAppResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateApp", varargs...) + ret0, _ := ret[0].(*pb.CreateAppResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateApp indicates an expected call of CreateApp +func (mr *MockClientMockRecorder) CreateApp(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateApp", reflect.TypeOf((*MockClient)(nil).CreateApp), varargs...) +} + +// ValidatePackage mocks base method +func (m *MockClient) ValidatePackage(ctx context.Context, in *pb.ValidatePackageRequest, opts ...grpc.CallOption) (*pb.ValidatePackageResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatePackage", varargs...) + ret0, _ := ret[0].(*pb.ValidatePackageResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatePackage indicates an expected call of ValidatePackage +func (mr *MockClientMockRecorder) ValidatePackage(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatePackage", reflect.TypeOf((*MockClient)(nil).ValidatePackage), varargs...) +} + +// GetAppStatistics mocks base method +func (m *MockClient) GetAppStatistics(ctx context.Context, in *pb.GetAppStatisticsRequest, opts ...grpc.CallOption) (*pb.GetAppStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetAppStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppStatistics indicates an expected call of GetAppStatistics +func (mr *MockClientMockRecorder) GetAppStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppStatistics", reflect.TypeOf((*MockClient)(nil).GetAppStatistics), varargs...) +} + +// DescribeApps mocks base method +func (m *MockClient) DescribeApps(ctx context.Context, in *pb.DescribeAppsRequest, opts ...grpc.CallOption) (*pb.DescribeAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeApps", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeApps indicates an expected call of DescribeApps +func (mr *MockClientMockRecorder) DescribeApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeApps", reflect.TypeOf((*MockClient)(nil).DescribeApps), varargs...) +} + +// DescribeActiveApps mocks base method +func (m *MockClient) DescribeActiveApps(ctx context.Context, in *pb.DescribeAppsRequest, opts ...grpc.CallOption) (*pb.DescribeAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeActiveApps", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeActiveApps indicates an expected call of DescribeActiveApps +func (mr *MockClientMockRecorder) DescribeActiveApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeActiveApps", reflect.TypeOf((*MockClient)(nil).DescribeActiveApps), varargs...) +} + +// ModifyApp mocks base method +func (m *MockClient) ModifyApp(ctx context.Context, in *pb.ModifyAppRequest, opts ...grpc.CallOption) (*pb.ModifyAppResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyApp", varargs...) + ret0, _ := ret[0].(*pb.ModifyAppResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyApp indicates an expected call of ModifyApp +func (mr *MockClientMockRecorder) ModifyApp(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyApp", reflect.TypeOf((*MockClient)(nil).ModifyApp), varargs...) +} + +// UploadAppAttachment mocks base method +func (m *MockClient) UploadAppAttachment(ctx context.Context, in *pb.UploadAppAttachmentRequest, opts ...grpc.CallOption) (*pb.UploadAppAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UploadAppAttachment", varargs...) + ret0, _ := ret[0].(*pb.UploadAppAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UploadAppAttachment indicates an expected call of UploadAppAttachment +func (mr *MockClientMockRecorder) UploadAppAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadAppAttachment", reflect.TypeOf((*MockClient)(nil).UploadAppAttachment), varargs...) +} + +// DeleteApps mocks base method +func (m *MockClient) DeleteApps(ctx context.Context, in *pb.DeleteAppsRequest, opts ...grpc.CallOption) (*pb.DeleteAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteApps", varargs...) + ret0, _ := ret[0].(*pb.DeleteAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteApps indicates an expected call of DeleteApps +func (mr *MockClientMockRecorder) DeleteApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteApps", reflect.TypeOf((*MockClient)(nil).DeleteApps), varargs...) +} + +// CreateAppVersion mocks base method +func (m *MockClient) CreateAppVersion(ctx context.Context, in *pb.CreateAppVersionRequest, opts ...grpc.CallOption) (*pb.CreateAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateAppVersion", varargs...) + ret0, _ := ret[0].(*pb.CreateAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateAppVersion indicates an expected call of CreateAppVersion +func (mr *MockClientMockRecorder) CreateAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAppVersion", reflect.TypeOf((*MockClient)(nil).CreateAppVersion), varargs...) +} + +// DescribeAppVersions mocks base method +func (m *MockClient) DescribeAppVersions(ctx context.Context, in *pb.DescribeAppVersionsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersions", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersions indicates an expected call of DescribeAppVersions +func (mr *MockClientMockRecorder) DescribeAppVersions(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersions", reflect.TypeOf((*MockClient)(nil).DescribeAppVersions), varargs...) +} + +// DescribeActiveAppVersions mocks base method +func (m *MockClient) DescribeActiveAppVersions(ctx context.Context, in *pb.DescribeAppVersionsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeActiveAppVersions", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeActiveAppVersions indicates an expected call of DescribeActiveAppVersions +func (mr *MockClientMockRecorder) DescribeActiveAppVersions(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeActiveAppVersions", reflect.TypeOf((*MockClient)(nil).DescribeActiveAppVersions), varargs...) +} + +// DescribeAppVersionAudits mocks base method +func (m *MockClient) DescribeAppVersionAudits(ctx context.Context, in *pb.DescribeAppVersionAuditsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionAuditsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersionAudits", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionAuditsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersionAudits indicates an expected call of DescribeAppVersionAudits +func (mr *MockClientMockRecorder) DescribeAppVersionAudits(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersionAudits", reflect.TypeOf((*MockClient)(nil).DescribeAppVersionAudits), varargs...) +} + +// DescribeAppVersionReviews mocks base method +func (m *MockClient) DescribeAppVersionReviews(ctx context.Context, in *pb.DescribeAppVersionReviewsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionReviewsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersionReviews", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionReviewsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersionReviews indicates an expected call of DescribeAppVersionReviews +func (mr *MockClientMockRecorder) DescribeAppVersionReviews(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersionReviews", reflect.TypeOf((*MockClient)(nil).DescribeAppVersionReviews), varargs...) +} + +// ModifyAppVersion mocks base method +func (m *MockClient) ModifyAppVersion(ctx context.Context, in *pb.ModifyAppVersionRequest, opts ...grpc.CallOption) (*pb.ModifyAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ModifyAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyAppVersion indicates an expected call of ModifyAppVersion +func (mr *MockClientMockRecorder) ModifyAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyAppVersion", reflect.TypeOf((*MockClient)(nil).ModifyAppVersion), varargs...) +} + +// GetAppVersionPackage mocks base method +func (m *MockClient) GetAppVersionPackage(ctx context.Context, in *pb.GetAppVersionPackageRequest, opts ...grpc.CallOption) (*pb.GetAppVersionPackageResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppVersionPackage", varargs...) + ret0, _ := ret[0].(*pb.GetAppVersionPackageResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppVersionPackage indicates an expected call of GetAppVersionPackage +func (mr *MockClientMockRecorder) GetAppVersionPackage(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppVersionPackage", reflect.TypeOf((*MockClient)(nil).GetAppVersionPackage), varargs...) +} + +// GetAppVersionPackageFiles mocks base method +func (m *MockClient) GetAppVersionPackageFiles(ctx context.Context, in *pb.GetAppVersionPackageFilesRequest, opts ...grpc.CallOption) (*pb.GetAppVersionPackageFilesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppVersionPackageFiles", varargs...) + ret0, _ := ret[0].(*pb.GetAppVersionPackageFilesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppVersionPackageFiles indicates an expected call of GetAppVersionPackageFiles +func (mr *MockClientMockRecorder) GetAppVersionPackageFiles(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppVersionPackageFiles", reflect.TypeOf((*MockClient)(nil).GetAppVersionPackageFiles), varargs...) +} + +// SubmitAppVersion mocks base method +func (m *MockClient) SubmitAppVersion(ctx context.Context, in *pb.SubmitAppVersionRequest, opts ...grpc.CallOption) (*pb.SubmitAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SubmitAppVersion", varargs...) + ret0, _ := ret[0].(*pb.SubmitAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitAppVersion indicates an expected call of SubmitAppVersion +func (mr *MockClientMockRecorder) SubmitAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAppVersion", reflect.TypeOf((*MockClient)(nil).SubmitAppVersion), varargs...) +} + +// CancelAppVersion mocks base method +func (m *MockClient) CancelAppVersion(ctx context.Context, in *pb.CancelAppVersionRequest, opts ...grpc.CallOption) (*pb.CancelAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CancelAppVersion", varargs...) + ret0, _ := ret[0].(*pb.CancelAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CancelAppVersion indicates an expected call of CancelAppVersion +func (mr *MockClientMockRecorder) CancelAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelAppVersion", reflect.TypeOf((*MockClient)(nil).CancelAppVersion), varargs...) +} + +// ReleaseAppVersion mocks base method +func (m *MockClient) ReleaseAppVersion(ctx context.Context, in *pb.ReleaseAppVersionRequest, opts ...grpc.CallOption) (*pb.ReleaseAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReleaseAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReleaseAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReleaseAppVersion indicates an expected call of ReleaseAppVersion +func (mr *MockClientMockRecorder) ReleaseAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseAppVersion", reflect.TypeOf((*MockClient)(nil).ReleaseAppVersion), varargs...) +} + +// DeleteAppVersion mocks base method +func (m *MockClient) DeleteAppVersion(ctx context.Context, in *pb.DeleteAppVersionRequest, opts ...grpc.CallOption) (*pb.DeleteAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAppVersion", varargs...) + ret0, _ := ret[0].(*pb.DeleteAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAppVersion indicates an expected call of DeleteAppVersion +func (mr *MockClientMockRecorder) DeleteAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAppVersion", reflect.TypeOf((*MockClient)(nil).DeleteAppVersion), varargs...) +} + +// IsvReviewAppVersion mocks base method +func (m *MockClient) IsvReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvReviewAppVersion indicates an expected call of IsvReviewAppVersion +func (mr *MockClientMockRecorder) IsvReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvReviewAppVersion", reflect.TypeOf((*MockClient)(nil).IsvReviewAppVersion), varargs...) +} + +// IsvPassAppVersion mocks base method +func (m *MockClient) IsvPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvPassAppVersion indicates an expected call of IsvPassAppVersion +func (mr *MockClientMockRecorder) IsvPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvPassAppVersion", reflect.TypeOf((*MockClient)(nil).IsvPassAppVersion), varargs...) +} + +// IsvRejectAppVersion mocks base method +func (m *MockClient) IsvRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvRejectAppVersion indicates an expected call of IsvRejectAppVersion +func (mr *MockClientMockRecorder) IsvRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvRejectAppVersion", reflect.TypeOf((*MockClient)(nil).IsvRejectAppVersion), varargs...) +} + +// BusinessReviewAppVersion mocks base method +func (m *MockClient) BusinessReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessReviewAppVersion indicates an expected call of BusinessReviewAppVersion +func (mr *MockClientMockRecorder) BusinessReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessReviewAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessReviewAppVersion), varargs...) +} + +// BusinessPassAppVersion mocks base method +func (m *MockClient) BusinessPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessPassAppVersion indicates an expected call of BusinessPassAppVersion +func (mr *MockClientMockRecorder) BusinessPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessPassAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessPassAppVersion), varargs...) +} + +// BusinessRejectAppVersion mocks base method +func (m *MockClient) BusinessRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessRejectAppVersion indicates an expected call of BusinessRejectAppVersion +func (mr *MockClientMockRecorder) BusinessRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessRejectAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessRejectAppVersion), varargs...) +} + +// TechnicalReviewAppVersion mocks base method +func (m *MockClient) TechnicalReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalReviewAppVersion indicates an expected call of TechnicalReviewAppVersion +func (mr *MockClientMockRecorder) TechnicalReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalReviewAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalReviewAppVersion), varargs...) +} + +// TechnicalPassAppVersion mocks base method +func (m *MockClient) TechnicalPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalPassAppVersion indicates an expected call of TechnicalPassAppVersion +func (mr *MockClientMockRecorder) TechnicalPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalPassAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalPassAppVersion), varargs...) +} + +// TechnicalRejectAppVersion mocks base method +func (m *MockClient) TechnicalRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalRejectAppVersion indicates an expected call of TechnicalRejectAppVersion +func (mr *MockClientMockRecorder) TechnicalRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalRejectAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalRejectAppVersion), varargs...) +} + +// AdminPassAppVersion mocks base method +func (m *MockClient) AdminPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AdminPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AdminPassAppVersion indicates an expected call of AdminPassAppVersion +func (mr *MockClientMockRecorder) AdminPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdminPassAppVersion", reflect.TypeOf((*MockClient)(nil).AdminPassAppVersion), varargs...) +} + +// AdminRejectAppVersion mocks base method +func (m *MockClient) AdminRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AdminRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AdminRejectAppVersion indicates an expected call of AdminRejectAppVersion +func (mr *MockClientMockRecorder) AdminRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdminRejectAppVersion", reflect.TypeOf((*MockClient)(nil).AdminRejectAppVersion), varargs...) +} + +// SuspendAppVersion mocks base method +func (m *MockClient) SuspendAppVersion(ctx context.Context, in *pb.SuspendAppVersionRequest, opts ...grpc.CallOption) (*pb.SuspendAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SuspendAppVersion", varargs...) + ret0, _ := ret[0].(*pb.SuspendAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SuspendAppVersion indicates an expected call of SuspendAppVersion +func (mr *MockClientMockRecorder) SuspendAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SuspendAppVersion", reflect.TypeOf((*MockClient)(nil).SuspendAppVersion), varargs...) +} + +// RecoverAppVersion mocks base method +func (m *MockClient) RecoverAppVersion(ctx context.Context, in *pb.RecoverAppVersionRequest, opts ...grpc.CallOption) (*pb.RecoverAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RecoverAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RecoverAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecoverAppVersion indicates an expected call of RecoverAppVersion +func (mr *MockClientMockRecorder) RecoverAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecoverAppVersion", reflect.TypeOf((*MockClient)(nil).RecoverAppVersion), varargs...) +} + +// CreateRepo mocks base method +func (m *MockClient) CreateRepo(ctx context.Context, in *pb.CreateRepoRequest, opts ...grpc.CallOption) (*pb.CreateRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRepo", varargs...) + ret0, _ := ret[0].(*pb.CreateRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRepo indicates an expected call of CreateRepo +func (mr *MockClientMockRecorder) CreateRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRepo", reflect.TypeOf((*MockClient)(nil).CreateRepo), varargs...) +} + +// DescribeRepos mocks base method +func (m *MockClient) DescribeRepos(ctx context.Context, in *pb.DescribeReposRequest, opts ...grpc.CallOption) (*pb.DescribeReposResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRepos", varargs...) + ret0, _ := ret[0].(*pb.DescribeReposResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRepos indicates an expected call of DescribeRepos +func (mr *MockClientMockRecorder) DescribeRepos(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRepos", reflect.TypeOf((*MockClient)(nil).DescribeRepos), varargs...) +} + +// ModifyRepo mocks base method +func (m *MockClient) ModifyRepo(ctx context.Context, in *pb.ModifyRepoRequest, opts ...grpc.CallOption) (*pb.ModifyRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRepo", varargs...) + ret0, _ := ret[0].(*pb.ModifyRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRepo indicates an expected call of ModifyRepo +func (mr *MockClientMockRecorder) ModifyRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRepo", reflect.TypeOf((*MockClient)(nil).ModifyRepo), varargs...) +} + +// DeleteRepos mocks base method +func (m *MockClient) DeleteRepos(ctx context.Context, in *pb.DeleteReposRequest, opts ...grpc.CallOption) (*pb.DeleteReposResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRepos", varargs...) + ret0, _ := ret[0].(*pb.DeleteReposResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRepos indicates an expected call of DeleteRepos +func (mr *MockClientMockRecorder) DeleteRepos(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRepos", reflect.TypeOf((*MockClient)(nil).DeleteRepos), varargs...) +} + +// ValidateRepo mocks base method +func (m *MockClient) ValidateRepo(ctx context.Context, in *pb.ValidateRepoRequest, opts ...grpc.CallOption) (*pb.ValidateRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateRepo", varargs...) + ret0, _ := ret[0].(*pb.ValidateRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateRepo indicates an expected call of ValidateRepo +func (mr *MockClientMockRecorder) ValidateRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRepo", reflect.TypeOf((*MockClient)(nil).ValidateRepo), varargs...) +} + +// DescribeCategories mocks base method +func (m *MockClient) DescribeCategories(ctx context.Context, in *pb.DescribeCategoriesRequest, opts ...grpc.CallOption) (*pb.DescribeCategoriesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeCategories", varargs...) + ret0, _ := ret[0].(*pb.DescribeCategoriesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeCategories indicates an expected call of DescribeCategories +func (mr *MockClientMockRecorder) DescribeCategories(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeCategories", reflect.TypeOf((*MockClient)(nil).DescribeCategories), varargs...) +} + +// CreateCategory mocks base method +func (m *MockClient) CreateCategory(ctx context.Context, in *pb.CreateCategoryRequest, opts ...grpc.CallOption) (*pb.CreateCategoryResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateCategory", varargs...) + ret0, _ := ret[0].(*pb.CreateCategoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCategory indicates an expected call of CreateCategory +func (mr *MockClientMockRecorder) CreateCategory(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCategory", reflect.TypeOf((*MockClient)(nil).CreateCategory), varargs...) +} + +// ModifyCategory mocks base method +func (m *MockClient) ModifyCategory(ctx context.Context, in *pb.ModifyCategoryRequest, opts ...grpc.CallOption) (*pb.ModifyCategoryResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyCategory", varargs...) + ret0, _ := ret[0].(*pb.ModifyCategoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyCategory indicates an expected call of ModifyCategory +func (mr *MockClientMockRecorder) ModifyCategory(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyCategory", reflect.TypeOf((*MockClient)(nil).ModifyCategory), varargs...) +} + +// DeleteCategories mocks base method +func (m *MockClient) DeleteCategories(ctx context.Context, in *pb.DeleteCategoriesRequest, opts ...grpc.CallOption) (*pb.DeleteCategoriesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteCategories", varargs...) + ret0, _ := ret[0].(*pb.DeleteCategoriesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteCategories indicates an expected call of DeleteCategories +func (mr *MockClientMockRecorder) DeleteCategories(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCategories", reflect.TypeOf((*MockClient)(nil).DeleteCategories), varargs...) +} + +// CreateAttachment mocks base method +func (m *MockClient) CreateAttachment(ctx context.Context, in *pb.CreateAttachmentRequest, opts ...grpc.CallOption) (*pb.CreateAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateAttachment", varargs...) + ret0, _ := ret[0].(*pb.CreateAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateAttachment indicates an expected call of CreateAttachment +func (mr *MockClientMockRecorder) CreateAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAttachment", reflect.TypeOf((*MockClient)(nil).CreateAttachment), varargs...) +} + +// AppendAttachment mocks base method +func (m *MockClient) AppendAttachment(ctx context.Context, in *pb.AppendAttachmentRequest, opts ...grpc.CallOption) (*pb.AppendAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AppendAttachment", varargs...) + ret0, _ := ret[0].(*pb.AppendAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AppendAttachment indicates an expected call of AppendAttachment +func (mr *MockClientMockRecorder) AppendAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendAttachment", reflect.TypeOf((*MockClient)(nil).AppendAttachment), varargs...) +} + +// ReplaceAttachment mocks base method +func (m *MockClient) ReplaceAttachment(ctx context.Context, in *pb.ReplaceAttachmentRequest, opts ...grpc.CallOption) (*pb.ReplaceAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReplaceAttachment", varargs...) + ret0, _ := ret[0].(*pb.ReplaceAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceAttachment indicates an expected call of ReplaceAttachment +func (mr *MockClientMockRecorder) ReplaceAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceAttachment", reflect.TypeOf((*MockClient)(nil).ReplaceAttachment), varargs...) +} + +// GetAttachments mocks base method +func (m *MockClient) GetAttachments(ctx context.Context, in *pb.GetAttachmentsRequest, opts ...grpc.CallOption) (*pb.GetAttachmentsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAttachments", varargs...) + ret0, _ := ret[0].(*pb.GetAttachmentsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAttachments indicates an expected call of GetAttachments +func (mr *MockClientMockRecorder) GetAttachments(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachments", reflect.TypeOf((*MockClient)(nil).GetAttachments), varargs...) +} + +// DeleteAttachments mocks base method +func (m *MockClient) DeleteAttachments(ctx context.Context, in *pb.DeleteAttachmentsRequest, opts ...grpc.CallOption) (*pb.DeleteAttachmentsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAttachments", varargs...) + ret0, _ := ret[0].(*pb.DeleteAttachmentsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAttachments indicates an expected call of DeleteAttachments +func (mr *MockClientMockRecorder) DeleteAttachments(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAttachments", reflect.TypeOf((*MockClient)(nil).DeleteAttachments), varargs...) +} + +// IndexRepo mocks base method +func (m *MockClient) IndexRepo(ctx context.Context, in *pb.IndexRepoRequest, opts ...grpc.CallOption) (*pb.IndexRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IndexRepo", varargs...) + ret0, _ := ret[0].(*pb.IndexRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexRepo indicates an expected call of IndexRepo +func (mr *MockClientMockRecorder) IndexRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexRepo", reflect.TypeOf((*MockClient)(nil).IndexRepo), varargs...) +} + +// DescribeRepoEvents mocks base method +func (m *MockClient) DescribeRepoEvents(ctx context.Context, in *pb.DescribeRepoEventsRequest, opts ...grpc.CallOption) (*pb.DescribeRepoEventsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRepoEvents", varargs...) + ret0, _ := ret[0].(*pb.DescribeRepoEventsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRepoEvents indicates an expected call of DescribeRepoEvents +func (mr *MockClientMockRecorder) DescribeRepoEvents(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRepoEvents", reflect.TypeOf((*MockClient)(nil).DescribeRepoEvents), varargs...) +} diff --git a/pkg/simple/client/openpitrix/openpitrixclient.go b/pkg/simple/client/openpitrix/openpitrix.go similarity index 77% rename from pkg/simple/client/openpitrix/openpitrixclient.go rename to pkg/simple/client/openpitrix/openpitrix.go index 3c8e7226e..b919c6ffc 100644 --- a/pkg/simple/client/openpitrix/openpitrixclient.go +++ b/pkg/simple/client/openpitrix/openpitrix.go @@ -30,16 +30,17 @@ import ( ) const ( - KubernetesProvider = "kubernetes" - Unknown = "-" - DeploySuffix = "-Deployment" - DaemonSuffix = "-DaemonSet" - StateSuffix = "-StatefulSet" - SystemUsername = "system" - SystemUserPath = ":system" + RuntimeAnnotationKey = "openpitrix_runtime" + KubernetesProvider = "kubernetes" + Unknown = "-" + DeploySuffix = "-Deployment" + DaemonSuffix = "-DaemonSet" + StateSuffix = "-StatefulSet" + SystemUsername = "system" + SystemUserPath = ":system" ) -type Interface interface { +type Client interface { pb.RuntimeManagerClient pb.ClusterManagerClient pb.AppManagerClient @@ -49,14 +50,14 @@ type Interface interface { pb.RepoIndexerClient } -type Client struct { - runtime pb.RuntimeManagerClient - cluster pb.ClusterManagerClient - app pb.AppManagerClient - repo pb.RepoManagerClient - category pb.CategoryManagerClient - attachment pb.AttachmentManagerClient - repoIndexer pb.RepoIndexerClient +type client struct { + pb.RuntimeManagerClient + pb.ClusterManagerClient + pb.AppManagerClient + pb.RepoManagerClient + pb.CategoryManagerClient + pb.AttachmentManagerClient + pb.RepoIndexerClient } func parseToHostPort(endpoint string) (string, int, error) { @@ -133,7 +134,7 @@ func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) { return pb.NewAppManagerClient(conn), nil } -func NewOpenPitrixClient(options *Options) (*Client, error) { +func NewOpenPitrixClient(options *Options) (Client, error) { runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint) @@ -184,42 +185,18 @@ func NewOpenPitrixClient(options *Options) (*Client, error) { return nil, err } - client := Client{ - runtime: runtimeMangerClient, - cluster: clusterManagerClient, - repo: repoManagerClient, - app: appManagerClient, - category: categoryManagerClient, - attachment: attachmentManagerClient, - repoIndexer: repoIndexerClient, + client := client{ + RuntimeManagerClient: runtimeMangerClient, + ClusterManagerClient: clusterManagerClient, + RepoManagerClient: repoManagerClient, + AppManagerClient: appManagerClient, + CategoryManagerClient: categoryManagerClient, + AttachmentManagerClient: attachmentManagerClient, + RepoIndexerClient: repoIndexerClient, } return &client, nil } -func (c *Client) Runtime() pb.RuntimeManagerClient { - return c.runtime -} -func (c *Client) App() pb.AppManagerClient { - return c.app -} -func (c *Client) Cluster() pb.ClusterManagerClient { - return c.cluster -} -func (c *Client) Category() pb.CategoryManagerClient { - return c.category -} - -func (c *Client) Repo() pb.RepoManagerClient { - return c.repo -} - -func (c *Client) RepoIndexer() pb.RepoIndexerClient { - return c.repoIndexer -} - -func (c *Client) Attachment() pb.AttachmentManagerClient { - return c.attachment -} func SystemContext() context.Context { ctx := context.Background() diff --git a/pkg/utils/k8sutil/k8sutil.go b/pkg/utils/k8sutil/k8sutil.go index 7d01fa424..89c1db73f 100644 --- a/pkg/utils/k8sutil/k8sutil.go +++ b/pkg/utils/k8sutil/k8sutil.go @@ -20,7 +20,7 @@ package k8sutil import ( "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/iam" ) func IsControlledBy(reference []metav1.OwnerReference, kind string, name string) bool { @@ -55,15 +55,15 @@ func ContainsUser(subjects interface{}, username string) bool { return true } } - case []models.User: - for _, u := range subjects.([]models.User) { + case []iam.User: + for _, u := range subjects.([]iam.User) { if u.Username == username { return true } } - case []*models.User: - for _, u := range subjects.([]*models.User) { + case []*iam.User: + for _, u := range subjects.([]*iam.User) { if u.Username == username { return true }