提交 71849f02 编写于 作者: H hongming 提交者: zryfish

[WIP] API refactor (#1737)

* refactor openpitrix API
Signed-off-by: Nhongming <talonwan@yunify.com>

* add openpitrix mock client
Signed-off-by: Nhongming <talonwan@yunify.com>

* refactor tenant API
Signed-off-by: Nhongming <talonwan@yunify.com>

* refactor IAM API
Signed-off-by: Nhongming <talonwan@yunify.com>

* refactor IAM API
Signed-off-by: Nhongming <talonwan@yunify.com>
上级 c40d1542
...@@ -39,6 +39,7 @@ require ( ...@@ -39,6 +39,7 @@ require (
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6
github.com/golang/example v0.0.0-20170904185048-46695d81d1fa 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/golang/protobuf v1.3.2
github.com/google/go-cmp v0.3.0 github.com/google/go-cmp v0.3.0
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
...@@ -79,6 +80,7 @@ require ( ...@@ -79,6 +80,7 @@ require (
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
github.com/xanzy/ssh-agent v0.2.1 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 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 google.golang.org/grpc v1.23.1
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
......
/*
*
* 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
}
package api package api
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"net/http" "net/http"
) )
func HandleInternalError(response *restful.Response, err error) { 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 HandleBadRequest(response *restful.Response, err error) {
} }
func HandleNotFound(response *restful.Response, err error) {
}
func HandleForbidden(response *restful.Response, err error) {
}
...@@ -32,7 +32,7 @@ type Dependencies struct { ...@@ -32,7 +32,7 @@ type Dependencies struct {
// Injected Dependencies // Injected Dependencies
KubeClient k8s.Client KubeClient k8s.Client
S3 s3.Interface S3 s3.Interface
OpenPitrix openpitrix.Interface OpenPitrix openpitrix.Client
Monitoring monitoring.Interface Monitoring monitoring.Interface
Logging logging.Interface Logging logging.Interface
Devops devops.Interface Devops devops.Interface
......
...@@ -16,185 +16,3 @@ ...@@ -16,185 +16,3 @@
*/ */
package iam 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)
}
...@@ -19,38 +19,15 @@ package iam ...@@ -19,38 +19,15 @@ package iam
import ( import (
"fmt" "fmt"
"github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful" "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"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/utils/iputil" "kubesphere.io/kubesphere/pkg/utils/iputil"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
"net/http" "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 { type OAuthRequest struct {
GrantType string `json:"grant_type"` GrantType string `json:"grant_type"`
Username string `json:"username,omitempty" description:"username"` Username string `json:"username,omitempty" description:"username"`
...@@ -58,36 +35,6 @@ type OAuthRequest struct { ...@@ -58,36 +35,6 @@ type OAuthRequest struct {
RefreshToken string `json:"refresh_token,omitempty"` 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) { func OAuth(req *restful.Request, resp *restful.Response) {
authRequest := &OAuthRequest{} authRequest := &OAuthRequest{}
...@@ -120,72 +67,3 @@ func OAuth(req *restful.Request, resp *restful.Response) { ...@@ -120,72 +67,3 @@ func OAuth(req *restful.Request, resp *restful.Response) {
resp.WriteEntity(result) 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
}
...@@ -136,7 +136,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) { ...@@ -136,7 +136,7 @@ func ListGroupUsers(req *restful.Request, resp *restful.Response) {
return return
} }
users := make([]*models.User, 0) users := make([]*iam.User, 0)
modify := false modify := false
......
...@@ -29,60 +29,10 @@ import ( ...@@ -29,60 +29,10 @@ import (
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors" "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) { func DeleteUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("user") username := req.PathParameter("user")
...@@ -110,7 +60,7 @@ func UpdateUser(req *restful.Request, resp *restful.Response) { ...@@ -110,7 +60,7 @@ func UpdateUser(req *restful.Request, resp *restful.Response) {
usernameInPath := req.PathParameter("user") usernameInPath := req.PathParameter("user")
usernameInHeader := req.HeaderParameter(constants.UserNameHeader) usernameInHeader := req.HeaderParameter(constants.UserNameHeader)
var user models.User var user iam.User
err := req.ReadEntity(&user) err := req.ReadEntity(&user)
...@@ -254,8 +204,8 @@ func DescribeUser(req *restful.Request, resp *restful.Response) { ...@@ -254,8 +204,8 @@ func DescribeUser(req *restful.Request, resp *restful.Response) {
} }
result := struct { result := struct {
*models.User *iam.User
ClusterRules []models.SimpleRule `json:"cluster_rules"` ClusterRules []iam.SimpleRule `json:"cluster_rules"`
}{ }{
User: user, User: user,
ClusterRules: clusterRules, ClusterRules: clusterRules,
......
...@@ -21,7 +21,6 @@ import ( ...@@ -21,7 +21,6 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
k8serr "k8s.io/apimachinery/pkg/api/errors" k8serr "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/workspaces" "kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
...@@ -113,7 +112,7 @@ func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) { ...@@ -113,7 +112,7 @@ func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) {
func InviteUser(req *restful.Request, resp *restful.Response) { func InviteUser(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
var user models.User var user iam.User
err := req.ReadEntity(&user) err := req.ReadEntity(&user)
if err != nil { if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
......
/*
*
* 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)
}
/*
*
* 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)
}
/*
*
* 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)
}
/*
*
* 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)
}
/*
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
}
...@@ -77,6 +77,7 @@ const ( ...@@ -77,6 +77,7 @@ const (
ComponentMetricsTag = "Component Metrics" ComponentMetricsTag = "Component Metrics"
LogQueryTag = "Log Query" LogQueryTag = "Log Query"
FluentBitSetting = "Fluent Bit Setting" FluentBitSetting = "Fluent Bit Setting"
TerminalTag = "Terminal"
) )
var ( var (
......
...@@ -100,7 +100,7 @@ var _ reconcile.Reconciler = &ReconcileNamespace{} ...@@ -100,7 +100,7 @@ var _ reconcile.Reconciler = &ReconcileNamespace{}
// ReconcileNamespace reconciles a Namespace object // ReconcileNamespace reconciles a Namespace object
type ReconcileNamespace struct { type ReconcileNamespace struct {
client.Client client.Client
openpitrixClient *openpitrix.Client openpitrixClient openpitrix.Client
scheme *runtime.Scheme scheme *runtime.Scheme
} }
...@@ -361,7 +361,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) ...@@ -361,7 +361,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace)
adminKubeConfigName := fmt.Sprintf("kubeconfig-%s", constants.AdminUserName) 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 { if err != nil {
klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err))
...@@ -382,7 +382,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) ...@@ -382,7 +382,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace)
return err 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}, Name: &wrappers.StringValue{Value: adminKubeConfigName},
Provider: &wrappers.StringValue{Value: "kubernetes"}, Provider: &wrappers.StringValue{Value: "kubernetes"},
Description: &wrappers.StringValue{Value: "kubeconfig"}, Description: &wrappers.StringValue{Value: "kubeconfig"},
...@@ -399,7 +399,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) ...@@ -399,7 +399,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace)
} }
// TODO runtime id is invalid when recreate runtime // 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}, Name: &wrappers.StringValue{Value: namespace.Name},
RuntimeCredentialId: &wrappers.StringValue{Value: kubesphereRuntimeCredentialId}, RuntimeCredentialId: &wrappers.StringValue{Value: kubesphereRuntimeCredentialId},
Provider: &wrappers.StringValue{Value: openpitrix.KubernetesProvider}, Provider: &wrappers.StringValue{Value: openpitrix.KubernetesProvider},
...@@ -420,7 +420,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) ...@@ -420,7 +420,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace)
func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error { func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error {
if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { 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) { if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) {
return nil return nil
......
/*
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
/*
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))
}
package v1alpha2 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)
}
...@@ -22,10 +22,13 @@ import ( ...@@ -22,10 +22,13 @@ import (
"github.com/emicklei/go-restful-openapi" "github.com/emicklei/go-restful-openapi"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "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/iam"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
iam2 "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy" "kubesphere.io/kubesphere/pkg/models/iam/policy"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"net/http" "net/http"
...@@ -118,174 +121,169 @@ type DescribeWorkspaceUserResponse struct { ...@@ -118,174 +121,169 @@ type DescribeWorkspaceUserResponse struct {
func addWebService(c *restful.Container) error { func addWebService(c *restful.Container) error {
ws := runtime.NewWebService(GroupVersion) ws := runtime.NewWebService(GroupVersion)
ok := "ok" handler := newIAMHandler()
ws.Route(ws.POST("/authenticate"). 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."). 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{}). Reads(iamv1alpha2.TokenReview{}).
Returns(http.StatusOK, ok, iam.TokenReview{}). Returns(http.StatusOK, api.StatusOK, iamv1alpha2.TokenReview{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.POST("/login"). 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."). 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{}). Reads(iamv1alpha2.LoginRequest{}).
Returns(http.StatusOK, ok, models.AuthGrantResponse{}). Returns(http.StatusOK, api.StatusOK, models.AuthGrantResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.POST("/token"). ws.Route(ws.POST("/users").
To(iam.OAuth). To(handler.CreateUser).
Doc("OAuth API,only support resource owner password credentials grant"). Doc("Create a user account.").
Reads(iam.LoginRequest{}). Reads(CreateUserRequest{}).
Returns(http.StatusOK, ok, models.AuthGrantResponse{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}"). ws.Route(ws.GET("/users/{user}").
To(iam.DescribeUser). To(iam.DescribeUser).
Doc("Describe the specified user."). Doc("Describe the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, ok, models.User{}). Returns(http.StatusOK, api.StatusOK, iam2.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{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.DELETE("/users/{user}"). ws.Route(ws.DELETE("/users/{user}").
To(iam.DeleteUser). To(iam.DeleteUser).
Doc("Delete the specified user."). Doc("Delete the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, ok, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.PUT("/users/{user}"). ws.Route(ws.PUT("/users/{user}").
To(iam.UpdateUser). To(iam.UpdateUser).
Doc("Update information about the specified user."). Doc("Update information about the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Reads(UserUpdateRequest{}). Reads(UserUpdateRequest{}).
Returns(http.StatusOK, ok, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}/logs"). ws.Route(ws.GET("/users/{user}/logs").
To(iam.UserLoginLogs). To(iam.UserLoginLogs).
Doc("Retrieve the \"login logs\" for the specified user."). Doc("Retrieve the \"login logs\" for the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, ok, LoginLog{}). Returns(http.StatusOK, api.StatusOK, LoginLog{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users"). ws.Route(ws.GET("/users").
To(iam.ListUsers). To(iam.ListUsers).
Doc("List all users."). Doc("List all users.").
Returns(http.StatusOK, ok, UserList{}). Returns(http.StatusOK, api.StatusOK, UserList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag}))
ws.Route(ws.GET("/users/{user}/roles"). ws.Route(ws.GET("/users/{user}/roles").
To(iam.ListUserRoles). To(iam.ListUserRoles).
Doc("Retrieve all the roles that are assigned to the specified user."). Doc("Retrieve all the roles that are assigned to the specified user.").
Param(ws.PathParameter("user", "username")). Param(ws.PathParameter("user", "username")).
Returns(http.StatusOK, ok, iam.RoleList{}). Returns(http.StatusOK, api.StatusOK, iam.RoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles"). ws.Route(ws.GET("/namespaces/{namespace}/roles").
To(iam.ListRoles). To(iam.ListRoles).
Doc("Retrieve the roles that are assigned to the user in the specified namespace."). Doc("Retrieve the roles that are assigned to the user in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Returns(http.StatusOK, ok, RoleList{}). Returns(http.StatusOK, api.StatusOK, RoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles"). ws.Route(ws.GET("/clusterroles").
To(iam.ListClusterRoles). To(iam.ListClusterRoles).
Doc("List all cluster roles."). Doc("List all cluster roles.").
Returns(http.StatusOK, ok, ClusterRoleList{}). Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users"). 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."). Doc("Retrieve the users that are bound to the role in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Param(ws.PathParameter("role", "role name")). Param(ws.PathParameter("role", "role name")).
Returns(http.StatusOK, ok, []NamespacedUser{}). Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/users"). ws.Route(ws.GET("/namespaces/{namespace}/users").
To(iam.ListNamespaceUsers). To(iam.ListNamespaceUsers).
Doc("List all users in the specified namespace."). Doc("List all users in the specified namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Returns(http.StatusOK, ok, []NamespacedUser{}). Returns(http.StatusOK, api.StatusOK, []NamespacedUser{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). ws.Route(ws.GET("/clusterroles/{clusterrole}/users").
To(iam.ListClusterRoleUsers). To(iam.ListClusterRoleUsers).
Doc("List all users that are bound to the specified cluster role."). Doc("List all users that are bound to the specified cluster role.").
Param(ws.PathParameter("clusterrole", "cluster role name")). Param(ws.PathParameter("clusterrole", "cluster role name")).
Returns(http.StatusOK, ok, UserList{}). Returns(http.StatusOK, api.StatusOK, UserList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/clusterroles/{clusterrole}/rules"). ws.Route(ws.GET("/clusterroles/{clusterrole}/rules").
To(iam.ListClusterRoleRules). To(iam.ListClusterRoleRules).
Doc("List all policy rules of the specified cluster role."). Doc("List all policy rules of the specified cluster role.").
Param(ws.PathParameter("clusterrole", "cluster role name")). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules"). ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules").
To(iam.ListRoleRules). To(iam.ListRoleRules).
Doc("List all policy rules of the specified role in the given namespace."). Doc("List all policy rules of the specified role in the given namespace.").
Param(ws.PathParameter("namespace", "kubernetes namespace")). Param(ws.PathParameter("namespace", "kubernetes namespace")).
Param(ws.PathParameter("role", "role name")). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules"). ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules").
To(iam.ListDevopsRoleRules). To(iam.ListDevopsRoleRules).
Doc("List all policy rules of the specified role in the given devops project."). Doc("List all policy rules of the specified role in the given devops project.").
Param(ws.PathParameter("devops", "devops project ID")). Param(ws.PathParameter("devops", "devops project ID")).
Param(ws.PathParameter("role", "devops role name")). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/rulesmapping/clusterroles"). ws.Route(ws.GET("/rulesmapping/clusterroles").
To(iam.ClusterRulesMapping). To(iam.ClusterRulesMapping).
Doc("Get the mapping relationships between cluster roles and policy rules."). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/rulesmapping/roles"). ws.Route(ws.GET("/rulesmapping/roles").
To(iam.RulesMapping). To(iam.RulesMapping).
Doc("Get the mapping relationships between namespaced roles and policy rules."). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles"). ws.Route(ws.GET("/workspaces/{workspace}/roles").
To(iam.ListWorkspaceRoles). To(iam.ListWorkspaceRoles).
Doc("List all workspace roles."). Doc("List all workspace roles.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, ok, ClusterRoleList{}). Returns(http.StatusOK, api.StatusOK, ClusterRoleList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}"). ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}").
To(iam.DescribeWorkspaceRole). To(iam.DescribeWorkspaceRole).
Doc("Describe the workspace role."). Doc("Describe the workspace role.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules"). ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules").
To(iam.ListWorkspaceRoleRules). To(iam.ListWorkspaceRoleRules).
Doc("List all policy rules of the specified workspace role."). Doc("List all policy rules of the specified workspace role.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("role", "workspace role 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members"). ws.Route(ws.GET("/workspaces/{workspace}/members").
To(iam.ListWorkspaceUsers). To(iam.ListWorkspaceUsers).
Doc("List all members in the specified workspace."). Doc("List all members in the specified workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Returns(http.StatusOK, ok, UserList{}). Returns(http.StatusOK, api.StatusOK, UserList{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.POST("/workspaces/{workspace}/members"). ws.Route(ws.POST("/workspaces/{workspace}/members").
To(iam.InviteUser). To(iam.InviteUser).
Doc("Invite a member to the specified workspace."). Doc("Invite a member to the specified workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Reads(InviteUserRequest{}). Reads(InviteUserRequest{}).
Returns(http.StatusOK, ok, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}").
To(iam.RemoveUser). To(iam.RemoveUser).
Doc("Remove the specified member from the workspace."). Doc("Remove the specified member from the workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "username")). Param(ws.PathParameter("member", "username")).
Returns(http.StatusOK, ok, errors.Error{}). Returns(http.StatusOK, api.StatusOK, errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}").
To(iam.DescribeWorkspaceUser). To(iam.DescribeWorkspaceUser).
Doc("Describe the specified user in the given workspace."). Doc("Describe the specified user in the given workspace.").
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "username")). Param(ws.PathParameter("member", "username")).
Returns(http.StatusOK, ok, DescribeWorkspaceUserResponse{}). Returns(http.StatusOK, api.StatusOK, DescribeWorkspaceUserResponse{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag}))
c.Add(ws) c.Add(ws)
return nil return nil
......
...@@ -26,6 +26,7 @@ func InstallAPIs(container *restful.Container, client k8s.Client) { ...@@ -26,6 +26,7 @@ func InstallAPIs(container *restful.Container, client k8s.Client) {
urlruntime.Must(resourcesv1alpha2.AddToContainer(container, client)) urlruntime.Must(resourcesv1alpha2.AddToContainer(container, client))
urlruntime.Must(tenantv1alpha2.AddToContainer(container)) urlruntime.Must(tenantv1alpha2.AddToContainer(container))
urlruntime.Must(terminalv1alpha2.AddToContainer(container)) urlruntime.Must(terminalv1alpha2.AddToContainer(container))
urlruntime.Must(tenantv1alpha2.AddToContainer(container))
} }
func InstallAuthorizationAPIs(container *restful.Container) { func InstallAuthorizationAPIs(container *restful.Container) {
......
/*
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
/*
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))
}
此差异已折叠。
此差异已折叠。
/*
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
/*
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))
}
...@@ -20,19 +20,21 @@ package v1alpha2 ...@@ -20,19 +20,21 @@ package v1alpha2
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"net/http" "net/http"
) )
const GroupName = "operations.kubesphere.io" const (
GroupName = "operations.kubesphere.io"
)
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
func AddToContainer(c *restful.Container, client k8s.Client) error { func AddToContainer(c *restful.Container, client k8s.Client) error {
ok := "ok"
webservice := runtime.NewWebService(GroupVersion) webservice := runtime.NewWebService(GroupVersion)
handler := newOperationHandler(client) handler := newOperationHandler(client)
...@@ -45,7 +47,7 @@ func AddToContainer(c *restful.Container, client k8s.Client) error { ...@@ -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.PathParameter("namespace", "the name of the namespace where the job runs in")).
Param(webservice.QueryParameter("action", "action must be \"rerun\"")). Param(webservice.QueryParameter("action", "action must be \"rerun\"")).
Param(webservice.QueryParameter("resourceVersion", "version of job, rerun when the version matches").Required(true)). 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) c.Add(webservice)
......
...@@ -54,18 +54,18 @@ func (r *resourceHandler) handleGetNamespacedResources(request *restful.Request, ...@@ -54,18 +54,18 @@ func (r *resourceHandler) handleGetNamespacedResources(request *restful.Request,
func (r *resourceHandler) handleListNamespaceResources(request *restful.Request, response *restful.Response) { func (r *resourceHandler) handleListNamespaceResources(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace") namespace := request.PathParameter("namespace")
resourceName := request.PathParameter("resources") resource := request.PathParameter("resources")
conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam))
orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, v1alpha2.CreateTime) orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam)) limit, offset := params.ParsePaging(request)
reverse := params.ParseReverse(request) reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, false)
conditions, err := params.ParseConditions(request)
if err != nil { if err != nil {
response.WriteHeaderAndEntity(http.StatusBadRequest, err) response.WriteHeaderAndEntity(http.StatusBadRequest, err)
return 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 { if err != nil {
api.HandleInternalError(response, err) api.HandleInternalError(response, err)
......
/*
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
/*
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))
}
package v1alpha2 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
}
...@@ -22,13 +22,14 @@ import ( ...@@ -22,13 +22,14 @@ import (
"github.com/emicklei/go-restful-openapi" "github.com/emicklei/go-restful-openapi"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/apiserver/tenant"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
...@@ -37,78 +38,72 @@ import ( ...@@ -37,78 +38,72 @@ import (
const ( const (
GroupName = "tenant.kubesphere.io" GroupName = "tenant.kubesphere.io"
RespOK = "ok"
) )
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var ( func AddToContainer(c *restful.Container) error {
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
ok := "ok"
ws := runtime.NewWebService(GroupVersion) ws := runtime.NewWebService(GroupVersion)
handler := newTenantHandler()
ws.Route(ws.GET("/workspaces"). ws.Route(ws.GET("/workspaces").
To(tenant.ListWorkspaces). To(handler.ListWorkspaces).
Returns(http.StatusOK, ok, models.PageableResponse{}). Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}).
Doc("List all workspaces that belongs to the current user"). Doc("List all workspaces that belongs to the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}"). ws.Route(ws.GET("/workspaces/{workspace}").
To(tenant.DescribeWorkspace). To(handler.DescribeWorkspace).
Doc("Describe the specified workspace"). Doc("Describe the specified workspace").
Param(ws.PathParameter("workspace", "workspace name")). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/rules"). ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(tenant.ListWorkspaceRules). To(handler.ListWorkspaceRules).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the rules of the specified workspace for the current user"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/namespaces/{namespace}/rules"). ws.Route(ws.GET("/namespaces/{namespace}/rules").
To(tenant.ListNamespaceRules). To(handler.ListNamespaceRules).
Param(ws.PathParameter("namespace", "the name of the namespace")). Param(ws.PathParameter("namespace", "the name of the namespace")).
Doc("List the rules of the specified namespace for the current user"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/devops/{devops}/rules"). ws.Route(ws.GET("/devops/{devops}/rules").
To(tenant.ListDevopsRules). To(handler.ListDevopsRules).
Param(ws.PathParameter("devops", "devops project ID")). Param(ws.PathParameter("devops", "devops project ID")).
Doc("List the rules of the specified DevOps project for the current user"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces"). ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(tenant.ListNamespaces). To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/namespaces"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/namespaces").
To(tenant.ListNamespacesByUsername). To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "workspace member's username")). Param(ws.PathParameter("member", "workspace member's username")).
Doc("List the namespaces for the workspace member"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.POST("/workspaces/{workspace}/namespaces"). ws.Route(ws.POST("/workspaces/{workspace}/namespaces").
To(tenant.CreateNamespace). To(handler.CreateNamespace).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create a namespace in the specified workspace"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}"). ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
To(tenant.DeleteNamespace). To(handler.DeleteNamespace).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("namespace", "the name of the namespace")). Param(ws.PathParameter("namespace", "the name of the namespace")).
Doc("Delete the specified namespace from the workspace"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(tenant.ListDevopsProjects). To(handler.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
Required(false). Required(false).
...@@ -120,7 +115,7 @@ func addWebService(c *restful.Container) error { ...@@ -120,7 +115,7 @@ func addWebService(c *restful.Container) error {
Doc("List devops projects for the current user"). Doc("List devops projects for the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops").
To(tenant.ListDevopsProjectsByUsername). To(handler.ListDevopsProjects).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "workspace member's username")). Param(ws.PathParameter("member", "workspace member's username")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
...@@ -130,32 +125,32 @@ func addWebService(c *restful.Container) error { ...@@ -130,32 +125,32 @@ func addWebService(c *restful.Container) error {
Param(ws.QueryParameter(params.ConditionsParam, "query conditions"). Param(ws.QueryParameter(params.ConditionsParam, "query conditions").
Required(false). Required(false).
DataFormat("key=%s,key~%s")). 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"). Doc("List the devops projects for the workspace member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/devopscount"). ws.Route(ws.GET("/devopscount").
To(tenant.GetDevOpsProjectsCount). To(handler.GetDevOpsProjectsCount).
Returns(http.StatusOK, ok, struct { Returns(http.StatusOK, api.StatusOK, struct {
Count uint32 `json:"count"` Count uint32 `json:"count"`
}{}). }{}).
Doc("Get the devops projects count for the member"). Doc("Get the devops projects count for the member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.POST("/workspaces/{workspace}/devops"). ws.Route(ws.POST("/workspaces/{workspace}/devops").
To(tenant.CreateDevopsProject). To(handler.CreateDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create a devops project in the specified workspace"). Doc("Create a devops project in the specified workspace").
Reads(devopsv1alpha2.DevOpsProject{}). Reads(devopsv1alpha2.DevOpsProject{}).
Returns(http.StatusOK, RespOK, devopsv1alpha2.DevOpsProject{}). Returns(http.StatusOK, api.StatusOK, devopsv1alpha2.DevOpsProject{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{devops}"). ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{devops}").
To(tenant.DeleteDevopsProject). To(handler.DeleteDevopsProject).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("devops", "devops project ID")). Param(ws.PathParameter("devops", "devops project ID")).
Doc("Delete the specified devops project from the workspace"). 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})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/logs"). ws.Route(ws.GET("/logs").
To(tenant.LogQuery). To(handler.LogQuery).
Doc("Query cluster-level logs in a multi-tenants environment"). 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("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)). 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 { ...@@ -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)). 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}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}).
Writes(v1alpha2.Response{}). Writes(v1alpha2.Response{}).
Returns(http.StatusOK, RespOK, v1alpha2.Response{})). Returns(http.StatusOK, api.StatusOK, v1alpha2.Response{})).
Consumes(restful.MIME_JSON, restful.MIME_XML). Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON, "text/plain") Produces(restful.MIME_JSON, "text/plain")
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -52,6 +52,10 @@ type ResourceGetter struct { ...@@ -52,6 +52,10 @@ type ResourceGetter struct {
resourcesGetters map[string]v1alpha2.Interface resourcesGetters map[string]v1alpha2.Interface
} }
func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) {
r.resourcesGetters[resource] = getter
}
func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
resourceGetters := make(map[string]v1alpha2.Interface) resourceGetters := make(map[string]v1alpha2.Interface)
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册