[WIP] API refactor (#1737)

* refactor openpitrix API

Signed-off-by: hongming <talonwan@yunify.com>

* add openpitrix mock client

Signed-off-by: hongming <talonwan@yunify.com>

* refactor tenant API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-01-13 13:36:21 +08:00
committed by zryfish
parent c40d1542a2
commit 71849f028f
66 changed files with 5415 additions and 4366 deletions

2
go.mod
View File

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

View File

@@ -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 <bg@example.com>"
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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <bg@example.com>"
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,

View File

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

View File

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

View File

@@ -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(&params.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(&params.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)
}

View File

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

View File

@@ -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(&params.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)
}

View File

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

View File

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

View File

@@ -77,6 +77,7 @@ const (
ComponentMetricsTag = "Component Metrics"
LogQueryTag = "Log Query"
FluentBitSetting = "Fluent Bit Setting"
TerminalTag = "Terminal"
)
var (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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(&params.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(&params.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(&params.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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

61
pkg/models/iam/types.go Normal file
View File

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

180
pkg/models/iam/utils.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
ns, err := t.ListNamespaces(username, &params.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0)
if err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
}
@@ -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

View File

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

View File

@@ -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"`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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