add ks-iam and ks-apigateway

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2019-03-08 11:09:05 +08:00
parent f579e97f6b
commit b59c244ca2
715 changed files with 108638 additions and 23446 deletions

View File

@@ -0,0 +1,172 @@
/*
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 authenticate
import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/dgrijalva/jwt-go"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
type Auth struct {
Rule Rule
Next httpserver.Handler
}
type Rule struct {
Secret []byte
Path string
ExceptedPath []string
}
type User struct {
Username string `json:"username"`
UID string `json:"uid"`
Groups *[]string `json:"groups,omitempty"`
Extra *map[string]interface{} `json:"extra,omitempty"`
}
func (h Auth) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) {
for _, path := range h.Rule.ExceptedPath {
if httpserver.Path(req.URL.Path).Matches(path) {
return h.Next.ServeHTTP(resp, req)
}
}
if httpserver.Path(req.URL.Path).Matches(h.Rule.Path) {
uToken, err := h.ExtractToken(req)
if err != nil {
return h.HandleUnauthorized(resp, err), nil
}
token, err := h.Validate(uToken)
if err != nil {
return h.HandleUnauthorized(resp, err), nil
}
req, err = h.InjectContext(req, token)
if err != nil {
return h.HandleUnauthorized(resp, err), nil
}
}
return h.Next.ServeHTTP(resp, req)
}
func (h Auth) InjectContext(req *http.Request, token *jwt.Token) (*http.Request, error) {
payLoad, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, errors.New("invalid payload")
}
for header := range req.Header {
if strings.HasPrefix(header, "X-Token-") {
req.Header.Del(header)
}
}
username, ok := payLoad["username"].(string)
if ok && username != "" {
req.Header.Set("X-Token-Username", username)
}
uid := payLoad["uid"]
if uid != nil {
switch uid.(type) {
case int:
req.Header.Set("X-Token-UID", strconv.Itoa(uid.(int)))
break
case string:
req.Header.Set("X-Token-UID", uid.(string))
break
}
}
groups, ok := payLoad["groups"].([]string)
if ok && len(groups) > 0 {
req.Header.Set("X-Token-Groups", strings.Join(groups, ","))
}
return req, nil
}
func (h Auth) Validate(uToken string) (*jwt.Token, error) {
if len(uToken) == 0 {
return nil, fmt.Errorf("token length is zero")
}
token, err := jwt.Parse(uToken, h.ProvideKey)
if err != nil {
return nil, err
}
return token, nil
}
func (h Auth) HandleUnauthorized(w http.ResponseWriter, err error) int {
message := fmt.Sprintf("Unauthorized,%v", err)
w.Header().Add("WWW-Authenticate", message)
return http.StatusUnauthorized
}
func (h Auth) ExtractToken(r *http.Request) (string, error) {
jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
return jwtHeader[1], nil
}
jwtCookie, err := r.Cookie("token")
if err == nil {
return jwtCookie.Value, nil
}
jwtQuery := r.URL.Query().Get("token")
if jwtQuery != "" {
return jwtQuery, nil
}
return "", fmt.Errorf("no token found")
}
func (h Auth) ProvideKey(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
return h.Rule.Secret, nil
} else {
return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"])
}
}

View File

@@ -0,0 +1,110 @@
/*
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 authenticate
import (
"fmt"
"strings"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
func init() {
caddy.RegisterPlugin("authenticate", caddy.Plugin{
ServerType: "http",
Action: Setup,
})
}
func Setup(c *caddy.Controller) error {
rule, err := parse(c)
if err != nil {
return err
}
c.OnStartup(func() error {
fmt.Println("Authenticate middleware is initiated")
return nil
})
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return &Auth{Next: next, Rule: rule}
})
return nil
}
func parse(c *caddy.Controller) (Rule, error) {
rule := Rule{ExceptedPath: make([]string, 0)}
if c.Next() {
args := c.RemainingArgs()
switch len(args) {
case 0:
for c.NextBlock() {
switch c.Val() {
case "path":
if !c.NextArg() {
return rule, c.ArgErr()
}
rule.Path = c.Val()
if c.NextArg() {
return rule, c.ArgErr()
}
case "secret":
if !c.NextArg() {
return rule, c.ArgErr()
}
rule.Secret = []byte(c.Val())
if c.NextArg() {
return rule, c.ArgErr()
}
case "except":
if !c.NextArg() {
return rule, c.ArgErr()
}
rule.ExceptedPath = strings.Split(c.Val(), ",")
for i := 0; i < len(rule.ExceptedPath); i++ {
rule.ExceptedPath[i] = strings.TrimSpace(rule.ExceptedPath[i])
}
if c.NextArg() {
return rule, c.ArgErr()
}
}
}
default:
return rule, c.ArgErr()
}
}
if c.Next() {
return rule, c.ArgErr()
}
return rule, nil
}

View File

@@ -0,0 +1,300 @@
/*
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 authentication
import (
"errors"
"fmt"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/request"
"net/http"
"strings"
"github.com/mholt/caddy/caddyhttp/httpserver"
"k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/informers"
sliceutils "kubesphere.io/kubesphere/pkg/utils"
)
type Authentication struct {
Rule Rule
Next httpserver.Handler
}
type Rule struct {
Path string
ExceptedPath []string
}
func (c Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
if httpserver.Path(r.URL.Path).Matches(c.Rule.Path) {
for _, path := range c.Rule.ExceptedPath {
if httpserver.Path(r.URL.Path).Matches(path) {
return c.Next.ServeHTTP(w, r)
}
}
attrs, err := getAuthorizerAttributes(r.Context())
if err != nil {
return http.StatusInternalServerError, err
}
permitted, err := permissionValidate(attrs)
if err != nil {
return http.StatusInternalServerError, err
}
if !permitted {
err = k8serr.NewForbidden(schema.GroupResource{Group: attrs.GetAPIGroup(), Resource: attrs.GetResource()}, attrs.GetName(), fmt.Errorf("permission undefined"))
return handleForbidden(w, err), nil
}
}
return c.Next.ServeHTTP(w, r)
}
func handleForbidden(w http.ResponseWriter, err error) int {
message := fmt.Sprintf("Forbidden,%s", err.Error())
w.Header().Add("WWW-Authenticate", message)
return http.StatusForbidden
}
func permissionValidate(attrs authorizer.Attributes) (bool, error) {
permitted, err := clusterRoleValidate(attrs)
if err != nil {
return false, err
}
if permitted {
return true, nil
}
if attrs.GetNamespace() != "" {
permitted, err = roleValidate(attrs)
if err != nil {
return false, err
}
if permitted {
return true, nil
}
}
return false, nil
}
func roleValidate(attrs authorizer.Attributes) (bool, error) {
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
roleBindings, err := roleBindingLister.RoleBindings(attrs.GetNamespace()).List(labels.Everything())
if err != nil {
return false, err
}
fullSource := attrs.GetResource()
if attrs.GetSubresource() != "" {
fullSource = fullSource + "/" + attrs.GetSubresource()
}
for _, roleBinding := range roleBindings {
for _, subj := range roleBinding.Subjects {
if (subj.Kind == v1.UserKind && subj.Name == attrs.GetUser().GetName()) ||
(subj.Kind == v1.GroupKind && slice.ContainsString(attrs.GetUser().GetGroups(), subj.Name, nil)) {
role, err := roleLister.Roles(attrs.GetNamespace()).Get(roleBinding.RoleRef.Name)
if err != nil {
return false, err
}
for _, rule := range role.Rules {
if ruleMatchesRequest(rule, attrs.GetAPIGroup(), "", attrs.GetResource(), attrs.GetSubresource(), attrs.GetName(), attrs.GetVerb()) {
return true, nil
}
}
}
}
}
return false, nil
}
func clusterRoleValidate(attrs authorizer.Attributes) (bool, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
if err != nil {
return false, err
}
for _, clusterRoleBinding := range clusterRoleBindings {
for _, subject := range clusterRoleBinding.Subjects {
if (subject.Kind == v1.UserKind && subject.Name == attrs.GetUser().GetName()) ||
(subject.Kind == v1.GroupKind && sliceutils.HasString(attrs.GetUser().GetGroups(), subject.Name)) {
clusterRole, err := clusterRoleLister.Get(clusterRoleBinding.RoleRef.Name)
if err != nil {
return false, err
}
for _, rule := range clusterRole.Rules {
if attrs.IsResourceRequest() {
if ruleMatchesRequest(rule, attrs.GetAPIGroup(), "", attrs.GetResource(), attrs.GetSubresource(), attrs.GetName(), attrs.GetVerb()) {
return true, nil
}
} else {
if ruleMatchesRequest(rule, "", attrs.GetPath(), "", "", "", attrs.GetVerb()) {
return true, nil
}
}
}
}
}
}
return false, nil
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !sliceutils.HasString(rule.APIGroups, apiGroup) && !sliceutils.HasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !sliceutils.HasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !sliceutils.HasString(rule.Verbs, verb) && !sliceutils.HasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
if spec == "*" {
return true
}
if spec == path {
return true
}
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func getAuthorizerAttributes(ctx request.Context) (authorizer.Attributes, error) {
attribs := authorizer.AttributesRecord{}
user, ok := request.UserFrom(ctx)
if ok {
attribs.User = user
}
requestInfo, found := request.RequestInfoFrom(ctx)
if !found {
return nil, errors.New("no RequestInfo found in the context")
}
// Start with common attributes that apply to resource and non-resource requests
attribs.ResourceRequest = requestInfo.IsResourceRequest
attribs.Path = requestInfo.Path
attribs.Verb = requestInfo.Verb
attribs.APIGroup = requestInfo.APIGroup
attribs.APIVersion = requestInfo.APIVersion
attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace
attribs.Name = requestInfo.Name
return &attribs, nil
}

View File

@@ -0,0 +1,119 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package authentication
import (
"fmt"
"strings"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/signals"
)
func init() {
caddy.RegisterPlugin("authentication", caddy.Plugin{
ServerType: "http",
Action: Setup,
})
}
// Setup is called by Caddy to parse the config block
func Setup(c *caddy.Controller) error {
rule, err := parse(c)
if err != nil {
return err
}
if err != nil {
return err
}
c.OnStartup(func() error {
stopChan := signals.SetupSignalHandler()
informers.SharedInformerFactory().Start(stopChan)
informers.SharedInformerFactory().WaitForCacheSync(stopChan)
fmt.Println("Authentication middleware is initiated")
return nil
})
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return &Authentication{Next: next, Rule: rule}
})
return nil
}
func parse(c *caddy.Controller) (Rule, error) {
rule := Rule{ExceptedPath: make([]string, 0)}
if c.Next() {
args := c.RemainingArgs()
switch len(args) {
case 0:
for c.NextBlock() {
switch c.Val() {
case "path":
if !c.NextArg() {
return rule, c.ArgErr()
}
rule.Path = c.Val()
if c.NextArg() {
return rule, c.ArgErr()
}
break
case "except":
if !c.NextArg() {
return rule, c.ArgErr()
}
rule.ExceptedPath = strings.Split(c.Val(), ",")
for i := 0; i < len(rule.ExceptedPath); i++ {
rule.ExceptedPath[i] = strings.TrimSpace(rule.ExceptedPath[i])
}
if c.NextArg() {
return rule, c.ArgErr()
}
break
}
}
case 1:
rule.Path = args[0]
if c.NextBlock() {
return rule, c.ArgErr()
}
default:
return rule, c.ArgErr()
}
}
if c.Next() {
return rule, c.ArgErr()
}
return rule, nil
}

View File

@@ -0,0 +1,100 @@
/*
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 authenticate
import (
"fmt"
"net/http"
"github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
func init() {
caddy.RegisterPlugin("swagger", caddy.Plugin{
ServerType: "http",
Action: Setup,
})
}
func Setup(c *caddy.Controller) error {
handler, err := parse(c)
if err != nil {
return err
}
c.OnStartup(func() error {
fmt.Println("Swagger middleware is initiated")
return nil
})
httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
return &Swagger{Next: next, Handler: handler}
})
return nil
}
func parse(c *caddy.Controller) (Handler, error) {
handler := Handler{URL: "/swagger-ui", FilePath: "/var/static/swagger-ui"}
if c.Next() {
args := c.RemainingArgs()
switch len(args) {
case 0:
for c.NextBlock() {
switch c.Val() {
case "url":
if !c.NextArg() {
return handler, c.ArgErr()
}
handler.URL = c.Val()
if c.NextArg() {
return handler, c.ArgErr()
}
case "filePath":
if !c.NextArg() {
return handler, c.ArgErr()
}
handler.FilePath = c.Val()
if c.NextArg() {
return handler, c.ArgErr()
}
default:
return handler, c.ArgErr()
}
}
default:
return handler, c.ArgErr()
}
}
if c.Next() {
return handler, c.ArgErr()
}
handler.Handler = http.StripPrefix(handler.URL, http.FileServer(http.Dir(handler.FilePath)))
return handler, nil
}

View File

@@ -0,0 +1,45 @@
/*
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 authenticate
import (
"net/http"
"github.com/mholt/caddy/caddyhttp/httpserver"
)
type Swagger struct {
Handler Handler
Next httpserver.Handler
}
type Handler struct {
URL string
FilePath string
Handler http.Handler
}
func (h Swagger) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) {
if httpserver.Path(req.URL.Path).Matches(h.Handler.URL) {
h.Handler.Handler.ServeHTTP(resp, req)
return http.StatusOK, nil
}
return h.Next.ServeHTTP(resp, req)
}

View File

@@ -15,17 +15,19 @@
limitations under the License.
*/
package monitoring
package install
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/monitoring/v1alpha2"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const apiGroup = "monitoring.kubesphere.io"
func AddToContainer(container *restful.Container) error {
container.Add(v1alpha2.WebService(apiGroup))
return nil
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(iamv1alpha2.AddToContainer(container))
}

View File

@@ -0,0 +1,214 @@
/*
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 v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/iam"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
)
const GroupName = "iam.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
tags := []string{"IAM"}
ws := runtime.NewWebService(GroupVersion)
ws.Route(ws.POST("/authenticate").
To(iam.TokenReviewHandler).
Doc("Token review").
Reads(iam.TokenReview{}).
Writes(iam.TokenReview{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/login").
To(iam.LoginHandler).
Doc("User login").
Reads(iam.LoginRequest{}).
Writes(models.Token{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{name}").
To(iam.UserDetail).
Doc("User detail").
Writes(models.User{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/users").
To(iam.CreateUser).
Reads(models.User{}).
Writes(errors.Error{}).
Doc("Create user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/users/{name}").
To(iam.DeleteUser).
Doc("Delete user").
Writes(errors.Error{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.PUT("/users/{name}").
To(iam.UpdateUser).
Reads(models.User{}).
Writes(errors.Error{}).
Doc("Update user").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{name}/log").
To(iam.UserLoginLog).
Doc("User login log").
Writes([]map[string]string{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users").
To(iam.UserList).
Doc("User list").
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups").
To(iam.RootGroupList).
Writes([]models.Group{}).
Doc("User group list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups/{path}").
To(iam.GroupDetail).
Doc("User group detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/groups/{path}/users").
To(iam.GroupUsers).
Doc("Group user list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/groups").
To(iam.CreateGroup).
Reads(models.Group{}).
Doc("Create user group").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/groups/{path}").
To(iam.DeleteGroup).
Doc("Delete user group").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.PUT("/groups/{path}").
To(iam.UpdateGroup).
Doc("Update user group").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/users/{username}/roles").
To(iam.UserRoles).
Doc("Get user role list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users").
To(iam.RoleUsers).
Doc("Get user list by role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules").
To(iam.RoleRules).
Doc("Get role detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/users").
To(iam.NamespaceUsers).
Doc("Get user list by namespace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/clusterroles/{clusterrole}/users").
To(iam.ClusterRoleUsers).
Doc("Get user list by cluster role").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/clusterroles/{clusterrole}/rules").
To(iam.ClusterRoleRules).
Doc("Get cluster role detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/rulesmapping/clusterroles").
To(iam.ClusterRulesMappingHandler).
Doc("Get cluster role policy rules mapping").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/rulesmapping/roles").
To(iam.RulesMappingHandler).
Doc("Get role policy rules mapping").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/rules").
To(iam.WorkspaceRulesHandler).
Doc("Get workspace level policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{workspace}/members").
To(iam.WorkspaceMemberList).
Doc("Get workspace member list").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/rules").
To(iam.NamespacesRulesHandler).
Doc("Get namespace level policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/devops/{devops}/rules").
To(iam.DevopsRulesHandler).
Doc("Get devops project level policy rules").
Metadata(restfulspec.KeyOpenAPITags, tags))
tags = []string{"Workspace"}
ws.Route(ws.GET("/workspaces").
To(iam.UserWorkspaceListHandler).
Doc("Get workspace list").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes([]models.Workspace{}))
ws.Route(ws.POST("/workspaces").
To(iam.WorkspaceCreateHandler).
Doc("Create workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.DELETE("/workspaces/{name}").
To(iam.DeleteWorkspaceHandler).
Doc("Delete workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(errors.Error{}))
ws.Route(ws.GET("/workspaces/{name}").
To(iam.WorkspaceDetailHandler).
Doc("Get workspace detail").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.PUT("/workspaces/{name}").
To(iam.WorkspaceEditHandler).
Doc("Update workspace").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.Workspace{}))
ws.Route(ws.GET("/workspaces/{name}/members/{member}").
To(iam.WorkspaceMemberDetail).
Doc("Get workspace member detail").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces/{name}/roles").
To(iam.WorkspaceRoles).
Doc("Get workspace roles").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.POST("/workspaces/{name}/members").
To(iam.WorkspaceMemberInvite).
Doc("Add user to workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.DELETE("/workspaces/{name}/members").
To(iam.WorkspaceMemberRemove).
Doc("Delete user from workspace").
Metadata(restfulspec.KeyOpenAPITags, tags))
c.Add(ws)
return nil
}

View File

@@ -1,18 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics

View File

@@ -19,6 +19,7 @@ package v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/metrics"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
@@ -36,8 +37,20 @@ var (
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.GET("/storageclasses/{storageclass}").To(metrics.GetScMetrics))
webservice.Route(webservice.GET("/metrics/storageclass").To(metrics.GetScMetricsList))
tags := []string{"metrics"}
webservice.Route(webservice.GET("/storageclasses/{storageclass}").
To(metrics.GetScMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Param(webservice.PathParameter("storageclass", "storageclass's name")).
Writes(metrics.ScMetricsItem{}))
webservice.Route(webservice.GET("/storageclasses").
To(metrics.GetScMetricsList).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Writes([]metrics.ScMetricsItem{}))
c.Add(webservice)

View File

@@ -15,22 +15,19 @@
limitations under the License.
*/
package v1alpha2
package install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/apis/monitoring/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const apiVersion = "v1alpha2"
var addToWebServiceFuncs []func(ws *restful.WebService)
func WebService(apiGroup string) *restful.WebService {
ws := new(restful.WebService)
ws.Path("/apis/" + apiGroup + "/" + apiVersion).
Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
for _, f := range addToWebServiceFuncs {
f(ws)
}
return ws
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(monitoringv1alpha2.AddToContainer(container))
}

View File

@@ -0,0 +1,207 @@
/*
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 v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/monitoring"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const GroupName = "monitoring.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
ws := runtime.NewWebService(GroupVersion)
tags := []string{"Monitoring"}
ws.Route(ws.GET("/clusters").To(monitoring.MonitorCluster).
Doc("monitor cluster level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("cluster_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/nodes").To(monitoring.MonitorNode).
Doc("monitor nodes level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("node_cpu_utilisation")).
Param(ws.QueryParameter("nodes_filter", "node re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/nodes/{node}").To(monitoring.MonitorNode).
Doc("monitor specific node level metrics").
Param(ws.PathParameter("node", "specific node").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("node_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces").To(monitoring.MonitorNamespace).
Doc("monitor namespaces level metrics").
Param(ws.QueryParameter("namespaces_filter", "namespaces re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("namespace_memory_utilisation")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}").To(monitoring.MonitorNamespace).
Doc("monitor specific namespace level metrics").
Param(ws.PathParameter("namespace", "specific namespace").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("namespace_memory_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/pods").To(monitoring.MonitorPod).
Doc("monitor pods level metrics").
Param(ws.PathParameter("namespace", "specific namespace").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}").To(monitoring.MonitorPod).
Doc("monitor specific pod level metrics").
Param(ws.PathParameter("namespace", "specific namespace").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod", "specific pod").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/nodes/{node}/pods").To(monitoring.MonitorPod).
Doc("monitor pods level metrics by nodeid").
Param(ws.PathParameter("node", "specific node").Required(true).DefaultValue("i-k89a62il")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/nodes/{node}/pods/{pod}").To(monitoring.MonitorPod).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node", "specific node").Required(true).DefaultValue("i-k89a62il")).
Param(ws.PathParameter("pod", "specific pod").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/nodes/{node}/pods/{pod}/containers").To(monitoring.MonitorContainer).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node", "specific node").Required(true)).
Param(ws.PathParameter("pod", "specific pod").Required(true)).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers").To(monitoring.MonitorContainer).
Doc("monitor containers level metrics").
Param(ws.PathParameter("namespace", "specific namespace").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod", "specific pod").Required(true).DefaultValue("")).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}").To(monitoring.MonitorContainer).
Doc("monitor specific container level metrics").
Param(ws.PathParameter("namespace", "specific namespace").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod", "specific pod").Required(true).DefaultValue("")).
Param(ws.PathParameter("container", "specific container").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/workloads/{workload_kind}").To(monitoring.MonitorWorkload).
Doc("monitor specific workload level metrics").
Param(ws.PathParameter("namespace", "namespace").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").Required(false)).
Param(ws.PathParameter("workload_kind", "workload kind").Required(false).DefaultValue("daemonset")).
Param(ws.QueryParameter("workload_name", "workload name").Required(true).DefaultValue("")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "max metric items in a page").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/workloads").To(monitoring.MonitorWorkload).
Doc("monitor all workload level metrics").
Param(ws.PathParameter("namespace", "namespace").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").Required(false)).
Param(ws.QueryParameter("workloads_filter", "pod re2 expression filter").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
// list all namespace in this workspace by selected metrics
ws.Route(ws.GET("/workspaces/{workspace}").To(monitoring.MonitorOneWorkspace).
Doc("monitor workspaces level metrics").
Param(ws.PathParameter("workspace", "workspace name").Required(true)).
Param(ws.QueryParameter("namespaces_filter", "namespaces filter").Required(false).DefaultValue("k.*")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("namespace_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/workspaces").To(monitoring.MonitorAllWorkspaces).
Doc("monitor workspaces level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("workspace_memory_utilisation")).
Param(ws.QueryParameter("workspaces_filter", "workspaces re2 expression filter").Required(false).DefaultValue(".*")).
Param(ws.QueryParameter("sort_metric", "sort metric").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").Required(false)).
Param(ws.QueryParameter("page", "page number").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/components").To(monitoring.MonitorComponentStatus).
Doc("monitor k8s components status").
Metadata(restfulspec.KeyOpenAPITags, tags))
c.Add(ws)
return nil
}

View File

@@ -1,18 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package operations

View File

@@ -23,6 +23,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/operations"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/errors"
)
const GroupName = "operations.kubesphere.io"
@@ -36,20 +37,24 @@ var (
func addWebService(c *restful.Container) error {
tags := []string{"Operations"}
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.POST("/nodes/{node}/drainage").To(operations.DrainNode))
webservice.Route(webservice.POST("/nodes/{node}/drainage").
To(operations.DrainNode).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Param(webservice.PathParameter("node", "node name")).
Writes(errors.Error{}))
webservice.Route(webservice.POST("/namespaces/{namespace}/jobs/{job}").
To(operations.RerunJob).
Metadata(restfulspec.KeyOpenAPITags, []string{"jobs"}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Handle job operation").
Param(webservice.PathParameter("job", "job name").
DataType("string")).
Param(webservice.PathParameter("namespace", "job's namespace").
DataType("string")).
Param(webservice.QueryParameter("a", "action").
DataType("string")).
Param(webservice.PathParameter("job", "job name")).
Param(webservice.PathParameter("namespace", "job's namespace")).
Param(webservice.QueryParameter("a", "action")).
Writes(""))
c.Add(webservice)

View File

@@ -1,18 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package metrics

View File

@@ -20,9 +20,20 @@ package v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/components"
"kubesphere.io/kubesphere/pkg/apiserver/quotas"
"kubesphere.io/kubesphere/pkg/apiserver/registries"
"kubesphere.io/kubesphere/pkg/apiserver/resources"
"kubesphere.io/kubesphere/pkg/apiserver/revisions"
"kubesphere.io/kubesphere/pkg/apiserver/routers"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/apiserver/workloadstatuses"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/params"
)
const GroupName = "resources.kubesphere.io"
@@ -38,18 +49,182 @@ func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").To(resources.NamespaceResourceHandler))
tags := []string{"Namespace resources"}
webservice.Route(webservice.GET("/{resources}").To(resources.ClusterResourceHandler))
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}").
To(resources.NamespaceResourceHandler).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Namespace level resource query").
Param(webservice.PathParameter("namespace", "which namespace")).
Param(webservice.PathParameter("resources", "namespace level resource type")).
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=%s,key~%s")).
Param(webservice.QueryParameter(params.PagingParam, "page").
Required(false).
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")).
Writes(models.PageableResponse{}))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").To(resources.GetPvcListBySc))
webservice.Route(webservice.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}/pods").To(resources.GetPodListByPvc))
tags = []string{"Cluster resources"}
tags := []string{"users"}
webservice.Route(webservice.GET("/users/{username}/kubectl").Doc("get user's kubectl pod").Param(webservice.PathParameter("username",
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(resources.GetKubectl))
webservice.Route(webservice.GET("/users/{username}/kubeconfig").Doc("get users' kubeconfig").Param(webservice.PathParameter("username",
"username").DataType("string")).Metadata(restfulspec.KeyOpenAPITags, tags).To(resources.GetKubeconfig))
webservice.Route(webservice.GET("/{resources}").
To(resources.ClusterResourceHandler).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Cluster level resource query").
Param(webservice.PathParameter("resources", "cluster level resource type"))).
Param(webservice.QueryParameter(params.ConditionsParam, "query conditions").
Required(false).
DataFormat("key=value,key~value").
DefaultValue("")).
Param(webservice.QueryParameter(params.PagingParam, "page").
Required(false).
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1"))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
To(resources.GetPvcListBySc).
Doc("get user's kubectl pod").
Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags))
webservice.Route(webservice.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}/pods").
To(resources.GetPodListByPvc))
tags = []string{"User resources"}
webservice.Route(webservice.GET("/users/{username}/kubectl").
To(resources.GetKubectl).
Doc("get user's kubectl pod").
Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(models.PodInfo{}))
webservice.Route(webservice.GET("/users/{username}/kubeconfig").
To(resources.GetKubeconfig).
Doc("get users' kubeconfig").
Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags))
tags = []string{"Components"}
webservice.Route(webservice.GET("/components").
To(components.GetComponents).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Writes(map[string]models.Component{}))
webservice.Route(webservice.GET("/components/{component}").
To(components.GetComponentStatus).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Param(webservice.PathParameter("component", "component name")).
Writes(models.Component{}))
webservice.Route(webservice.GET("/health").
To(components.GetSystemHealthStatus).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Writes(map[string]int{}))
tags = []string{"Quotas"}
webservice.Route(webservice.GET("/quotas").
To(quotas.GetClusterQuotas).
Doc("get whole cluster's resource usage").
Writes(models.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
webservice.Route(webservice.GET("/namespaces/{namespace}/quotas").
Doc("get specified namespace's resource quota and usage").
Param(webservice.PathParameter("namespace", "namespace's name")).
Writes(models.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(quotas.GetNamespaceQuotas))
tags = []string{"Registries"}
webservice.Route(webservice.POST("registries/verify").
To(registries.RegistryVerify).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("docker registry verify").
Writes(errors.Error{}))
tags = []string{"Revision"}
webservice.Route(webservice.GET("/namespaces/{namespace}/daemonsets/{daemonset}/revisions/{revision}").
To(revisions.GetDaemonSetRevision).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Handle daemonset operation").
Param(webservice.PathParameter("daemonset", "daemonset's name")).
Param(webservice.PathParameter("namespace", "daemonset's namespace")).
Param(webservice.PathParameter("revision", "daemonset's revision")).
Writes(appsv1.DaemonSet{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/deployments/{deployment}/revisions/{revision}").
To(revisions.GetDeployRevision).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Handle deployment operation").
Param(webservice.PathParameter("deployment", "deployment's name")).
Param(webservice.PathParameter("namespace", "deployment's namespace")).
Param(webservice.PathParameter("revision", "deployment's revision")).
Writes(appsv1.ReplicaSet{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}").
To(revisions.GetStatefulSetRevision).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Handle statefulset operation").
Param(webservice.PathParameter("statefulset", "statefulset's name")).
Param(webservice.PathParameter("namespace", "statefulset's namespace")).
Param(webservice.PathParameter("revision", "statefulset's revision")).
Writes(appsv1.StatefulSet{}))
tags = []string{"Router"}
webservice.Route(webservice.GET("/routers").
To(routers.GetAllRouters).
Doc("Get all routers").
Metadata(restfulspec.KeyOpenAPITags, tags).
Writes(corev1.Service{}))
webservice.Route(webservice.GET("/users/{username}/routers").
To(routers.GetAllRoutersOfUser).
Doc("Get routers for user").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("username", "")).
Writes(corev1.Service{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/router").
To(routers.GetRouter).
Doc("Get router of a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))
webservice.Route(webservice.DELETE("/namespaces/{namespace}/router").
To(routers.DeleteRouter).
Doc("Get router of a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))
webservice.Route(webservice.POST("/namespaces/{namespace}/router").
To(routers.CreateRouter).
Doc("Create a router for a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))
webservice.Route(webservice.PUT("/namespaces/{namespace}/router").
To(routers.UpdateRouter).
Doc("Update a router for a specified project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "name of the project")))
tags = []string{"WorkloadStatus"}
webservice.Route(webservice.GET("/workloadstatuses").
Doc("get abnormal workloads' count of whole cluster").
Metadata(restfulspec.KeyOpenAPITags, tags).
To(workloadstatuses.GetClusterResourceStatus))
webservice.Route(webservice.GET("/namespaces/{namespace}/workloadstatuses").
Doc("get abnormal workloads' count of specified namespace").
Param(webservice.PathParameter("namespace", "the name of namespace")).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(workloadstatuses.GetNamespacesResourceStatus))
c.Add(webservice)

View File

@@ -22,18 +22,14 @@ import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/components"
"net/http"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/components").To(getComponents))
ws.Route(ws.GET("/components/{component}").To(getComponentStatus))
ws.Route(ws.GET("/health").To(getSystemHealthStatus))
}
func GetSystemHealthStatus(request *restful.Request, response *restful.Response) {
result, err := components.GetSystemHealthStatus()
func getSystemHealthStatus(request *restful.Request, response *restful.Response) {
result, err := components.GetAllComponentsStatus()
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -41,12 +37,13 @@ func getSystemHealthStatus(request *restful.Request, response *restful.Response)
}
// get a specific component status
func getComponentStatus(request *restful.Request, response *restful.Response) {
func GetComponentStatus(request *restful.Request, response *restful.Response) {
component := request.PathParameter("component")
result, err := components.GetComponentStatus(component)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -54,11 +51,12 @@ func getComponentStatus(request *restful.Request, response *restful.Response) {
}
// get all componentsHandler
func getComponents(request *restful.Request, response *restful.Response) {
func GetComponents(request *restful.Request, response *restful.Response) {
result, err := components.GetAllComponentsStatus()
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -1,52 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package hpa
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/api/autoscaling/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/hpa"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/namespaces/{namespace}/horizontalpodautoscalers/{horizontalpodautoscaler}").
To(getHpa).
Metadata(restfulspec.KeyOpenAPITags, []string{"hpa"}).
Doc("get horizontalpodautoscalers").
Param(ws.PathParameter("namespace", "horizontalpodautoscalers's namespace").
DataType("string")).
Param(ws.PathParameter("horizontalpodautoscaler", "horizontalpodautoscaler's name")).
Writes(v1.HorizontalPodAutoscaler{}))
}
func getHpa(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("horizontalpodautoscaler")
namespace := req.PathParameter("namespace")
result, err := hpa.GetHPA(namespace, name)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(result)
}

190
pkg/apiserver/iam/am.go Normal file
View File

@@ -0,0 +1,190 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"github.com/emicklei/go-restful"
"k8s.io/api/rbac/v1"
"net/http"
"sort"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
)
type roleList struct {
ClusterRoles []*v1.ClusterRole `json:"clusterRoles" protobuf:"bytes,2,rep,name=clusterRoles"`
Roles []*v1.Role `json:"roles" protobuf:"bytes,2,rep,name=roles"`
}
func RoleRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
roleName := req.PathParameter("role")
role, err := iam.GetRole(namespace, roleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
rules, err := iam.GetRoleSimpleRules([]*v1.Role{role}, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules[namespace])
}
func RoleUsers(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 NamespaceUsers(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.Slice(users, func(i, j int) bool {
return users[i].Username < users[j].Username
})
resp.WriteAsJson(users)
}
func UserRoles(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("username")
roles, err := iam.GetRoles(username, "")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roleList := roleList{}
roleList.Roles = roles
roleList.ClusterRoles = clusterRoles
resp.WriteAsJson(roleList)
}
func NamespaceRulesHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader)
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
roles, err := iam.GetRoles(username, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
for _, clusterRole := range clusterRoles {
role := new(v1.Role)
role.Name = clusterRole.Name
role.Labels = clusterRole.Labels
role.Namespace = namespace
role.Annotations = clusterRole.Annotations
role.Kind = "Role"
role.Rules = clusterRole.Rules
roles = append(roles, role)
}
rules, err := iam.GetRoleSimpleRules(roles, namespace)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules[namespace])
}
func RulesMappingHandler(req *restful.Request, resp *restful.Response) {
rules := policy.RoleRuleMapping
resp.WriteAsJson(rules)
}
func ClusterRulesMappingHandler(req *restful.Request, resp *restful.Response) {
rules := policy.ClusterRoleRuleMapping
resp.WriteAsJson(rules)
}
func ClusterRoleRules(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
clusterRole, err := iam.GetClusterRole(clusterRoleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
rules, err := iam.GetClusterRoleSimpleRules([]*v1.ClusterRole{clusterRole})
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}
func ClusterRoleUsers(req *restful.Request, resp *restful.Response) {
clusterRoleName := req.PathParameter("clusterrole")
users, err := iam.ClusterRoleUsers(clusterRoleName)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(users)
}

142
pkg/apiserver/iam/auth.go Normal file
View File

@@ -0,0 +1,142 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/models"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/utils"
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
)
type Spec struct {
Token string `json:"token"`
}
type Status struct {
Authenticated bool `json:"authenticated"`
User map[string]interface{} `json:"user,omitempty"`
}
type TokenReview struct {
APIVersion string `json:"apiVersion"`
Kind string `json:"kind"`
Spec *Spec `json:"spec,omitempty"`
Status *Status `json:"status,omitempty"`
}
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
const (
APIVersion = "authentication.k8s.io/v1beta1"
KindTokenReview = "TokenReview"
)
func LoginHandler(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.Wrap(fmt.Errorf("incorrect username or password")))
return
}
ip := utils.RemoteIp(req.Request)
token, err := iam.Login(loginRequest.Username, loginRequest.Password, ip)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err))
return
}
resp.WriteAsJson(models.Token{Token: token})
}
// 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.Wrap(fmt.Errorf("token must not be null")))
return
}
uToken := tokenReview.Spec.Token
token, err := jwtutils.ValidateToken(uToken)
if err != nil {
failed := TokenReview{APIVersion: APIVersion,
Kind: KindTokenReview,
Status: &Status{
Authenticated: false,
},
}
resp.WriteAsJson(failed)
return
}
claims := token.Claims.(jwt.MapClaims)
username := claims["username"].(string)
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
success := TokenReview{APIVersion: APIVersion,
Kind: KindTokenReview,
Status: &Status{
Authenticated: true,
User: map[string]interface{}{"username": user.Username, "uid": user.Username, "groups": user.Groups},
},
}
resp.WriteAsJson(success)
return
}

253
pkg/apiserver/iam/groups.go Normal file
View File

@@ -0,0 +1,253 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
)
func CreateGroup(req *restful.Request, resp *restful.Response) {
//var json map[string]interface{}
var group models.Group
err := req.ReadEntity(&group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if !regexp.MustCompile("[a-z0-9]([-a-z0-9]*[a-z0-9])?").MatchString(group.Name) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, fmt.Errorf("incalid group name %s", group))
return
}
if group.Creator == "" {
resp.WriteHeaderAndEntity(http.StatusBadRequest, fmt.Errorf("creator should not be null"))
return
}
created, err := iam.CreateGroup(group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(created)
}
func DeleteGroup(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("path")
if path == "" {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("group path must not be null")))
return
}
err := iam.DeleteGroup(path)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func UpdateGroup(req *restful.Request, resp *restful.Response) {
groupPathInPath := req.PathParameter("path")
var group models.Group
req.ReadEntity(&group)
if groupPathInPath != group.Path {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the path of group (%s) does not match the path on the URL (%s)", group.Path, groupPathInPath)))
return
}
edited, err := iam.UpdateGroup(&group)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(edited)
}
func GroupDetail(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("path")
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
group, err := iam.GroupDetail(path, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(group)
}
func GroupUsers(req *restful.Request, resp *restful.Response) {
path := req.PathParameter("path")
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
group, err := iam.GroupDetail(path, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
users := make([]*models.User, 0)
modify := false
for i := 0; i < len(group.Members); i++ {
name := group.Members[i]
user, err := iam.UserDetail(name, conn)
if err != nil {
if ldap.IsErrorWithCode(err, 32) {
group.Members = append(group.Members[:i], group.Members[i+1:]...)
i--
modify = true
continue
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
}
clusterRoles, err := iam.GetClusterRoles(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
if group.Path == group.Name {
workspaceRole := iam.GetWorkspaceRole(clusterRoles, group.Name)
user.WorkspaceRole = workspaceRole
}
users = append(users, user)
}
if modify {
go iam.UpdateGroup(group)
}
resp.WriteAsJson(users)
}
func CountHandler(req *restful.Request, resp *restful.Response) {
count, err := iam.CountChild("")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]int{"total_count": count})
}
func RootGroupList(req *restful.Request, resp *restful.Response) {
array := req.QueryParameter("path")
if array == "" {
groups, err := iam.ChildList("")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(groups)
} else {
paths := strings.Split(array, ",")
groups := make([]*models.Group, 0)
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
for _, v := range paths {
path := strings.TrimSpace(v)
group, err := iam.GroupDetail(path, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
groups = append(groups, group)
}
resp.WriteAsJson(groups)
}
}

365
pkg/apiserver/iam/users.go Normal file
View File

@@ -0,0 +1,365 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
)
const (
emailRegex = "^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"
)
func CreateUser(req *restful.Request, resp *restful.Response) {
var user models.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if user.Username == "" {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid username")))
return
}
if !regexp.MustCompile(emailRegex).MatchString(user.Email) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid email")))
return
}
if len(user.Password) < 6 {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid password")))
return
}
err = iam.CreateUser(user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func DeleteUser(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
operator := req.HeaderParameter(constants.UserNameHeader)
if operator == username {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(fmt.Errorf("cannot delete yourself")))
return
}
err := iam.DeleteUser(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func UpdateUser(req *restful.Request, resp *restful.Response) {
usernameInPath := req.PathParameter("name")
username := req.HeaderParameter(constants.UserNameHeader)
var user models.User
err := req.ReadEntity(&user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if usernameInPath != user.Username {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the name of user (%s) does not match the name on the URL (%s)", user.Username, usernameInPath)))
return
}
if !regexp.MustCompile(emailRegex).MatchString(user.Email) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid email")))
return
}
if user.Password != "" && len(user.Password) < 6 {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid password")))
return
}
if username == user.Username && user.Password != "" {
_, err = iam.Login(username, user.CurrentPassword, "")
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("incorrect current password")))
return
}
}
err = iam.UpdateUser(user)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func UserLoginLog(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
logs, err := iam.LoginLog(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := make([]map[string]string, 0)
for _, v := range logs {
item := strings.Split(v, ",")
time := item[0]
var ip string
if len(item) > 1 {
ip = item[1]
}
result = append(result, map[string]string{"login_time": time, "login_ip": ip})
}
resp.WriteAsJson(result)
}
func CurrentUserDetail(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRules, err := iam.GetClusterRoleSimpleRules(clusterRoles)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
user.ClusterRules = clusterRules
resp.WriteAsJson(user)
}
func NamespacesListHandler(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
namespaces, err := iam.GetNamespaces(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(namespaces)
}
func UserDetail(req *restful.Request, resp *restful.Response) {
username := req.PathParameter("name")
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
user, err := iam.UserDetail(username, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
clusterRules, err := iam.GetClusterRoleSimpleRules(clusterRoles)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
workspaceRoles := iam.GetWorkspaceRoles(clusterRoles)
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
user.ClusterRules = clusterRules
user.WorkspaceRoles = workspaceRoles
resp.WriteAsJson(user)
}
func UserList(req *restful.Request, resp *restful.Response) {
limit, err := strconv.Atoi(req.QueryParameter("limit"))
if err != nil {
limit = 65535
}
offset, err := strconv.Atoi(req.QueryParameter("offset"))
if err != nil {
offset = 0
}
if check := req.QueryParameter("check"); check != "" {
exist, err := iam.UserCreateCheck(check)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
return
}
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
if query := req.QueryParameter("name"); query != "" {
names := strings.Split(query, ",")
users := make([]*models.User, 0)
for _, name := range names {
user, err := iam.UserDetail(name, conn)
if err != nil {
if ldap.IsErrorWithCode(err, 32) {
continue
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
}
users = append(users, user)
}
resp.WriteAsJson(users)
return
}
var total int
var users []models.User
if query := req.QueryParameter("search"); query != "" {
total, users, err = iam.Search(query, limit, offset)
} else if query := req.QueryParameter("keyword"); query != "" {
total, users, err = iam.Search(query, limit, offset)
} else {
total, users, err = iam.UserList(limit, offset)
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for i := 0; i < len(users); i++ {
clusterRoles, err := iam.GetClusterRoles(users[i].Username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for j := 0; j < len(clusterRoles); j++ {
if clusterRoles[j].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
users[i].ClusterRole = clusterRoles[j].Name
break
}
}
}
items := make([]interface{}, 0)
for _, u := range users {
items = append(items, u)
}
resp.WriteAsJson(models.PageableResponse{Items: items, TotalCount: total})
}

View File

@@ -0,0 +1,746 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"fmt"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
"github.com/emicklei/go-restful"
"github.com/go-ldap/ldap"
"k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
apierror "k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics"
"kubesphere.io/kubesphere/pkg/models/workspaces"
sliceutils "kubesphere.io/kubesphere/pkg/utils"
)
const UserNameHeader = "X-Token-Username"
func WorkspaceRoles(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roles, err := workspaces.Roles(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(roles)
}
func WorkspaceMemberQuery(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
keyword := req.QueryParameter("keyword")
users, err := workspaces.GetWorkspaceMembers(workspace, keyword)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(users)
}
func WorkspaceMemberDetail(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("member")
user, err := iam.GetUser(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespaces, err := workspaces.Namespaces(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.WorkspaceRole = user.WorkspaceRoles[workspace]
roles := make(map[string]string)
for _, namespace := range namespaces {
if role := user.Roles[namespace.Name]; role != "" {
roles[namespace.Name] = role
}
}
user.Roles = roles
user.Rules = nil
user.WorkspaceRules = nil
user.WorkspaceRoles = nil
user.ClusterRules = nil
resp.WriteAsJson(user)
}
func WorkspaceMemberInvite(req *restful.Request, resp *restful.Response) {
var users []models.UserInvite
workspace := req.PathParameter("name")
err := req.ReadEntity(&users)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.Invite(workspace, users)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceMemberRemove(req *restful.Request, resp *restful.Response) {
query := req.QueryParameter("name")
workspace := req.PathParameter("name")
names := strings.Split(query, ",")
err := workspaces.RemoveMembers(workspace, names)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func NamespaceCheckHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
exist, err := workspaces.NamespaceExistCheck(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
workspace := req.PathParameter("name")
err := workspaces.DeleteNamespace(workspace, namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("name")
force := req.QueryParameter("force")
username := req.HeaderParameter(UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
if err != nil && force != "true" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
var devops models.DevopsProject
err := req.ReadEntity(&devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(project)
}
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
namespace := &v1.Namespace{}
err := req.ReadEntity(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if namespace.Annotations == nil {
namespace.Annotations = make(map[string]string, 0)
}
namespace.Annotations["creator"] = username
namespace.Annotations["workspace"] = workspace
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
namespace.Labels["kubesphere.io/workspace"] = workspace
namespace, err = workspaces.CreateNamespace(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
resp.WriteAsJson(namespace)
}
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
total, devOpsProjects, err := workspaces.ListDevopsProjectsByUser(username, workspace, keyword, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range devOpsProjects {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
var workspace models.Workspace
username := req.HeaderParameter(UserNameHeader)
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
if workspace.Admin != "" {
workspace.Creator = workspace.Admin
} else {
workspace.Creator = username
}
created, err := workspaces.Create(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(created)
}
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
if name == "" || strings.Contains(name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = workspaces.Delete(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
var workspace models.Workspace
name := req.PathParameter("name")
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
if name != workspace.Name {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name)))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid workspace name")))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
edited, err := workspaces.Edit(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(edited)
}
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(workspace)
}
// List all workspaces for the current user
func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) {
keyword := req.QueryParameter("keyword")
username := req.HeaderParameter(constants.UserNameHeader)
ws, err := workspaces.ListWorkspaceByUser(username, keyword)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
sort.Slice(ws, func(i, j int) bool {
t1, err := ws[i].GetCreateTime()
if err != nil {
return false
}
t2, err := ws[j].GetCreateTime()
if err != nil {
return true
}
return t1.After(t2)
})
resp.WriteAsJson(ws)
}
func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) {
withMetrics, err := strconv.ParseBool(req.QueryParameter("metrics"))
if err != nil {
withMetrics = false
}
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
workspaceName := req.PathParameter("workspace")
total, namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username, keyword, orderBy, reverse, limit, offset)
if withMetrics {
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range namespaces {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func DevopsRulesHandler(req *restful.Request, resp *restful.Response) {
//workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
devopsName := req.PathParameter("devops")
var rules []models.SimpleRule
role, err := iam.GetDevopsRole(devopsName, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
switch role {
case "developer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "owner":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"edit", "view", "delete"}},
}
break
case "maintainer":
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}},
{Name: "devops", Actions: []string{"view"}},
}
break
case "reporter":
fallthrough
default:
rules = []models.SimpleRule{
{Name: "pipelines", Actions: []string{"view"}},
{Name: "roles", Actions: []string{"view"}},
{Name: "members", Actions: []string{"view"}},
{Name: "devops", Actions: []string{"view"}},
}
break
}
resp.WriteAsJson(rules)
}
func NamespacesRulesHandler(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
namespaceName := req.PathParameter("namespace")
namespace, err := iam.GetNamespace(namespaceName)
if err != nil {
if apierror.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("permission undefined")))
} else {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
}
return
}
if namespace.Labels == nil || namespace.Labels["kubesphere.io/workspace"] != workspaceName {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(fmt.Errorf("permission undefined")))
return
}
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
roles, err := iam.GetRoles(username, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
for _, clusterRole := range clusterRoles {
role := new(rbac.Role)
role.Name = clusterRole.Name
role.Labels = clusterRole.Labels
role.Namespace = namespaceName
role.Annotations = clusterRole.Annotations
role.Kind = "Role"
role.Rules = clusterRole.Rules
roles = append(roles, role)
}
rules, err := iam.GetRoleSimpleRules(roles, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
if rules[namespaceName] == nil {
resp.WriteAsJson(make([]models.SimpleRule, 0))
} else {
resp.WriteAsJson(rules[namespaceName])
}
}
func WorkspaceRulesHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
rules := iam.GetWorkspaceSimpleRules(clusterRoles, workspace)
if rules[workspace] != nil {
resp.WriteAsJson(rules[workspace])
} else if rules["*"] != nil {
resp.WriteAsJson(rules["*"])
} else {
resp.WriteAsJson(make([]models.SimpleRule, 0))
}
}
func WorkspaceMemberList(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
limit, err := strconv.Atoi(req.QueryParameter("limit"))
if err != nil {
limit = 500
}
offset, err := strconv.Atoi(req.QueryParameter("offset"))
if err != nil {
offset = 0
}
conn, err := iam.NewConnection()
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
defer conn.Close()
group, err := iam.GroupDetail(workspace, conn)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
keyword := ""
if query := req.QueryParameter("keyword"); query != "" {
keyword = query
}
users := make([]*models.User, 0)
total := len(group.Members)
members := sliceutils.RemoveString(group.Members, func(item string) bool {
return keyword != "" && !strings.Contains(item, keyword)
})
for i := 0; i < len(members); i++ {
username := members[i]
if i < offset {
continue
}
if len(users) == limit {
break
}
user, err := iam.UserDetail(username, conn)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
group.Members = sliceutils.RemoveString(group.Members, func(item string) bool {
return item == username
})
continue
} else {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
}
clusterRoles, err := iam.GetClusterRoles(username)
for i := 0; i < len(clusterRoles); i++ {
if clusterRoles[i].Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
user.ClusterRole = clusterRoles[i].Name
break
}
}
if group.Path == group.Name {
workspaceRole := iam.GetWorkspaceRole(clusterRoles, group.Name)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
user.WorkspaceRole = workspaceRole
}
users = append(users, user)
}
if total > len(group.Members) {
go iam.UpdateGroup(group)
}
if req.QueryParameter("limit") != "" {
resp.WriteAsJson(map[string]interface{}{"items": users, "total_count": len(members)})
} else {
resp.WriteAsJson(users)
}
}

View File

@@ -21,9 +21,10 @@ import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/storage"
"net/http"
)
type scMetricsItem struct {
type ScMetricsItem struct {
Name string `json:"name"`
Metrics *storage.ScMetrics `json:"metrics"`
}
@@ -35,11 +36,12 @@ func GetScMetrics(request *restful.Request, response *restful.Response) {
metrics, err := storage.GetScMetrics(scName)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := scMetricsItem{
result := ScMetricsItem{
Name: scName, Metrics: metrics,
}
@@ -51,21 +53,23 @@ func GetScMetrics(request *restful.Request, response *restful.Response) {
func GetScMetricsList(request *restful.Request, response *restful.Response) {
scList, err := storage.GetScList()
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
// Set return value
items := make([]scMetricsItem, 0)
items := make([]ScMetricsItem, 0)
for _, v := range scList {
metrics, err := storage.GetScMetrics(v.GetName())
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
item := scMetricsItem{
item := ScMetricsItem{
Name: v.GetName(), Metrics: metrics,
}

View File

@@ -0,0 +1,220 @@
/*
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 monitoring
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/models/metrics"
)
func MonitorPod(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
podName := requestParams.PodName
metricName := requestParams.MetricsName
if podName != "" {
// single pod single metric
queryType, params, nullRule := metrics.AssemblePodMetricRequestInfo(requestParams, metricName)
var res *metrics.FormatedMetric
if !nullRule {
res = metrics.GetMetric(queryType, params, metricName)
}
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelPod)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func MonitorContainer(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if requestParams.MetricsFilter != "" {
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelContainer)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelContainerName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorContainer(requestParams, metricName)
response.WriteAsJson(res)
}
}
func MonitorWorkload(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkload)
var sortedMetrics *metrics.FormatedLevelMetric
var maxMetricCount int
wlKind := requestParams.WorkloadKind
// sorting
if wlKind == "" {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkload)
} else {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
}
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
func MonitorAllWorkspaces(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "_statistics" {
// merge multiple metric: all-devops, all-roles, all-projects...this api is designed for admin
res := metrics.MonitorAllWorkspacesStatistics()
response.WriteAsJson(res)
} else if tp == "rank" {
rawMetrics := metrics.MonitorAllWorkspaces(requestParams)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkspace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func MonitorOneWorkspace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "rank" {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else if tp == "_statistics" {
wsName := requestParams.WsName
// merge multiple metric: devops, roles, projects...
res := metrics.MonitorOneWorkspaceStatistics(wsName)
response.WriteAsJson(res)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func MonitorNamespace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
nsName := requestParams.NsName
if nsName != "" {
// single
queryType, params := metrics.AssembleNamespaceMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNamespace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func MonitorCluster(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleClusterMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelCluster)
response.WriteAsJson(res)
}
}
func MonitorNode(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleNodeMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
nodeAddress := metrics.GetNodeAddressInfo()
metrics.AddNodeAddressMetric(res, nodeAddress)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNode)
nodeAddress := metrics.GetNodeAddressInfo()
for i := 0; i < len(rawMetrics.Results); i++ {
metrics.AddNodeAddressMetric(&rawMetrics.Results[i], nodeAddress)
}
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNode)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
// k8s component(controller, scheduler, etcd) status
func MonitorComponentStatus(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
status := metrics.MonitorComponentStatus(requestParams)
response.WriteAsJson(status)
}

View File

@@ -19,10 +19,9 @@
package operations
import (
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/workloads"
"net/http"
"github.com/emicklei/go-restful"
@@ -40,10 +39,11 @@ func RerunJob(req *restful.Request, resp *restful.Response) {
case "rerun":
err = workloads.JobReRun(namespace, job)
default:
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, fmt.Sprintf("invalid operation %s", action)))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid operation %s", action)))
return
}
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -20,6 +20,7 @@ package operations
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/nodes"
@@ -31,7 +32,8 @@ func DrainNode(request *restful.Request, response *restful.Response) {
err := nodes.DrainNode(nodeName)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -20,48 +20,30 @@ package quotas
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/quotas"
)
func V1Alpha2(ws *restful.WebService) {
tags := []string{"quotas"}
ws.Route(ws.GET("/quotas").
To(getClusterQuotas).
Doc("get whole cluster's resource usage").
Writes(quotas.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags))
ws.Route(ws.GET("/namespaces/{namespace}/quotas").
Doc("get specified namespace's resource quota and usage").
Param(ws.PathParameter("namespace", "namespace's name").
DataType("string")).
Writes(quotas.ResourceQuota{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getNamespaceQuotas))
}
func getNamespaceQuotas(req *restful.Request, resp *restful.Response) {
func GetNamespaceQuotas(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
quota, err := quotas.GetNamespaceQuotas(namespace)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(quota)
}
func getClusterQuotas(req *restful.Request, resp *restful.Response) {
func GetClusterQuotas(req *restful.Request, resp *restful.Response) {
quota, err := quotas.GetClusterQuotas()
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -20,55 +20,29 @@ package registries
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/registries"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.POST("registries/verify").To(registryVerify))
}
func registryVerify(request *restful.Request, response *restful.Response) {
func RegistryVerify(request *restful.Request, response *restful.Response) {
authInfo := registries.AuthInfo{}
err := request.ReadEntity(&authInfo)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
err = registries.RegistryVerify(authInfo)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
response.WriteAsJson(errors.None)
}
//func (c *registriesHandler) handlerImageSearch(request *restful.Request, response *restful.Response) {
//
// registry := request.PathParameter("name")
// searchWord := request.PathParameter("searchWord")
// namespace := request.PathParameter("namespace")
//
// res := c.registries.ImageSearch(namespace, registry, searchWord)
//
// response.WriteAsJson(res)
//
//}
//
//func (c *registriesHandler) handlerGetImageTags(request *restful.Request, response *restful.Response) {
//
// registry := request.PathParameter("name")
// image := request.QueryParameter("image")
// namespace := request.PathParameter("namespace")
//
// res := c.registries.GetImageTags(namespace, registry, image)
//
// response.WriteAsJson(res)
//}

View File

@@ -19,6 +19,7 @@ package resources
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
@@ -27,14 +28,15 @@ import (
func ClusterResourceHandler(req *restful.Request, resp *restful.Response) {
resourceName := req.PathParameter("resources")
conditions := req.QueryParameter(params.Conditions)
orderBy := req.QueryParameter(params.OrderBy)
limit, offset := params.ParsePaging(req.QueryParameter(params.Paging))
reverse := params.ParseReserve(req.QueryParameter(params.Reserve))
conditions, err := params.ParseConditions(req)
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req)
reverse := params.ParseReverse(req)
result, err := resources.ListClusterResource(resourceName, conditions, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -19,6 +19,7 @@ package resources
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/resources"
@@ -28,14 +29,15 @@ import (
func NamespaceResourceHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
resourceName := req.PathParameter("resources")
conditions := req.QueryParameter(params.Conditions)
orderBy := req.QueryParameter(params.OrderBy)
limit, offset := params.ParsePaging(req.QueryParameter(params.Paging))
reverse := params.ParseReserve(req.QueryParameter(params.Reserve))
conditions, err := params.ParseConditions(req)
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req)
reverse := params.ParseReverse(req)
result, err := resources.ListNamespaceResource(namespace, resourceName, conditions, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -20,6 +20,7 @@ package resources
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/storage"
@@ -43,7 +44,8 @@ func GetPodListByPvc(request *restful.Request, response *restful.Response) {
pvcName := request.PathParameter("pvc")
nsName := request.PathParameter("namespace")
pods, err := storage.GetPodListByPvc(pvcName, nsName)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := podListByPvc{Name: pvcName, Namespace: nsName, Pods: pods}
@@ -56,7 +58,8 @@ func GetPvcListBySc(request *restful.Request, response *restful.Response) {
scName := request.PathParameter("storageclass")
claims, err := storage.GetPvcListBySc(scName)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -19,6 +19,7 @@ package resources
import (
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
@@ -31,7 +32,8 @@ func GetKubectl(req *restful.Request, resp *restful.Response) {
kubectlPod, err := kubectl.GetKubectlPod(user)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -44,7 +46,8 @@ func GetKubeconfig(req *restful.Request, resp *restful.Response) {
kubectlConfig, err := kubeconfig.GetKubeConfig(user)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -22,93 +22,59 @@ import (
"strconv"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/api/apps/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/revisions"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/namespaces/{namespace}/daemonsets/{daemonset}/revisions/{revision}").
To(getDaemonSetRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"daemonsets", "revision"}).
Doc("Handle daemonset operation").
Param(ws.PathParameter("daemonset", "daemonset's name").
DataType("string")).
Param(ws.PathParameter("namespace", "daemonset's namespace").
DataType("string")).
Param(ws.PathParameter("revision", "daemonset's revision")).
Writes(v1.DaemonSet{}))
ws.Route(ws.GET("/namespaces/{namespace}/deployments/{deployment}/revisions/{revision}").
To(getDeployRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"deployments", "revision"}).
Doc("Handle deployment operation").
Param(ws.PathParameter("deployment", "deployment's name").
DataType("string")).
Param(ws.PathParameter("namespace",
"deployment's namespace").
DataType("string")).
Param(ws.PathParameter("deployment", "deployment's name")).
Writes(v1.ReplicaSet{}))
ws.Route(ws.GET("/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}").
To(getStatefulSetRevision).
Metadata(restfulspec.KeyOpenAPITags, []string{"statefulsets", "revisions"}).
Doc("Handle statefulset operation").
Param(ws.PathParameter("statefulset", "statefulset's name").
DataType("string")).
Param(ws.PathParameter("namespace", "statefulset's namespace").
DataType("string")).
Param(ws.PathParameter("revision", "statefulset's revision")).
Writes(v1.StatefulSet{}))
}
func getDaemonSetRevision(req *restful.Request, resp *restful.Response) {
func GetDaemonSetRevision(req *restful.Request, resp *restful.Response) {
daemonset := req.PathParameter("daemonset")
namespace := req.PathParameter("namespace")
revision, err := strconv.Atoi(req.PathParameter("revision"))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := revisions.GetDaemonSetRevision(namespace, daemonset, revision)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func getDeployRevision(req *restful.Request, resp *restful.Response) {
func GetDeployRevision(req *restful.Request, resp *restful.Response) {
deploy := req.PathParameter("deployment")
namespace := req.PathParameter("namespace")
revision := req.PathParameter("revision")
result, err := revisions.GetDeployRevision(namespace, deploy, revision)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func getStatefulSetRevision(req *restful.Request, resp *restful.Response) {
func GetStatefulSetRevision(req *restful.Request, resp *restful.Response) {
statefulset := req.PathParameter("statefulset")
namespace := req.PathParameter("namespace")
revision, err := strconv.Atoi(req.PathParameter("revision"))
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := revisions.GetStatefulSetRevision(namespace, statefulset, revision)
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}

View File

@@ -19,58 +19,31 @@
package routers
import (
"fmt"
"github.com/emicklei/go-restful"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"net/http"
"strings"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"kubesphere.io/kubesphere/pkg/models/routers"
)
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/routers").To(getAllRouters).
Doc("Get all routers"))
ws.Route(ws.GET("/users/{username}/routers").To(getAllRoutersOfUser).
Doc("Get routers for user"))
ws.Route(ws.GET("/namespaces/{namespace}/router").To(getRouter).
Doc("Get router of a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.DELETE("/namespaces/{namespace}/router").To(deleteRouter).
Doc("Get router of a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.POST("/namespaces/{namespace}/router").To(createRouter).
Doc("Create a router for a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
ws.Route(ws.PUT("/namespaces/{namespace}/router").To(updateRouter).
Doc("Update a router for a specified project").
Param(ws.PathParameter("namespace", "name of the project").
DataType("string")))
}
type Router struct {
RouterType string `json:"type"`
Annotations map[string]string `json:"annotations"`
}
// Get all namespace ingress controller services
func getAllRouters(request *restful.Request, response *restful.Response) {
func GetAllRouters(request *restful.Request, response *restful.Response) {
routers, err := routers.GetAllRouters()
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -78,13 +51,14 @@ func getAllRouters(request *restful.Request, response *restful.Response) {
}
// Get all namespace ingress controller services for user
func getAllRoutersOfUser(request *restful.Request, response *restful.Response) {
func GetAllRoutersOfUser(request *restful.Request, response *restful.Response) {
username := request.PathParameter("username")
routers, err := routers.GetAllRoutersOfUser(username)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -92,12 +66,13 @@ func getAllRoutersOfUser(request *restful.Request, response *restful.Response) {
}
// Get ingress controller service for specified namespace
func getRouter(request *restful.Request, response *restful.Response) {
func GetRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
router, err := routers.GetRouter(namespace)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -105,7 +80,7 @@ func getRouter(request *restful.Request, response *restful.Response) {
}
// Create ingress controller and related services
func createRouter(request *restful.Request, response *restful.Response) {
func CreateRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
@@ -119,18 +94,17 @@ func createRouter(request *restful.Request, response *restful.Response) {
var router *v1.Service
serviceType, annotationMap, err := ParseParameter(newRouter)
serviceType, annotationMap, err := parseParameter(newRouter)
if err != nil {
glog.Error("Wrong annotations, missing key or value")
response.WriteHeaderAndEntity(http.StatusBadRequest,
errors.New(errors.InvalidArgument, "Wrong annotations, missing key or value"))
response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("wrong annotations, missing key or value")))
return
}
router, err = routers.CreateRouter(namespace, serviceType, annotationMap)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
@@ -138,19 +112,20 @@ func createRouter(request *restful.Request, response *restful.Response) {
}
// Delete ingress controller and services
func deleteRouter(request *restful.Request, response *restful.Response) {
func DeleteRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
router, err := routers.DeleteRouter(namespace)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
response.WriteAsJson(router)
}
func updateRouter(request *restful.Request, response *restful.Response) {
func UpdateRouter(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace")
@@ -158,23 +133,23 @@ func updateRouter(request *restful.Request, response *restful.Response) {
err := request.ReadEntity(&newRouter)
if err != nil {
glog.Error(err)
response.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
serviceType, annotationMap, err := ParseParameter(newRouter)
serviceType, annotationMap, err := parseParameter(newRouter)
router, err := routers.UpdateRouter(namespace, serviceType, annotationMap)
if errors.HandlerError(err, response) {
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
response.WriteAsJson(router)
}
func ParseParameter(router Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) {
func parseParameter(router Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) {
routerType = v1.ServiceTypeNodePort

View File

@@ -35,7 +35,9 @@ type ContainerBuilder []func(c *restful.Container) error
func NewWebService(gv schema.GroupVersion) *restful.WebService {
webservice := restful.WebService{}
webservice.Path(ApiRootPath + "/" + gv.String())
webservice.Path(ApiRootPath + "/" + gv.String()).
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON)
return &webservice
}

View File

@@ -20,39 +20,25 @@ package workloadstatuses
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/status"
)
func V1Alpha2(ws *restful.WebService) {
tags := []string{"workloadStatus"}
ws.Route(ws.GET("/workloadstatuses").
Doc("get abnormal workloads' count of whole cluster").
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getClusterResourceStatus))
ws.Route(ws.GET("/namespaces/{namespace}/workloadstatuses").
Doc("get abnormal workloads' count of specified namespace").
Param(ws.PathParameter("namespace", "the name of namespace").
DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
To(getNamespacesResourceStatus))
}
func getClusterResourceStatus(req *restful.Request, resp *restful.Response) {
func GetClusterResourceStatus(req *restful.Request, resp *restful.Response) {
res, err := status.GetClusterResourceStatus()
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(res)
}
func getNamespacesResourceStatus(req *restful.Request, resp *restful.Response) {
func GetNamespacesResourceStatus(req *restful.Request, resp *restful.Response) {
res, err := status.GetNamespacesResourceStatus(req.PathParameter("namespace"))
if errors.HandlerError(err, resp) {
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(res)

View File

@@ -16,491 +16,3 @@
*/
package workspaces
import (
"net/http"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/metrics"
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
"fmt"
"strings"
"strconv"
"regexp"
"sort"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/workspaces"
)
const UserNameHeader = "X-Token-Username"
func V1Alpha2(ws *restful.WebService) {
ws.Route(ws.GET("/workspaces").To(UserWorkspaceListHandler))
ws.Route(ws.POST("/workspaces").To(WorkspaceCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}").To(DeleteWorkspaceHandler))
ws.Route(ws.GET("/workspaces/{name}").To(WorkspaceDetailHandler))
ws.Route(ws.PUT("/workspaces/{name}").To(WorkspaceEditHandler))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").To(UserNamespaceListHandler))
ws.Route(ws.GET("/workspaces/{workspace}/members/{username}/namespaces").To(UserNamespaceListHandler))
ws.Route(ws.POST("/workspaces/{name}/namespaces").To(NamespaceCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}/namespaces/{namespace}").To(NamespaceDeleteHandler))
ws.Route(ws.GET("/workspaces/{name}/namespaces/{namespace}").To(NamespaceCheckHandler))
ws.Route(ws.GET("/namespaces/{namespace}").To(NamespaceCheckHandler))
ws.Route(ws.GET("/workspaces/{name}/devops").To(DevOpsProjectHandler))
ws.Route(ws.GET("/workspaces/{name}/members/{username}/devops").To(DevOpsProjectHandler))
ws.Route(ws.POST("/workspaces/{name}/devops").To(DevOpsProjectCreateHandler))
ws.Route(ws.DELETE("/workspaces/{name}/devops/{id}").To(DevOpsProjectDeleteHandler))
ws.Route(ws.GET("/workspaces/{name}/members").To(MembersHandler))
ws.Route(ws.GET("/workspaces/{name}/members/{member}").To(MemberHandler))
ws.Route(ws.GET("/workspaces/{name}/roles").To(RolesHandler))
// TODO /workspaces/{name}/roles/{role}
ws.Route(ws.POST("/workspaces/{name}/members").To(MembersInviteHandler))
ws.Route(ws.DELETE("/workspaces/{name}/members").To(MembersRemoveHandler))
}
func RolesHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
roles, err := workspaces.Roles(workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(roles)
}
func MembersHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
keyword := req.QueryParameter("keyword")
users, err := workspaces.GetWorkspaceMembers(workspace, keyword)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(users)
}
func MemberHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("member")
user, err := iam.GetUser(username)
if errors.HandlerError(err, resp) {
return
}
namespaces, err := workspaces.Namespaces(workspace)
if errors.HandlerError(err, resp) {
return
}
user.WorkspaceRole = user.WorkspaceRoles[workspace]
roles := make(map[string]string)
for _, namespace := range namespaces {
if role := user.Roles[namespace.Name]; role != "" {
roles[namespace.Name] = role
}
}
user.Roles = roles
user.Rules = nil
user.WorkspaceRules = nil
user.WorkspaceRoles = nil
user.ClusterRules = nil
resp.WriteAsJson(user)
}
func MembersInviteHandler(req *restful.Request, resp *restful.Response) {
var users []workspaces.UserInvite
workspace := req.PathParameter("name")
err := req.ReadEntity(&users)
if errors.HandlerError(err, resp) {
return
}
err = workspaces.Invite(workspace, users)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func MembersRemoveHandler(req *restful.Request, resp *restful.Response) {
query := req.QueryParameter("name")
workspace := req.PathParameter("name")
names := strings.Split(query, ",")
err := workspaces.RemoveMembers(workspace, names)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func NamespaceCheckHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
exist, err := workspaces.NamespaceExistCheck(namespace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(map[string]bool{"exist": exist})
}
func NamespaceDeleteHandler(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
workspace := req.PathParameter("name")
err := workspaces.DeleteNamespace(workspace, namespace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectDeleteHandler(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("id")
workspace := req.PathParameter("name")
force := req.QueryParameter("force")
username := req.HeaderParameter(UserNameHeader)
err := workspaces.UnBindDevopsProject(workspace, devops)
if err != nil && force != "true" {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.New(errors.Internal, err.Error()))
return
}
err = workspaces.DeleteDevopsProject(username, devops)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func DevOpsProjectCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
var devops workspaces.DevopsProject
err := req.ReadEntity(&devops)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
project, err := workspaces.CreateDevopsProject(username, workspace, devops)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(project)
}
func NamespaceCreateHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.HeaderParameter(UserNameHeader)
namespace := &v1.Namespace{}
err := req.ReadEntity(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if namespace.Annotations == nil {
namespace.Annotations = make(map[string]string, 0)
}
namespace.Annotations["creator"] = username
namespace.Annotations["workspace"] = workspace
if namespace.Labels == nil {
namespace.Labels = make(map[string]string, 0)
}
namespace.Labels["kubesphere.io/workspace"] = workspace
namespace, err = workspaces.CreateNamespace(namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
resp.WriteAsJson(namespace)
}
func DevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("name")
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
total, devOpsProjects, err := workspaces.ListDevopsProjectsByUser(username, workspace, keyword, orderBy, reverse, limit, offset)
if errors.HandlerError(err, resp) {
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range devOpsProjects {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}
func WorkspaceCreateHandler(req *restful.Request, resp *restful.Response) {
var workspace workspaces.Workspace
username := req.HeaderParameter(UserNameHeader)
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
if workspace.Admin != "" {
workspace.Creator = workspace.Admin
} else {
workspace.Creator = username
}
created, err := workspaces.Create(&workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(created)
}
func DeleteWorkspaceHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
if name == "" || strings.Contains(name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
err = workspaces.Delete(workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(errors.None)
}
func WorkspaceEditHandler(req *restful.Request, resp *restful.Response) {
var workspace workspaces.Workspace
name := req.PathParameter("name")
err := req.ReadEntity(&workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, err.Error()))
return
}
if name != workspace.Name {
resp.WriteError(http.StatusBadRequest, fmt.Errorf("the name of workspace (%s) does not match the name on the URL (%s)", workspace.Name, name))
return
}
if workspace.Name == "" || strings.Contains(workspace.Name, ":") {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(errors.InvalidArgument, "invalid workspace name"))
return
}
workspace.Path = workspace.Name
workspace.Members = nil
edited, err := workspaces.Edit(&workspace)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(edited)
}
func WorkspaceDetailHandler(req *restful.Request, resp *restful.Response) {
name := req.PathParameter("name")
workspace, err := workspaces.Detail(name)
if errors.HandlerError(err, resp) {
return
}
resp.WriteAsJson(workspace)
}
// List all workspaces for the current user
func UserWorkspaceListHandler(req *restful.Request, resp *restful.Response) {
keyword := req.QueryParameter("keyword")
username := req.HeaderParameter(UserNameHeader)
ws, err := workspaces.ListWorkspaceByUser(username, keyword)
if errors.HandlerError(err, resp) {
return
}
sort.Slice(ws, func(i, j int) bool {
t1, err := ws[i].GetCreateTime()
if err != nil {
return false
}
t2, err := ws[j].GetCreateTime()
if err != nil {
return true
}
return t1.After(t2)
})
resp.WriteAsJson(ws)
}
func UserNamespaceListHandler(req *restful.Request, resp *restful.Response) {
withMetrics, err := strconv.ParseBool(req.QueryParameter("metrics"))
if err != nil {
withMetrics = false
}
username := req.PathParameter("username")
keyword := req.QueryParameter("keyword")
if username == "" {
username = req.HeaderParameter(UserNameHeader)
}
limit := 65535
offset := 0
orderBy := "createTime"
reverse := true
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(req.QueryParameter("paging")); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
if groups := regexp.MustCompile(`^(createTime|name)$`).FindStringSubmatch(req.QueryParameter("order")); len(groups) == 2 {
orderBy = groups[1]
reverse = false
}
if q := req.QueryParameter("reverse"); q != "" {
b, err := strconv.ParseBool(q)
if err == nil {
reverse = b
}
}
workspaceName := req.PathParameter("workspace")
total, namespaces, err := workspaces.ListNamespaceByUser(workspaceName, username, keyword, orderBy, reverse, limit, offset)
if withMetrics {
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
}
if errors.HandlerError(err, resp) {
return
}
result := models.PageableResponse{}
result.TotalCount = total
result.Items = make([]interface{}, 0)
for _, n := range namespaces {
result.Items = append(result.Items, n)
}
resp.WriteAsJson(result)
}

View File

@@ -1,51 +1,56 @@
/*
Copyright 2018 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
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 client
import (
"fmt"
"flag"
"log"
"sync"
_ "github.com/go-sql-driver/mysql"
"github.com/golang/glog"
"github.com/jinzhu/gorm"
)
var dbClient *gorm.DB
var (
dbClientOnce sync.Once
dbClient *gorm.DB
dsn string
)
func NewDBClient() *gorm.DB {
conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", "", "", "", "")
db, err := gorm.Open("mysql", conn)
if err != nil {
glog.Error(err)
panic(err)
}
return db
func init() {
flag.StringVar(&dsn, "database-connection", "root@tcp(localhost:3306)/kubesphere?charset=utf8&parseTime=True", "data source name")
}
func NewSharedDBClient() *gorm.DB {
func DBClient() *gorm.DB {
dbClientOnce.Do(func() {
var err error
dbClient, err = gorm.Open("mysql", dsn)
if dbClient != nil {
err := dbClient.DB().Ping()
if err == nil {
return dbClient
} else {
glog.Error(err)
panic(err)
if err != nil {
log.Fatalln(err)
}
}
return NewDBClient()
if err := dbClient.DB().Ping(); err != nil {
log.Fatalln(err)
}
})
return dbClient
}

View File

@@ -19,25 +19,31 @@
package client
import (
"flag"
"fmt"
"log"
"os"
"sync"
"k8s.io/client-go/tools/clientcmd"
"github.com/mitchellh/go-homedir"
"github.com/golang/glog"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
var (
KubeConfigFile string
kubeConfigFile string
k8sClient *kubernetes.Clientset
k8sClientOnce sync.Once
KubeConfig *rest.Config
)
func init() {
flag.StringVar(&kubeConfigFile, "kubeconfig", fmt.Sprintf("%s/.kube/config", os.Getenv("HOME")), "path to kubeconfig file")
}
func K8sClient() *kubernetes.Clientset {
k8sClientOnce.Do(func() {
@@ -45,14 +51,10 @@ func K8sClient() *kubernetes.Clientset {
config, err := getKubeConfig()
if err != nil {
glog.Fatalf("cannot load kubeconfig: %v", err)
log.Fatalln(err)
}
k8sClient, err = kubernetes.NewForConfig(config)
if err != nil {
glog.Fatalf("cannot create k8s client: %v", err)
}
k8sClient = kubernetes.NewForConfigOrDie(config)
KubeConfig = config
})
@@ -62,36 +64,28 @@ func K8sClient() *kubernetes.Clientset {
func getKubeConfig() (kubeConfig *rest.Config, err error) {
if KubeConfigFile == "" {
if kubeConfigFile == "" {
if env := os.Getenv("KUBECONFIG"); env != "" {
KubeConfigFile = env
kubeConfigFile = env
} else {
if home, err := homedir.Dir(); err == nil {
KubeConfigFile = fmt.Sprintf("%s/.kube/config", home)
kubeConfigFile = fmt.Sprintf("%s/.kube/config", home)
}
}
}
if KubeConfigFile != "" {
kubeConfig, err = clientcmd.BuildConfigFromFlags("", KubeConfigFile)
if err != nil {
return nil, err
}
if _, err = os.Stat(kubeConfigFile); err == nil {
kubeConfig, err = clientcmd.BuildConfigFromFlags("", kubeConfigFile)
} else {
kubeConfig, err = rest.InClusterConfig()
}
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
kubeConfig.QPS = 1e6
kubeConfig.Burst = 1e6
return kubeConfig, nil
}

188
pkg/client/ldap/channel.go Normal file
View File

@@ -0,0 +1,188 @@
/*
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 ldap
import (
"errors"
"log"
"sync"
"github.com/go-ldap/ldap"
)
// channelPool implements the Pool interface based on buffered channels.
type channelPool struct {
// storage for our net.Conn connections
mu sync.Mutex
conns chan ldap.Client
name string
aliveChecks bool
// net.Conn generator
factory PoolFactory
closeAt []uint16
}
// PoolFactory is a function to create new connections.
type PoolFactory func(string) (ldap.Client, error)
// NewChannelPool returns a new pool based on buffered channels with an initial
// capacity and maximum capacity. Factory is used when initial capacity is
// greater than zero to fill the pool. A zero initialCap doesn't fill the Pool
// until a new Get() is called. During a Get(), If there is no new connection
// available in the pool, a new connection will be created via the Factory()
// method.
//
// closeAt will automagically mark the connection as unusable if the return code
// of the call is one of those passed, most likely you want to set this to something
// like
// []uint8{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork}
func NewChannelPool(initialCap, maxCap int, name string, factory PoolFactory, closeAt []uint16) (Pool, error) {
if initialCap < 0 || maxCap <= 0 || initialCap > maxCap {
return nil, errors.New("invalid capacity settings")
}
c := &channelPool{
conns: make(chan ldap.Client, maxCap),
name: name,
factory: factory,
closeAt: closeAt,
aliveChecks: true,
}
// create initial connections, if something goes wrong,
// just close the pool error out.
for i := 0; i < initialCap; i++ {
conn, err := factory(c.name)
if err != nil {
c.Close()
return nil, errors.New("factory is not able to fill the pool: " + err.Error())
}
c.conns <- conn
}
return c, nil
}
func (c *channelPool) AliveChecks(on bool) {
c.mu.Lock()
c.aliveChecks = on
c.mu.Unlock()
}
func (c *channelPool) getConns() chan ldap.Client {
c.mu.Lock()
conns := c.conns
c.mu.Unlock()
return conns
}
// Get implements the Pool interfaces Get() method. If there is no new
// connection available in the pool, a new connection will be created via the
// Factory() method.
func (c *channelPool) Get() (*PoolConn, error) {
conns := c.getConns()
if conns == nil {
return nil, ErrClosed
}
// wrap our connections with our ldap.Client implementation (wrapConn
// method) that puts the connection back to the pool if it's closed.
select {
case conn := <-conns:
if conn == nil {
return nil, ErrClosed
}
if !c.aliveChecks || isAlive(conn) {
return c.wrapConn(conn, c.closeAt), nil
}
conn.Close()
return c.NewConn()
default:
return c.NewConn()
}
}
func isAlive(conn ldap.Client) bool {
_, err := conn.Search(&ldap.SearchRequest{BaseDN: "", Scope: ldap.ScopeBaseObject, Filter: "(&)", Attributes: []string{"1.1"}})
return err == nil
}
func (c *channelPool) NewConn() (*PoolConn, error) {
conn, err := c.factory(c.name)
if err != nil {
return nil, err
}
return c.wrapConn(conn, c.closeAt), nil
}
// put puts the connection back to the pool. If the pool is full or closed,
// conn is simply closed. A nil conn will be rejected.
func (c *channelPool) put(conn ldap.Client) {
if conn == nil {
log.Printf("connection is nil. rejecting")
return
}
c.mu.Lock()
defer c.mu.Unlock()
if c.conns == nil {
// pool is closed, close passed connection
conn.Close()
return
}
// put the resource back into the pool. If the pool is full, this will
// block and the default case will be executed.
select {
case c.conns <- conn:
return
default:
// pool is full, close passed connection
conn.Close()
return
}
}
func (c *channelPool) Close() {
c.mu.Lock()
conns := c.conns
c.conns = nil
c.factory = nil
c.mu.Unlock()
if conns == nil {
return
}
close(conns)
for conn := range conns {
conn.Close()
}
return
}
func (c *channelPool) Len() int { return len(c.getConns()) }
func (c *channelPool) wrapConn(conn ldap.Client, closeAt []uint16) *PoolConn {
p := &PoolConn{c: c, closeAt: closeAt}
p.Conn = conn
return p
}

113
pkg/client/ldap/conn.go Normal file
View File

@@ -0,0 +1,113 @@
/*
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 ldap
import (
"crypto/tls"
"log"
"time"
"github.com/go-ldap/ldap"
)
// PoolConn implements Client to override the Close() method
type PoolConn struct {
Conn ldap.Client
c *channelPool
unusable bool
closeAt []uint16
}
func (p *PoolConn) Start() {
p.Conn.Start()
}
func (p *PoolConn) StartTLS(config *tls.Config) error {
// FIXME - check if already TLS and then ignore?
return p.Conn.StartTLS(config)
}
// Close() puts the given connects back to the pool instead of closing it.
func (p *PoolConn) Close() {
if p.unusable {
log.Printf("Closing unusable connection")
if p.Conn != nil {
p.Conn.Close()
}
return
}
p.c.put(p.Conn)
}
func (p *PoolConn) SimpleBind(simpleBindRequest *ldap.SimpleBindRequest) (*ldap.SimpleBindResult, error) {
return p.Conn.SimpleBind(simpleBindRequest)
}
func (p *PoolConn) Bind(username, password string) error {
return p.Conn.Bind(username, password)
}
func (p *PoolConn) ModifyDN(modifyDNRequest *ldap.ModifyDNRequest) error {
return p.Conn.ModifyDN(modifyDNRequest)
}
// MarkUnusable() marks the connection not usable any more, to let the pool close it
// instead of returning it to pool.
func (p *PoolConn) MarkUnusable() {
p.unusable = true
}
func (p *PoolConn) autoClose(err error) {
for _, code := range p.closeAt {
if ldap.IsErrorWithCode(err, code) {
p.MarkUnusable()
return
}
}
}
func (p *PoolConn) SetTimeout(t time.Duration) {
p.Conn.SetTimeout(t)
}
func (p *PoolConn) Add(addRequest *ldap.AddRequest) error {
return p.Conn.Add(addRequest)
}
func (p *PoolConn) Del(delRequest *ldap.DelRequest) error {
return p.Conn.Del(delRequest)
}
func (p *PoolConn) Modify(modifyRequest *ldap.ModifyRequest) error {
return p.Conn.Modify(modifyRequest)
}
func (p *PoolConn) Compare(dn, attribute, value string) (bool, error) {
return p.Conn.Compare(dn, attribute, value)
}
func (p *PoolConn) PasswordModify(passwordModifyRequest *ldap.PasswordModifyRequest) (*ldap.PasswordModifyResult, error) {
return p.Conn.PasswordModify(passwordModifyRequest)
}
func (p *PoolConn) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) {
return p.Conn.Search(searchRequest)
}
func (p *PoolConn) SearchWithPaging(searchRequest *ldap.SearchRequest, pagingSize uint32) (*ldap.SearchResult, error) {
return p.Conn.SearchWithPaging(searchRequest, pagingSize)
}

43
pkg/client/ldap/pool.go Normal file
View File

@@ -0,0 +1,43 @@
/*
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 ldap
import (
"errors"
)
var (
// ErrClosed is the error resulting if the pool is closed via pool.Close().
ErrClosed = errors.New("pool is closed")
)
// Pool interface describes a pool implementation. A pool should have maximum
// capacity. An ideal pool is threadsafe and easy to use.
type Pool interface {
// Get returns a new connection from the pool. Closing the connections puts
// it back to the Pool. Closing it when the pool is destroyed or full will
// be counted as an error.
Get() (*PoolConn, error)
// Close closes the pool and all its connections. After Close() the pool is
// no longer usable.
Close()
// Len returns the current number of connections of the pool.
Len() int
}

65
pkg/client/ldapclient.go Normal file
View File

@@ -0,0 +1,65 @@
/*
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 client
import (
"flag"
"fmt"
"github.com/go-ldap/ldap"
ldapPool "kubesphere.io/kubesphere/pkg/client/ldap"
"os"
"sync"
)
var (
once sync.Once
pool ldapPool.Pool
ldapHost string
ManagerDN string
ManagerPassword string
UserSearchBase string
GroupSearchBase string
)
func init() {
flag.StringVar(&ldapHost, "ldap-server", "localhost:389", "ldap server host")
flag.StringVar(&ManagerDN, "ldap-manager-dn", "cn=admin,dc=example,dc=org", "ldap manager dn")
flag.StringVar(&ManagerPassword, "ldap-manager-password", "admin", "ldap manager password")
flag.StringVar(&UserSearchBase, "ldap-user-search-base", "ou=Users,dc=example,dc=org", "ldap user search base")
flag.StringVar(&GroupSearchBase, "ldap-group-search-base", "ou=Groups,dc=example,dc=org", "ldap group search base")
}
func LdapClient() ldapPool.Pool {
once.Do(func() {
var err error
pool, err = ldapPool.NewChannelPool(8, 96, "kubesphere", func(s string) (ldap.Client, error) {
conn, err := ldap.Dial("tcp", ldapHost)
if err != nil {
return nil, err
}
return conn, nil
}, []uint16{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork})
if err != nil {
fmt.Fprint(os.Stderr, err.Error())
panic(err)
}
})
return pool
}

View File

@@ -1,18 +1,24 @@
/*
Copyright 2018 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
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 client
import (
"flag"
"io/ioutil"
"net/http"
"net/url"
@@ -20,31 +26,23 @@ import (
"strings"
"time"
"os"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
)
const (
DefaultScheme = "http"
DefaultPrometheusPort = "9090"
PrometheusApiPath = "/api/v1/"
DefaultQueryStep = "10m"
DefaultQueryTimeout = "10s"
RangeQueryType = "query_range?"
DefaultQueryType = "query?"
PrometheusAPIServerEnv = "PROMETHEUS_API_SERVER"
DefaultQueryStep = "10m"
DefaultQueryTimeout = "10s"
RangeQueryType = "query_range?"
DefaultQueryType = "query?"
)
var PrometheusAPIServer = "prometheus-k8s.kubesphere-monitoring-system.svc"
var PrometheusEndpointUrl string
var (
prometheusAPIEndpoint string
)
func init() {
if env := os.Getenv(PrometheusAPIServerEnv); env != "" {
PrometheusAPIServer = env
}
PrometheusEndpointUrl = DefaultScheme + "://" + PrometheusAPIServer + ":" + DefaultPrometheusPort + PrometheusApiPath
flag.StringVar(&prometheusAPIEndpoint, "prometheus-endpoint", "http://prometheus-k8s.kubesphere-monitoring-system.svc:9090/api/v1/", "prometheus api endpoint")
}
type MonitoringRequestParams struct {
@@ -72,11 +70,10 @@ type MonitoringRequestParams struct {
WorkloadKind string
}
var client = &http.Client{}
func SendMonitoringRequest(queryType string, params string) string {
epurl := PrometheusEndpointUrl + queryType + params
response, err := client.Get(epurl)
epurl := prometheusAPIEndpoint + queryType + params
response, err := http.DefaultClient.Get(epurl)
if err != nil {
glog.Error(err)
} else {
@@ -116,11 +113,11 @@ func ParseMonitoringRequestParams(request *restful.Request) *MonitoringRequestPa
metricsName := strings.Trim(request.QueryParameter("metrics_name"), " ")
workloadName := strings.Trim(request.QueryParameter("workload_name"), " ")
nodeId := strings.Trim(request.PathParameter("node_id"), " ")
wsName := strings.Trim(request.PathParameter("workspace_name"), " ")
nsName := strings.Trim(request.PathParameter("ns_name"), " ")
podName := strings.Trim(request.PathParameter("pod_name"), " ")
containerName := strings.Trim(request.PathParameter("container_name"), " ")
nodeId := strings.Trim(request.PathParameter("node"), " ")
wsName := strings.Trim(request.PathParameter("workspace"), " ")
nsName := strings.Trim(request.PathParameter("namespace"), " ")
podName := strings.Trim(request.PathParameter("pod"), " ")
containerName := strings.Trim(request.PathParameter("container"), " ")
workloadKind := strings.Trim(request.PathParameter("workload_kind"), " ")
var requestParams = MonitoringRequestParams{

56
pkg/client/redis.go Normal file
View File

@@ -0,0 +1,56 @@
/*
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 client
import (
"flag"
"log"
"sync"
"github.com/go-redis/redis"
)
var (
redisHost string
redisPassword string
redisDB int
redisClientOnce sync.Once
redisClient *redis.Client
)
func init() {
flag.StringVar(&redisHost, "redis-server", "localhost:6379", "redis server host")
flag.StringVar(&redisPassword, "redis-password", "", "redis password")
flag.IntVar(&redisDB, "redis-db", 0, "redis db")
}
func RedisClient() *redis.Client {
redisClientOnce.Do(func() {
redisClient = redis.NewClient(&redis.Options{
Addr: redisHost,
Password: redisPassword,
DB: redisDB,
})
if err := redisClient.Ping().Err(); err != nil {
log.Fatalln(err)
}
})
return redisClient
}

View File

@@ -1,6 +1,21 @@
package constants
/*
import "os"
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 constants
const (
APIVersion = "v1alpha1"
@@ -24,32 +39,18 @@ const (
DevopsOwner = "owner"
DevopsReporter = "reporter"
DevopsAPIServerEnv = "DEVOPS_API_SERVER"
AccountAPIServerEnv = "ACCOUNT_API_SERVER"
DevopsProxyTokenEnv = "DEVOPS_PROXY_TOKEN"
OpenPitrixProxyTokenEnv = "OPENPITRIX_PROXY_TOKEN"
envDevopsAPIServer = "DEVOPS_API_SERVER"
envAccountAPIServer = "ACCOUNT_API_SERVER"
envDevopsProxyToken = "DEVOPS_PROXY_TOKEN"
envOpenPitrixProxyToken = "OPENPITRIX_PROXY_TOKEN"
UserNameHeader = "X-Token-Username"
)
var (
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
AccountAPIServer = "ks-account.kubesphere-system.svc"
DevopsProxyToken = ""
OpenPitrixProxyToken = ""
SystemNamespaces = []string{KubeSystemNamespace, OpenPitrixNamespace, KubeSystemNamespace}
WorkSpaceRoles = []string{WorkspaceAdmin, WorkspaceRegular, WorkspaceViewer}
SystemWorkspace = "system-workspace"
DevopsAPIServer = "ks-devops-apiserver.kubesphere-system.svc"
AccountAPIServer = "ks-account.kubesphere-system.svc"
SystemNamespaces = []string{KubeSystemNamespace, OpenPitrixNamespace, KubeSystemNamespace}
)
func init() {
if env := os.Getenv(DevopsAPIServerEnv); env != "" {
DevopsAPIServer = env
}
if env := os.Getenv(AccountAPIServerEnv); env != "" {
AccountAPIServer = env
}
if env := os.Getenv(DevopsProxyTokenEnv); env != "" {
DevopsProxyToken = env
}
if env := os.Getenv(OpenPitrixProxyTokenEnv); env != "" {
OpenPitrixProxyToken = env
}
}

View File

@@ -1,17 +0,0 @@
package errors
type Code int
const (
OK Code = iota
Canceled
Unknown
InvalidArgument
Internal // 5
Unavailable
AlreadyExists
NotFound
NotImplement
VerifyFailed
Conflict
)

View File

@@ -1,16 +0,0 @@
// Code generated by "stringer -type=Code"; DO NOT EDIT.
package errors
import "strconv"
const _Code_name = "OKCanceledUnknownInvalidArgumentInternalUnavailableAlreadyExistsWTFNotFoundNotImplementVerifyFailed"
var _Code_index = [...]uint8{0, 2, 10, 17, 32, 40, 51, 64, 67, 75, 87, 99}
func (i Code) String() string {
if i < 0 || i >= Code(len(_Code_index)-1) {
return "Code(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Code_name[_Code_index[i]:_Code_index[i+1]]
}

View File

@@ -1,7 +0,0 @@
package errors
import "testing"
func TestCode_String(t *testing.T) {
t.Log(Code(1).String())
}

View File

@@ -1,105 +1,42 @@
/*
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 errors
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"reflect"
k8sError "k8s.io/apimachinery/pkg/api/errors"
"github.com/emicklei/go-restful"
"github.com/go-sql-driver/mysql"
"github.com/golang/glog"
)
type Error struct {
Code Code `json:"code"`
Message string `json:"message"`
}
var None = New(OK, "success")
var None = Error{Message: "success"}
func (e *Error) Error() string {
return fmt.Sprintf("error: %v,message: %v", e.Code.String(), e.Message)
}
func (e *Error) HttpStatusCode() int {
switch e.Code {
case OK:
return http.StatusOK
case InvalidArgument:
return http.StatusBadRequest
case AlreadyExists:
return http.StatusConflict
case Unavailable:
return http.StatusServiceUnavailable
case NotImplement:
return http.StatusNotImplemented
case VerifyFailed:
return http.StatusBadRequest
case Conflict:
return http.StatusConflict
case Internal:
fallthrough
case Unknown:
fallthrough
default:
return http.StatusInternalServerError
}
return e.Message
}
func New(code Code, message string) error {
if message == "" {
message = code.String()
}
return &Error{Code: code, Message: message}
func Wrap(err error) Error {
return Error{Message: err.Error()}
}
func HandlerError(err error, resp *restful.Response) bool {
if err == nil {
return false
}
glog.Errorln(reflect.TypeOf(err), err)
resp.WriteHeaderAndEntity(wrapper(err))
return true
}
func wrapper(err error) (int, interface{}) {
switch err.(type) {
case *Error:
case *json.UnmarshalTypeError:
err = New(InvalidArgument, err.Error())
case *mysql.MySQLError:
err = wrapperMysqlError(err.(*mysql.MySQLError))
case *net.OpError:
err = New(Internal, err.Error())
default:
if k8sError.IsNotFound(err) {
err = New(NotFound, err.Error())
} else {
err = New(Unknown, err.Error())
}
}
return err.(*Error).HttpStatusCode(), err
}
func wrapperMysqlError(sqlError *mysql.MySQLError) error {
switch sqlError.Number {
case 1062:
return New(AlreadyExists, sqlError.Message)
default:
return New(Unknown, sqlError.Message)
}
}
func Wrap(data []byte) error {
func Parse(data []byte) error {
var j map[string]string
err := json.Unmarshal(data, &j)
if err != nil {

View File

@@ -18,47 +18,27 @@
package components
import (
"time"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/models"
lister "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
coreV1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
)
type Component struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
SelfLink string `json:"selfLink"`
Label interface{} `json:"label"`
StartedAt time.Time `json:"startedAt"`
TotalBackends int `json:"totalBackends"`
HealthyBackends int `json:"healthyBackends"`
}
var (
componentStatusLister lister.ComponentStatusLister
serviceLister lister.ServiceLister
podLister lister.PodLister
nodeLister lister.NodeLister
)
func init() {
componentStatusLister = informers.SharedInformerFactory().Core().V1().ComponentStatuses().Lister()
serviceLister = informers.SharedInformerFactory().Core().V1().Services().Lister()
podLister = informers.SharedInformerFactory().Core().V1().Pods().Lister()
nodeLister = informers.SharedInformerFactory().Core().V1().Nodes().Lister()
}
func GetComponentStatus(name string) (interface{}, error) {
var service *coreV1.Service
var service *corev1.Service
var err error
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
for _, ns := range constants.SystemNamespaces {
service, err = serviceLister.Services(ns).Get(name)
if err == nil {
@@ -70,13 +50,15 @@ func GetComponentStatus(name string) (interface{}, error) {
return nil, err
}
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
pods, err := podLister.Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector))
if err != nil {
return nil, err
}
component := Component{
component := models.Component{
Name: service.Name,
Namespace: service.Namespace,
SelfLink: service.SelfLink,
@@ -102,11 +84,11 @@ func GetSystemHealthStatus() (map[string]interface{}, error) {
status := make(map[string]interface{})
componentStatuses, err := componentStatusLister.List(labels.Everything())
componentStatuses, err := client.K8sClient().CoreV1().ComponentStatuses().List(meta_v1.ListOptions{})
if err != nil {
return nil, err
}
for _, cs := range componentStatuses {
for _, cs := range componentStatuses.Items {
status[cs.Name] = cs.Conditions[0]
}
@@ -119,6 +101,8 @@ func GetSystemHealthStatus() (map[string]interface{}, error) {
for k, v := range systemComponentStatus {
status[k] = v
}
nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister()
// get node status
nodes, err := nodeLister.List(labels.Everything())
if err != nil {
@@ -132,7 +116,7 @@ func GetSystemHealthStatus() (map[string]interface{}, error) {
for _, nodes := range nodes {
totalNodes++
for _, condition := range nodes.Status.Conditions {
if condition.Type == coreV1.NodeReady && condition.Status == coreV1.ConditionTrue {
if condition.Type == corev1.NodeReady && condition.Status == corev1.ConditionTrue {
healthyNodes++
}
}
@@ -147,11 +131,12 @@ func GetSystemHealthStatus() (map[string]interface{}, error) {
}
func GetAllComponentsStatus() (map[string]interface{}, error) {
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
status := make(map[string]interface{})
var err error
for _, ns := range constants.SystemNamespaces {
nsStatus := make(map[string]interface{})
@@ -164,7 +149,7 @@ func GetAllComponentsStatus() (map[string]interface{}, error) {
}
for _, service := range services {
component := Component{
component := models.Component{
Name: service.Name,
Namespace: service.Namespace,
SelfLink: service.SelfLink,

822
pkg/models/iam/am.go Normal file
View File

@@ -0,0 +1,822 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/informers"
"log"
"net/http"
"regexp"
"strings"
"github.com/go-ldap/ldap"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
)
func GetNamespaces(username string) ([]*corev1.Namespace, error) {
roles, err := GetRoles(username, "")
if err != nil {
return nil, err
}
namespaces := make([]*corev1.Namespace, 0)
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
for _, role := range roles {
namespace, err := namespaceLister.Get(role.Name)
if err != nil {
return nil, err
}
namespaces = append(namespaces, namespace)
}
return namespaces, nil
}
func GetNamespacesByWorkspace(workspace string) ([]*corev1.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
return namespaceLister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspace}))
}
func GetDevopsRole(projectId string, username string) (string, error) {
//Hard fix
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > 200 {
return "", errors.New(string(data))
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
func GetNamespace(namespaceName string) (*corev1.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
return namespaceLister.Get(namespaceName)
}
func GetRoles(username string, namespace string) ([]*v1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]*v1.Role, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules}
role.Namespace = roleBinding.Namespace
roles = append(roles, &role)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
} else {
if subject.Kind == v1.UserKind && subject.Name == username {
rule, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
if err == nil {
roles = append(roles, rule)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
}
}
}
}
}
return roles, nil
}
func GetClusterRoles(username string) ([]*v1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]*v1.ClusterRole, 0)
for _, rb := range clusterRoleBindings {
if rb.RoleRef.Kind == ClusterRoleKind {
for _, subject := range rb.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
role, err := clusterRoleLister.Get(rb.RoleRef.Name)
role = role.DeepCopy()
if err == nil {
if role.Annotations == nil {
role.Annotations = make(map[string]string, 0)
}
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = rb.Name
if rb.Annotations != nil &&
rb.Annotations["rbac.authorization.k8s.io/clusterrole"] == rb.RoleRef.Name {
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
}
roles = append(roles, role)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
}
}
}
}
return roles, nil
}
func GetRoleBindings(namespace string, roleName string) ([]*v1.RoleBinding, error) {
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleBindingList, err := roleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.RoleBinding, 0)
for _, roleBinding := range roleBindingList {
if roleName == "" {
items = append(items, roleBinding)
} else if roleBinding.RoleRef.Name == roleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func GetClusterRoleBindings(clusterRoleName string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
roleBindingList, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.ClusterRoleBinding, 0)
for _, roleBinding := range roleBindingList {
if roleBinding.RoleRef.Name == clusterRoleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func ClusterRoleUsers(clusterRoleName string) ([]*models.User, error) {
roleBindings, err := GetClusterRoleBindings(clusterRoleName)
if err != nil {
return nil, err
}
conn, err := NewConnection()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !strings.HasPrefix(subject.Name, "system") &&
!slice.ContainsString(names, subject.Name, nil) {
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
users = append(users, user)
}
}
}
return users, nil
}
func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespace, roleName)
if err != nil {
return nil, err
}
conn, err := NewConnection()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind &&
!strings.HasPrefix(subject.Name, "system") &&
!slice.ContainsString(names, subject.Name, nil) {
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
users = append(users, user)
}
}
}
return users, nil
}
func NamespaceUsers(namespaceName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespaceName, "")
if err != nil {
return nil, err
}
conn, err := NewConnection()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind &&
!slice.ContainsString(names, subject.Name, nil) &&
!strings.HasPrefix(subject.Name, "system") {
if roleBinding.Name == "viewer" {
continue
}
if roleBinding.Name == "admin" {
continue
}
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
user.Role = roleBinding.RoleRef.Name
user.RoleBinding = roleBinding.Name
users = append(users, user)
}
}
}
return users, nil
}
func GetWorkspaceRoles(clusterRoles []*v1.ClusterRole) map[string]string {
workspaceRoles := make(map[string]string, 0)
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
workspaceRoles[groups[1]] = groups[2]
}
}
return workspaceRoles
}
func GetWorkspaceRole(clusterRoles []*v1.ClusterRole, workspace string) string {
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
if groups[1] == workspace {
return groups[2]
}
}
}
return ""
}
func GetWorkspaceSimpleRules(clusterRoles []*v1.ClusterRole, workspace string) map[string][]models.SimpleRule {
workspaceRules := make(map[string][]models.SimpleRule, 0)
clusterSimpleRules := make([]models.SimpleRule, 0)
clusterRules := make([]v1.PolicyRule, 0)
for _, clusterRole := range clusterRoles {
clusterRules = append(clusterRules, clusterRole.Rules...)
}
for i := 0; i < len(policy.WorkspaceRoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.WorkspaceRoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < (len(policy.WorkspaceRoleRuleMapping[i].Actions)); j++ {
if RulesMatchesAction(clusterRules, policy.WorkspaceRoleRuleMapping[i].Actions[j]) {
rule.Actions = append(rule.Actions, policy.WorkspaceRoleRuleMapping[i].Actions[j].Name)
}
}
if len(rule.Actions) > 0 {
clusterSimpleRules = append(clusterSimpleRules, rule)
}
}
if len(clusterRules) > 0 {
workspaceRules["*"] = clusterSimpleRules
}
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
if workspace != "" && groups[1] != workspace {
continue
}
policyRules := make([]v1.PolicyRule, 0)
for _, rule := range v.Rules {
rule.ResourceNames = nil
policyRules = append(policyRules, rule)
}
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.WorkspaceRoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.WorkspaceRoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < (len(policy.WorkspaceRoleRuleMapping[i].Actions)); j++ {
action := policy.WorkspaceRoleRuleMapping[i].Actions[j]
if RulesMatchesAction(policyRules, action) {
rule.Actions = append(rule.Actions, action.Name)
}
}
if len(rule.Actions) > 0 {
rules = append(rules, rule)
}
}
workspaceRules[groups[1]] = merge(rules, clusterSimpleRules)
}
}
return workspaceRules
}
func merge(clusterRules, rules []models.SimpleRule) []models.SimpleRule {
for _, clusterRule := range clusterRules {
exist := false
for i := 0; i < len(rules); i++ {
if rules[i].Name == clusterRule.Name {
exist = true
for _, action := range clusterRule.Actions {
if !slice.ContainsString(rules[i].Actions, action, nil) {
rules[i].Actions = append(rules[i].Actions, action)
}
}
}
}
if !exist {
rules = append(rules, clusterRule)
}
}
return rules
}
// Convert cluster roles to rules
func GetClusterRoleSimpleRules(clusterRoles []*v1.ClusterRole) ([]models.SimpleRule, error) {
clusterRules := make([]v1.PolicyRule, 0)
for _, v := range clusterRoles {
clusterRules = append(clusterRules, v.Rules...)
}
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ {
validActions := make([]string, 0)
for j := 0; j < (len(policy.ClusterRoleRuleMapping[i].Actions)); j++ {
if RulesMatchesAction(clusterRules, policy.ClusterRoleRuleMapping[i].Actions[j]) {
validActions = append(validActions, policy.ClusterRoleRuleMapping[i].Actions[j].Name)
}
}
if len(validActions) > 0 {
rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions})
}
}
return rules, nil
}
// Convert roles to rules
func GetRoleSimpleRules(roles []*v1.Role, namespace string) (map[string][]models.SimpleRule, error) {
rulesMapping := make(map[string][]models.SimpleRule, 0)
policyRulesMapping := make(map[string][]v1.PolicyRule, 0)
for _, v := range roles {
if namespace != "" && v.Namespace != namespace {
continue
}
policyRules := policyRulesMapping[v.Namespace]
if policyRules == nil {
policyRules = make([]v1.PolicyRule, 0)
}
policyRules = append(policyRules, v.Rules...)
policyRulesMapping[v.Namespace] = policyRules
}
for namespace, policyRules := range policyRulesMapping {
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.RoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ {
if RulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) {
rule.Actions = append(rule.Actions, policy.RoleRuleMapping[i].Actions[j].Name)
}
}
if len(rule.Actions) > 0 {
rules = append(rules, rule)
}
}
rulesMapping[namespace] = rules
}
return rulesMapping, nil
}
func CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
_, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
return err
}
clusterRoles, err := GetClusterRoles(username)
if err != nil {
return err
}
for _, clusterRole := range clusterRoles {
if clusterRole.Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
if clusterRole.Name == clusterRoleName {
return nil
}
clusterRoleBindingName := clusterRole.Annotations["rbac.authorization.k8s.io/clusterrolebinding"]
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBinding, err := clusterRoleBindingLister.Get(clusterRoleBindingName)
if err != nil {
return err
}
for i, v := range clusterRoleBinding.Subjects {
if v.Kind == v1.UserKind && v.Name == username {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects[:i], clusterRoleBinding.Subjects[i+1:]...)
break
}
}
_, err = client.K8sClient().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding)
if err != nil {
return err
}
break
}
}
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return err
}
var clusterRoleBinding *v1.ClusterRoleBinding
for _, roleBinding := range clusterRoleBindings {
if roleBinding.Annotations != nil && roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == clusterRoleName &&
roleBinding.RoleRef.Name == clusterRoleName {
clusterRoleBinding = roleBinding
break
}
}
if clusterRoleBinding != nil {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, v1.Subject{Kind: v1.UserKind, Name: username})
_, err := client.K8sClient().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding)
if err != nil {
return err
}
} else {
clusterRoleBinding = new(v1.ClusterRoleBinding)
clusterRoleBinding.Annotations = map[string]string{"rbac.authorization.k8s.io/clusterrole": clusterRoleName}
clusterRoleBinding.Name = clusterRoleName
clusterRoleBinding.RoleRef = v1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind}
clusterRoleBinding.Subjects = []v1.Subject{{Kind: v1.UserKind, Name: username}}
_, err = client.K8sClient().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
if err != nil {
return err
}
}
return nil
}
func GetRole(namespace string, roleName string) (*v1.Role, error) {
return informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).Get(roleName)
}
func GetClusterRole(clusterRoleName string) (*v1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
return clusterRoleLister.Get(clusterRoleName)
}
func RulesMatchesAction(rules []v1.PolicyRule, action models.Action) bool {
for _, required := range action.Rules {
if !rulesMatchesRequired(rules, required) {
return false
}
}
return true
}
func rulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}

View File

@@ -15,15 +15,40 @@
limitations under the License.
*/
package hpa
package iam
import (
"k8s.io/api/autoscaling/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
import "sync"
"kubesphere.io/kubesphere/pkg/client"
)
func GetHPA(namespace, hpa string) (*v1.HorizontalPodAutoscaler, error) {
return client.K8sClient().AutoscalingV1().HorizontalPodAutoscalers(namespace).Get(hpa, metaV1.GetOptions{})
type Counter struct {
value int
m *sync.Mutex
}
func NewCounter(value int) Counter {
c := Counter{}
c.m = &sync.Mutex{}
c.Set(value)
return c
}
func (c *Counter) Set(value int) {
c.m.Lock()
c.value = value
c.m.Unlock()
}
func (c *Counter) Add(value int) {
c.m.Lock()
c.value += value
c.m.Unlock()
}
func (c *Counter) Sub(value int) {
c.m.Lock()
c.value -= value
c.m.Unlock()
}
func (c *Counter) Get() int {
return c.value
}

View File

@@ -1,3 +1,20 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
@@ -7,38 +24,22 @@ import (
"net/http"
"strings"
v12 "k8s.io/client-go/listers/rbac/v1"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/constants"
ksErr "kubesphere.io/kubesphere/pkg/errors"
kserr "kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
)
const ClusterRoleKind = "ClusterRole"
var (
clusterRoleBindingLister v12.ClusterRoleBindingLister
clusterRoleLister v12.ClusterRoleLister
roleBindingLister v12.RoleBindingLister
roleLister v12.RoleLister
)
func init() {
clusterRoleBindingLister = informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleLister = informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister = informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister = informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
}
// Get user list based on workspace role
func WorkspaceRoleUsers(workspace string, roleName string) ([]User, error) {
func WorkspaceRoleUsers(workspace string, roleName string) ([]models.User, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
@@ -67,11 +68,11 @@ func WorkspaceRoleUsers(workspace string, roleName string) ([]User, error) {
return users, nil
}
func GetUsers(names []string) ([]User, error) {
var users []User
func GetUsers(names []string) ([]models.User, error) {
var users []models.User
if names == nil || len(names) == 0 {
return make([]User, 0), nil
return make([]models.User, 0), nil
}
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users?name=%s", constants.AccountAPIServer, strings.Join(names, ",")))
@@ -88,7 +89,7 @@ func GetUsers(names []string) ([]User, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
err = json.Unmarshal(data, &users)
@@ -100,7 +101,7 @@ func GetUsers(names []string) ([]User, error) {
return users, nil
}
func GetUser(name string) (*User, error) {
func GetUser(name string) (*models.User, error) {
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/users/%s", constants.AccountAPIServer, name))
@@ -116,10 +117,10 @@ func GetUser(name string) (*User, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var user User
var user models.User
err = json.Unmarshal(data, &user)
@@ -187,16 +188,8 @@ func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespac
return false, namespaces, nil
}
func GetRole(namespace string, name string) (*v1.Role, error) {
role, err := roleLister.Roles(namespace).Get(name)
if err != nil {
return nil, err
}
return role.DeepCopy(), nil
}
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
if err != nil {
@@ -213,108 +206,6 @@ func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error)
return users, nil
}
func GetClusterRole(name string) (*v1.ClusterRole, error) {
role, err := clusterRoleLister.Get(name)
if err != nil {
return nil, err
}
return role.DeepCopy(), nil
}
func GetRoles(namespace string, username string) ([]v1.Role, error) {
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]v1.Role, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules}
role.Namespace = roleBinding.Namespace
roles = append(roles, role)
break
} else if apierrors.IsNotFound(err) {
glog.Infoln(err.Error())
break
} else {
return nil, err
}
} else {
if subject.Kind == v1.UserKind && subject.Name == username {
role, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
if err == nil {
roles = append(roles, *role)
break
} else if apierrors.IsNotFound(err) {
glog.Infoln(err.Error())
break
} else {
return nil, err
}
}
}
}
}
}
return roles, nil
}
// Get cluster roles by username
func GetClusterRoles(username string) ([]v1.ClusterRole, error) {
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]v1.ClusterRole, 0)
for _, roleBinding := range clusterRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
role, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
role = role.DeepCopy()
if role.Annotations == nil {
role.Annotations = make(map[string]string, 0)
}
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = roleBinding.Name
if roleBinding.Annotations != nil &&
roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == roleBinding.RoleRef.Name {
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
}
roles = append(roles, *role)
break
} else if apierrors.IsNotFound(err) {
glog.Warning(err)
break
} else {
return nil, err
}
}
}
}
}
return roles, nil
}
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
@@ -323,139 +214,3 @@ func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
}
return false
}
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}

1155
pkg/models/iam/im.go Normal file

File diff suppressed because it is too large Load Diff

76
pkg/models/iam/path.go Normal file
View File

@@ -0,0 +1,76 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package iam
import (
"fmt"
"kubesphere.io/kubesphere/pkg/client"
"regexp"
"strings"
)
func convertDNToPath(dn string) string {
paths := regexp.MustCompile("cn=[a-z0-9]([-a-z0-9]*[a-z0-9])?").FindAllString(dn, -1)
if len(paths) > 1 {
for i := 0; i < len(paths); i++ {
paths[i] = strings.Replace(paths[i], "cn=", "", 1)
}
for i, j := 0, len(paths)-1; i < j; i, j = i+1, j-1 {
paths[i], paths[j] = paths[j], paths[i]
}
return strings.Join(paths, ":")
} else if len(paths) == 1 {
return strings.Replace(paths[0], "cn=", "", -1)
} else {
return ""
}
}
func splitPath(path string) (searchBase string, cn string) {
paths := strings.Split(path, ":")
length := len(paths)
if length > 2 {
cn = paths[length-1]
basePath := paths[:length-1]
for i := 0; i < len(basePath); i++ {
basePath[i] = fmt.Sprintf("cn=%s", basePath[i])
}
for i, j := 0, length-2; i < j; i, j = i+1, j-1 {
basePath[i], basePath[j] = basePath[j], basePath[i]
}
searchBase = fmt.Sprintf("%s,%s", strings.Join(basePath, ","), client.GroupSearchBase)
} else if length == 2 {
searchBase = fmt.Sprintf("cn=%s,%s", paths[0], client.GroupSearchBase)
cn = paths[1]
} else {
searchBase = client.GroupSearchBase
if paths[0] == "" {
cn = "*"
} else {
cn = paths[0]
}
}
return
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
package iam
import (
"k8s.io/api/rbac/v1"
)
type Action struct {
Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Actions []Action `json:"actions"`
}
type SimpleRule struct {
Name string `json:"name"`
Actions []string `json:"actions"`
}
type User struct {
Username string `json:"username"`
Groups []string `json:"groups"`
Password string `json:"password,omitempty"`
AvatarUrl string `json:"avatar_url"`
Description string `json:"description"`
Email string `json:"email"`
LastLoginTime string `json:"last_login_time"`
Status int `json:"status"`
ClusterRole string `json:"cluster_role"`
ClusterRules []SimpleRule `json:"cluster_rules,omitempty"`
Roles map[string]string `json:"roles,omitempty"`
Rules map[string][]SimpleRule `json:"rules,omitempty"`
Role string `json:"role,omitempty"`
WorkspaceRoles map[string]string `json:"workspace_roles,omitempty"`
WorkspaceRole string `json:"workspace_role,omitempty"`
WorkspaceRules map[string][]SimpleRule `json:"workspace_rules,omitempty"`
}

View File

@@ -258,7 +258,7 @@ func CreateKubeConfig(user string) error {
configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: user}, Data: data}
_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap)
if err != nil && !errors.IsAlreadyExists(err) {
glog.Errorf("create user %s's kubeConfig failed, reason: %s", user, err)
glog.Errorf("create user %s's kubeConfig failed, reason: %v", user, err)
return err
}
}
@@ -271,7 +271,7 @@ func GetKubeConfig(user string) (string, error) {
k8sClient := client.K8sClient()
configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metaV1.GetOptions{})
if err != nil {
glog.Errorf("cannot get user %s's kubeConfig, reason: %s", user, err)
glog.Errorf("cannot get user %s's kubeConfig, reason: %v", user, err)
return "", err
}
return configMap.Data[kubectlConfigKey], nil
@@ -287,7 +287,7 @@ func DelKubeConfig(user string) error {
deletePolicy := metaV1.DeletePropagationBackground
err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(user, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
glog.Errorf("delete user %s's kubeConfig failed, reason: %s", user, err)
glog.Errorf("delete user %s's kubeConfig failed, reason: %v", user, err)
return err
}
return nil

View File

@@ -20,6 +20,7 @@ package kubectl
import (
"fmt"
"kubesphere.io/kubesphere/pkg/models"
"math/rand"
"github.com/golang/glog"
@@ -38,18 +39,12 @@ const (
namespace = constants.KubeSphereControlNamespace
)
type PodInfo struct {
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Container string `json:"container"`
}
func GetKubectlPod(username string) (PodInfo, error) {
func GetKubectlPod(username string) (models.PodInfo, error) {
k8sClient := client.K8sClient()
deploy, err := k8sClient.AppsV1beta2().Deployments(namespace).Get(username, metav1.GetOptions{})
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
return models.PodInfo{}, err
}
selectors := deploy.Spec.Selector.MatchLabels
@@ -57,16 +52,16 @@ func GetKubectlPod(username string) (PodInfo, error) {
podList, err := k8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
return models.PodInfo{}, err
}
pod, err := selectCorrectPod(namespace, podList.Items)
if err != nil {
glog.Errorln(err)
return PodInfo{}, err
return models.PodInfo{}, err
}
info := PodInfo{Namespace: pod.Namespace, Pod: pod.Name, Container: pod.Status.ContainerStatuses[0].Name}
info := models.PodInfo{Namespace: pod.Namespace, Pod: pod.Name, Container: pod.Status.ContainerStatuses[0].Name}
return info, nil

View File

@@ -20,14 +20,13 @@ package metrics
import (
"fmt"
"kubesphere.io/kubesphere/pkg/models"
"net/url"
"regexp"
"strings"
"sync"
"time"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/components"
@@ -50,7 +49,6 @@ import (
var (
jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary
nodeStatusDelLabel = []string{"endpoint", "instance", "job", "namespace", "pod", "service"}
nodeLister v12.NodeLister
)
const (
@@ -126,10 +124,6 @@ type OneComponentStatus struct {
Error string `json:"error,omitempty"`
}
func init() {
nodeLister = informers.SharedInformerFactory().Core().V1().Nodes().Lister()
}
func getAllWorkspaceNames(formatedMetric *FormatedMetric) map[string]int {
var wsMap = make(map[string]int)
@@ -197,12 +191,12 @@ func unifyMetricHistoryTimeRange(fmtMetrics *FormatedMetric) {
var timestampMap = make(map[float64]bool)
if fmtMetrics.Data.ResultType == ResultTypeMatrix {
for i, _ := range fmtMetrics.Data.Result {
for i := range fmtMetrics.Data.Result {
values, exist := fmtMetrics.Data.Result[i][ResultItemValues]
if exist {
valueArray, sure := values.([]interface{})
if sure {
for j, _ := range valueArray {
for j := range valueArray {
timeAndValue := valueArray[j].([]interface{})
timestampMap[float64(timeAndValue[0].(uint64))] = true
}
@@ -213,7 +207,7 @@ func unifyMetricHistoryTimeRange(fmtMetrics *FormatedMetric) {
timestampArray := make([]float64, len(timestampMap))
i := 0
for timestamp, _ := range timestampMap {
for timestamp := range timestampMap {
timestampArray[i] = timestamp
i++
}
@@ -230,7 +224,7 @@ func unifyMetricHistoryTimeRange(fmtMetrics *FormatedMetric) {
formatValueArray := make([][]interface{}, len(timestampArray))
j := 0
for k, _ := range timestampArray {
for k := range timestampArray {
valueItem, sure := valueArray[j].([]interface{})
if sure && float64(valueItem[0].(uint64)) == timestampArray[k] {
formatValueArray[k] = []interface{}{int64(timestampArray[k]), valueItem[1]}
@@ -294,7 +288,7 @@ func GetMetric(queryType, params, metricName string) *FormatedMetric {
}
func GetNodeAddressInfo() *map[string][]v1.NodeAddress {
nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister()
nodes, err := nodeLister.List(labels.Everything())
if err != nil {
@@ -938,7 +932,7 @@ func MonitorComponentStatus(monitoringRequest *client.MonitoringRequestParams) *
nsStatus, exist := Components[ns]
if exist {
for _, nsStatusItem := range nsStatus.(map[string]interface{}) {
component := nsStatusItem.(components.Component)
component := nsStatusItem.(models.Component)
namespaceComponentTotalMap[ns] += 1
if component.HealthyBackends != 0 && component.HealthyBackends == component.TotalBackends {
namespaceComponentHealthyMap[ns] += 1

View File

@@ -33,7 +33,6 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/errors"
)
func DrainNode(nodename string) (err error) {
@@ -45,7 +44,7 @@ func DrainNode(nodename string) (err error) {
}
if node.Spec.Unschedulable {
return errors.New(errors.Conflict, fmt.Sprintf("node %s have been drained", nodename))
return fmt.Errorf("node %s have been drained", nodename)
}
data := []byte(" {\"spec\":{\"unschedulable\":true}}")

View File

@@ -23,10 +23,10 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
)
const (
@@ -51,27 +51,17 @@ var (
statefulsetsKey: resources.StatefulSets, persistentvolumeclaimsKey: resources.PersistentVolumeClaims, podsKey: resources.Pods,
namespaceKey: resources.Namespaces, storageClassesKey: resources.StorageClasses, clusterRolesKey: resources.ClusterRoles,
jobsKey: resources.Jobs, cronJobsKey: resources.CronJobs}
resouceQuotaLister v12.ResourceQuotaLister
)
type ResourceQuota struct {
Namespace string `json:"namespace"`
Data v1.ResourceQuotaStatus `json:"data"`
}
func getUsage(namespace, resource string) (int, error) {
list, err := resources.ListNamespaceResource(namespace, resource, "", "", false, -1, 0)
list, err := resources.ListNamespaceResource(namespace, resource, &params.Conditions{}, "", false, -1, 0)
if err != nil {
return 0, err
}
return list.TotalCount, nil
}
func init() {
resouceQuotaLister = informers.SharedInformerFactory().Core().V1().ResourceQuotas().Lister()
}
func GetClusterQuotas() (*ResourceQuota, error) {
func GetClusterQuotas() (*models.ResourceQuota, error) {
quota := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)}
@@ -85,11 +75,11 @@ func GetClusterQuotas() (*ResourceQuota, error) {
quota.Used[v1.ResourceName(k)] = quantity
}
return &ResourceQuota{Namespace: "\"\"", Data: quota}, nil
return &models.ResourceQuota{Namespace: "\"\"", Data: quota}, nil
}
func GetNamespaceQuotas(namespace string) (*ResourceQuota, error) {
func GetNamespaceQuotas(namespace string) (*models.ResourceQuota, error) {
quota, err := getNamespaceResourceQuota(namespace)
if err != nil {
glog.Error(err)
@@ -115,7 +105,7 @@ func GetNamespaceQuotas(namespace string) (*ResourceQuota, error) {
}
}
return &ResourceQuota{Namespace: namespace, Data: *quota}, nil
return &models.ResourceQuota{Namespace: namespace, Data: *quota}, nil
}
func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) {
@@ -135,7 +125,8 @@ func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) {
}
func getNamespaceResourceQuota(namespace string) (*v1.ResourceQuotaStatus, error) {
quotaList, err := resouceQuotaLister.ResourceQuotas(namespace).List(labels.Everything())
resourceQuotaLister := informers.SharedInformerFactory().Core().V1().ResourceQuotas().Lister()
quotaList, err := resourceQuotaLister.ResourceQuotas(namespace).List(labels.Everything())
if err != nil || len(quotaList) == 0 {
return nil, err
}

View File

@@ -20,12 +20,11 @@ package registries
import (
"context"
"encoding/base64"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/golang/glog"
"kubesphere.io/kubesphere/pkg/errors"
)
type AuthInfo struct {
@@ -62,6 +61,6 @@ func RegistryVerify(authInfo AuthInfo) error {
if resp.Status == loginSuccess {
return nil
} else {
return errors.New(errors.VerifyFailed, resp.Status)
return fmt.Errorf(resp.Status)
}
}

View File

@@ -18,19 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/listers/rbac/v1"
)
type clusterRoleSearcher struct {
clusterRoleLister v1.ClusterRoleLister
}
// exactly match
// exactly Match
func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool {
for k, v := range match {
switch k {
@@ -45,7 +45,7 @@ func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRol
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool {
for k, v := range fuzzy {
switch k {
@@ -86,8 +86,8 @@ func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool
}
}
func (s *clusterRoleSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
clusterRoles, err := s.clusterRoleLister.List(labels.Everything())
func (s *clusterRoleSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
clusterRoles, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().List(labels.Everything())
if err != nil {
return nil, err
@@ -95,11 +95,11 @@ func (s *clusterRoleSearcher) search(conditions *conditions, orderBy string, rev
result := make([]*rbac.ClusterRole, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = clusterRoles
} else {
for _, item := range clusterRoles {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type configMapSearcher struct {
configMapLister lister.ConfigMapLister
}
// exactly match
// exactly Match
func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool {
for k, v := range match {
switch k {
@@ -46,7 +45,7 @@ func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) boo
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool {
for k, v := range fuzzy {
switch k {
@@ -91,8 +90,8 @@ func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool {
}
}
func (s *configMapSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
configMaps, err := s.configMapLister.ConfigMaps(namespace).List(labels.Everything())
func (s *configMapSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
configMaps, err := informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -100,11 +99,11 @@ func (s *configMapSearcher) search(namespace string, conditions *conditions, ord
result := make([]*v1.ConfigMap, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = configMaps
} else {
for _, item := range configMaps {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,28 +18,28 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/batch/v2alpha1"
"k8s.io/api/batch/v1beta1"
"k8s.io/api/batch/v2alpha1"
"k8s.io/apimachinery/pkg/labels"
)
type cronJobSearcher struct {
cronJobLister lister.CronJobLister
}
func cronJobStatus(item *v2alpha1.CronJob) string {
func cronJobStatus(item *v1beta1.CronJob) string {
if item.Spec.Suspend != nil && *item.Spec.Suspend {
return paused
}
return running
}
// Exactly match
func (*cronJobSearcher) match(match map[string]string, item *v2alpha1.CronJob) bool {
// Exactly Match
func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool {
for k, v := range match {
switch k {
case status:
@@ -53,7 +53,7 @@ func (*cronJobSearcher) match(match map[string]string, item *v2alpha1.CronJob) b
return true
}
func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v2alpha1.CronJob) bool {
func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bool {
for k, v := range fuzzy {
switch k {
@@ -88,7 +88,7 @@ func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v2alpha1.CronJob) b
return true
}
func (*cronJobSearcher) compare(a, b *v2alpha1.CronJob, orderBy string) bool {
func (*cronJobSearcher) compare(a, b *v1beta1.CronJob, orderBy string) bool {
switch orderBy {
case createTime:
return a.CreationTimestamp.Time.After(b.CreationTimestamp.Time)
@@ -99,20 +99,20 @@ func (*cronJobSearcher) compare(a, b *v2alpha1.CronJob, orderBy string) bool {
}
}
func (s *cronJobSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
cronJobs, err := s.cronJobLister.CronJobs(namespace).List(labels.Everything())
func (s *cronJobSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
cronJobs, err := informers.SharedInformerFactory().Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*v2alpha1.CronJob, 0)
result := make([]*v1beta1.CronJob, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = cronJobs
} else {
for _, item := range cronJobs {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,17 +18,16 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
)
type daemonSetSearcher struct {
daemonSetLister lister.DaemonSetLister
}
func daemonSetStatus(item *v1.DaemonSet) string {
@@ -41,7 +40,7 @@ func daemonSetStatus(item *v1.DaemonSet) string {
}
}
// Exactly match
// Exactly Match
func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool {
for k, v := range match {
switch k {
@@ -102,8 +101,8 @@ func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool {
}
}
func (s *daemonSetSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
daemonSets, err := s.daemonSetLister.DaemonSets(namespace).List(labels.Everything())
func (s *daemonSetSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
daemonSets, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -111,11 +110,11 @@ func (s *daemonSetSearcher) search(namespace string, conditions *conditions, ord
result := make([]*v1.DaemonSet, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = daemonSets
} else {
for _, item := range daemonSets {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,18 +18,17 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/api/apps/v1"
)
type deploymentSearcher struct {
deploymentLister lister.DeploymentLister
}
func deploymentStatus(item *v1.Deployment) string {
@@ -45,7 +44,7 @@ func deploymentStatus(item *v1.Deployment) string {
return stopped
}
// Exactly match
// Exactly Match
func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) bool {
for k, v := range match {
switch k {
@@ -106,8 +105,8 @@ func (*deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool {
}
}
func (s *deploymentSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
deployments, err := s.deploymentLister.Deployments(namespace).List(labels.Everything())
func (s *deploymentSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
deployments, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -115,11 +114,11 @@ func (s *deploymentSearcher) search(namespace string, conditions *conditions, or
result := make([]*v1.Deployment, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = deployments
} else {
for _, item := range deployments {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,21 +18,20 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/extensions/v1beta1"
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/labels"
)
type ingressSearcher struct {
ingressLister lister.IngressLister
}
// exactly match
// exactly Match
func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress) bool {
for k, v := range match {
switch k {
@@ -47,7 +46,7 @@ func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress)
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress) bool {
for k, v := range fuzzy {
switch k {
@@ -92,8 +91,8 @@ func (*ingressSearcher) compare(a, b *extensions.Ingress, orderBy string) bool {
}
}
func (s *ingressSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
ingresses, err := s.ingressLister.Ingresses(namespace).List(labels.Everything())
func (s *ingressSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
ingresses, err := informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -101,11 +100,11 @@ func (s *ingressSearcher) search(namespace string, conditions *conditions, order
result := make([]*extensions.Ingress, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = ingresses
} else {
for _, item := range ingresses {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,37 +18,36 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
"time"
lister "k8s.io/client-go/listers/batch/v1"
batchV1 "k8s.io/api/batch/v1"
coreV1 "k8s.io/api/core/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type jobSearcher struct {
jobLister lister.JobLister
}
func jobStatus(item *batchV1.Job) string {
func jobStatus(item *batchv1.Job) string {
status := ""
for _, condition := range item.Status.Conditions {
if condition.Type == batchV1.JobFailed && condition.Status == coreV1.ConditionTrue {
if condition.Type == batchv1.JobFailed && condition.Status == corev1.ConditionTrue {
status = failed
}
if condition.Type == batchV1.JobComplete && condition.Status == coreV1.ConditionTrue {
if condition.Type == batchv1.JobComplete && condition.Status == corev1.ConditionTrue {
status = complete
}
}
return status
}
// Exactly match
func (*jobSearcher) match(match map[string]string, item *batchV1.Job) bool {
// Exactly Match
func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool {
for k, v := range match {
switch k {
case status:
@@ -62,7 +61,7 @@ func (*jobSearcher) match(match map[string]string, item *batchV1.Job) bool {
return true
}
func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchV1.Job) bool {
func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool {
for k, v := range fuzzy {
switch k {
@@ -97,7 +96,7 @@ func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchV1.Job) bool {
return true
}
func jobUpdateTime(item *batchV1.Job) time.Time {
func jobUpdateTime(item *batchv1.Job) time.Time {
updateTime := item.CreationTimestamp.Time
for _, condition := range item.Status.Conditions {
if updateTime.Before(condition.LastProbeTime.Time) {
@@ -110,7 +109,7 @@ func jobUpdateTime(item *batchV1.Job) time.Time {
return updateTime
}
func (*jobSearcher) compare(a, b *batchV1.Job, orderBy string) bool {
func (*jobSearcher) compare(a, b *batchv1.Job, orderBy string) bool {
switch orderBy {
case updateTime:
return jobUpdateTime(a).After(jobUpdateTime(b))
@@ -121,20 +120,20 @@ func (*jobSearcher) compare(a, b *batchV1.Job, orderBy string) bool {
}
}
func (s *jobSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
jobs, err := s.jobLister.Jobs(namespace).List(labels.Everything())
func (s *jobSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
jobs, err := informers.SharedInformerFactory().Batch().V1().Jobs().Lister().Jobs(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
result := make([]*batchV1.Job, 0)
result := make([]*batchv1.Job, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = jobs
} else {
for _, item := range jobs {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,19 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
)
type namespaceSearcher struct {
namespaceLister lister.NamespaceLister
}
// exactly match
// exactly Match
func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool {
for k, v := range match {
switch k {
@@ -45,7 +45,7 @@ func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) boo
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool {
for k, v := range fuzzy {
switch k {
@@ -90,8 +90,8 @@ func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool {
}
}
func (s *namespaceSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
namespaces, err := s.namespaceLister.List(labels.Everything())
func (s *namespaceSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
namespaces, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything())
if err != nil {
return nil, err
@@ -99,11 +99,11 @@ func (s *namespaceSearcher) search(conditions *conditions, orderBy string, rever
result := make([]*v1.Namespace, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = namespaces
} else {
for _, item := range namespaces {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,19 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
)
type nodeSearcher struct {
nodeLister lister.NodeLister
}
// exactly match
// exactly Match
func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
for k, v := range match {
switch k {
@@ -45,7 +45,7 @@ func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool {
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool {
for k, v := range fuzzy {
switch k {
@@ -90,8 +90,8 @@ func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool {
}
}
func (s *nodeSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
nodes, err := s.nodeLister.List(labels.Everything())
func (s *nodeSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
nodes, err := informers.SharedInformerFactory().Core().V1().Nodes().Lister().List(labels.Everything())
if err != nil {
return nil, err
@@ -99,11 +99,11 @@ func (s *nodeSearcher) search(conditions *conditions, orderBy string, reverse bo
result := make([]*v1.Node, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = nodes
} else {
for _, item := range nodes {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type persistentVolumeClaimSearcher struct {
persistentVolumeClaimLister lister.PersistentVolumeClaimLister
}
// exactly match
// exactly Match
func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool {
for k, v := range match {
switch k {
@@ -46,7 +45,7 @@ func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.Pe
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.PersistentVolumeClaim) bool {
for k, v := range fuzzy {
switch k {
@@ -91,8 +90,8 @@ func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, or
}
}
func (s *persistentVolumeClaimSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
persistentVolumeClaims, err := s.persistentVolumeClaimLister.PersistentVolumeClaims(namespace).List(labels.Everything())
func (s *persistentVolumeClaimSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
persistentVolumeClaims, err := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -100,11 +99,11 @@ func (s *persistentVolumeClaimSearcher) search(namespace string, conditions *con
result := make([]*v1.PersistentVolumeClaim, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = persistentVolumeClaims
} else {
for _, item := range persistentVolumeClaims {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
v12 "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type podSearcher struct {
podLister v12.PodLister
}
// exactly match
// exactly Match
func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
for k, v := range match {
switch k {
@@ -46,7 +45,7 @@ func (*podSearcher) match(match map[string]string, item *v1.Pod) bool {
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool {
for k, v := range fuzzy {
switch k {
@@ -91,9 +90,9 @@ func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool {
}
}
func (s *podSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
func (s *podSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
pods, err := s.podLister.Pods(namespace).List(labels.Everything())
pods, err := informers.SharedInformerFactory().Core().V1().Pods().Lister().Pods(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -101,11 +100,11 @@ func (s *podSearcher) search(namespace string, conditions *conditions, orderBy s
result := make([]*v1.Pod, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = pods
} else {
for _, item := range pods {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,74 +18,35 @@
package resources
import (
"regexp"
"fmt"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/params"
"strings"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/errors"
)
func init() {
namespacedResources[ConfigMaps] = &configMapSearcher{
configMapLister: informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister(),
}
namespacedResources[CronJobs] = &cronJobSearcher{
cronJobLister: informers.SharedInformerFactory().Batch().V2alpha1().CronJobs().Lister(),
}
namespacedResources[DaemonSets] = &daemonSetSearcher{
daemonSetLister: informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister(),
}
namespacedResources[Deployments] = &deploymentSearcher{
deploymentLister: informers.SharedInformerFactory().Apps().V1().Deployments().Lister(),
}
namespacedResources[Ingresses] = &ingressSearcher{
ingressLister: informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister(),
}
namespacedResources[Jobs] = &jobSearcher{
jobLister: informers.SharedInformerFactory().Batch().V1().Jobs().Lister(),
}
namespacedResources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{
persistentVolumeClaimLister: informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister(),
}
namespacedResources[Secrets] = &secretSearcher{
secretLister: informers.SharedInformerFactory().Core().V1().Secrets().Lister(),
}
namespacedResources[Services] = &serviceSearcher{
serviceLister: informers.SharedInformerFactory().Core().V1().Services().Lister(),
}
namespacedResources[StatefulSets] = &statefulSetSearcher{
statefulSetLister: informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister(),
}
namespacedResources[Pods] = &podSearcher{
podLister: informers.SharedInformerFactory().Core().V1().Pods().Lister(),
}
namespacedResources[Roles] = &roleSearcher{
roleLister: informers.SharedInformerFactory().Rbac().V1().Roles().Lister(),
}
namespacedResources[ConfigMaps] = &configMapSearcher{}
namespacedResources[CronJobs] = &cronJobSearcher{}
namespacedResources[DaemonSets] = &daemonSetSearcher{}
namespacedResources[Deployments] = &deploymentSearcher{}
namespacedResources[Ingresses] = &ingressSearcher{}
namespacedResources[Jobs] = &jobSearcher{}
namespacedResources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{}
namespacedResources[Secrets] = &secretSearcher{}
namespacedResources[Services] = &serviceSearcher{}
namespacedResources[StatefulSets] = &statefulSetSearcher{}
namespacedResources[Pods] = &podSearcher{}
namespacedResources[Roles] = &roleSearcher{}
clusterResources[Nodes] = &nodeSearcher{
nodeLister: informers.SharedInformerFactory().Core().V1().Nodes().Lister(),
}
clusterResources[Namespaces] = &namespaceSearcher{
namespaceLister: informers.SharedInformerFactory().Core().V1().Namespaces().Lister(),
}
clusterResources[ClusterRoles] = &clusterRoleSearcher{
clusterRoleLister: informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister(),
}
clusterResources[StorageClasses] = &storageClassesSearcher{
storageClassesLister: informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister(),
}
clusterResources[Nodes] = &nodeSearcher{}
clusterResources[Namespaces] = &namespaceSearcher{}
clusterResources[ClusterRoles] = &clusterRoleSearcher{}
clusterResources[StorageClasses] = &storageClassesSearcher{}
}
var namespacedResources = make(map[string]namespacedSearcherInterface)
var clusterResources = make(map[string]clusterSearcherInterface)
type conditions struct {
match map[string]string
fuzzy map[string]string
}
const (
name = "name"
label = "label"
@@ -123,33 +84,26 @@ const (
)
type namespacedSearcherInterface interface {
search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error)
search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
}
type clusterSearcherInterface interface {
search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error)
search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error)
}
func ListNamespaceResource(namespace, resource, conditionStr, orderBy string, reverse bool, limit, offset int) (*ResourceList, error) {
func ListNamespaceResource(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
total := 0
var err error
conditions, err := parseToConditions(conditionStr)
if err != nil {
return nil, err
}
var result []interface{}
if searcher, ok := namespacedResources[resource]; ok {
result, err = searcher.search(namespace, conditions, orderBy, reverse)
} else {
return nil, errors.New(errors.NotImplement, "not support")
return nil, fmt.Errorf("not support")
}
if err != nil {
return nil, errors.New(errors.Internal, err.Error())
return nil, err
}
total = len(result)
@@ -160,16 +114,14 @@ func ListNamespaceResource(namespace, resource, conditionStr, orderBy string, re
}
}
return &ResourceList{TotalCount: total, Items: items}, nil
return &models.PageableResponse{TotalCount: total, Items: items}, nil
}
func ListClusterResource(resource, conditionStr, orderBy string, reverse bool, limit, offset int) (*ResourceList, error) {
func ListClusterResource(resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
items := make([]interface{}, 0)
total := 0
var err error
conditions, err := parseToConditions(conditionStr)
if err != nil {
return nil, err
}
@@ -179,11 +131,11 @@ func ListClusterResource(resource, conditionStr, orderBy string, reverse bool, l
if searcher, ok := clusterResources[resource]; ok {
result, err = searcher.search(conditions, orderBy, reverse)
} else {
return nil, errors.New(errors.NotImplement, "not support")
return nil, fmt.Errorf("not support")
}
if err != nil {
return nil, errors.New(errors.Internal, err.Error())
return nil, err
}
total = len(result)
@@ -194,36 +146,7 @@ func ListClusterResource(resource, conditionStr, orderBy string, reverse bool, l
}
}
return &ResourceList{TotalCount: total, Items: items}, nil
}
func parseToConditions(str string) (*conditions, error) {
conditions := &conditions{match: make(map[string]string, 0), fuzzy: make(map[string]string, 0)}
if str == "" {
return conditions, nil
}
for _, item := range strings.Split(str, ",") {
if strings.Count(item, "=") > 1 || strings.Count(item, "~") > 1 {
return nil, errors.New(errors.InvalidArgument, "invalid condition")
}
if groups := regexp.MustCompile(`(\S+)([=~])(\S+)`).FindStringSubmatch(item); len(groups) == 4 {
if groups[2] == "=" {
conditions.match[groups[1]] = groups[3]
} else {
conditions.fuzzy[groups[1]] = groups[3]
}
} else {
return nil, errors.New(errors.InvalidArgument, "invalid condition")
}
}
return conditions, nil
}
type ResourceList struct {
TotalCount int `json:"total_count"`
Items []interface{} `json:"items"`
return &models.PageableResponse{TotalCount: total, Items: items}, nil
}
func searchFuzzy(m map[string]string, key, value string) bool {

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
rbac "k8s.io/api/rbac/v1"
lister "k8s.io/client-go/listers/rbac/v1"
"k8s.io/apimachinery/pkg/labels"
)
type roleSearcher struct {
roleLister lister.RoleLister
}
// exactly match
// exactly Match
func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
for k, v := range match {
switch k {
@@ -46,7 +45,7 @@ func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool {
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool {
for k, v := range fuzzy {
switch k {
@@ -87,8 +86,8 @@ func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool {
}
}
func (s *roleSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
roles, err := s.roleLister.Roles(namespace).List(labels.Everything())
func (s *roleSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
roles, err := informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -96,11 +95,11 @@ func (s *roleSearcher) search(namespace string, conditions *conditions, orderBy
result := make([]*rbac.Role, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = roles
} else {
for _, item := range roles {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type secretSearcher struct {
secretLister lister.SecretLister
}
// exactly match
// exactly Match
func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool {
for k, v := range match {
switch k {
@@ -50,7 +49,7 @@ func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool {
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool {
for k, v := range fuzzy {
switch k {
@@ -95,8 +94,8 @@ func (*secretSearcher) compare(a, b *v1.Secret, orderBy string) bool {
}
}
func (s *secretSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
secrets, err := s.secretLister.Secrets(namespace).List(labels.Everything())
func (s *secretSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
secrets, err := informers.SharedInformerFactory().Core().V1().Secrets().Lister().Secrets(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -104,11 +103,11 @@ func (s *secretSearcher) search(namespace string, conditions *conditions, orderB
result := make([]*v1.Secret, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = secrets
} else {
for _, item := range secrets {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,20 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
)
type serviceSearcher struct {
serviceLister lister.ServiceLister
}
// exactly match
// exactly Match
func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool {
for k, v := range match {
switch k {
@@ -46,7 +45,7 @@ func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool {
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool {
for k, v := range fuzzy {
switch k {
@@ -91,8 +90,8 @@ func (*serviceSearcher) compare(a, b *v1.Service, orderBy string) bool {
}
}
func (s *serviceSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
services, err := s.serviceLister.Services(namespace).List(labels.Everything())
func (s *serviceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
services, err := informers.SharedInformerFactory().Core().V1().Services().Lister().Services(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -100,11 +99,11 @@ func (s *serviceSearcher) search(namespace string, conditions *conditions, order
result := make([]*v1.Service, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = services
} else {
for _, item := range services {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,17 +18,16 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
lister "k8s.io/client-go/listers/apps/v1"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
)
type statefulSetSearcher struct {
statefulSetLister lister.StatefulSetLister
}
func statefulSetStatus(item *v1.StatefulSet) string {
@@ -44,7 +43,7 @@ func statefulSetStatus(item *v1.StatefulSet) string {
return stopped
}
// Exactly match
// Exactly Match
func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) bool {
for k, v := range match {
switch k {
@@ -105,8 +104,8 @@ func (*statefulSetSearcher) compare(a, b *v1.StatefulSet, orderBy string) bool {
}
}
func (s *statefulSetSearcher) search(namespace string, conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
statefulSets, err := s.statefulSetLister.StatefulSets(namespace).List(labels.Everything())
func (s *statefulSetSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
statefulSets, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).List(labels.Everything())
if err != nil {
return nil, err
@@ -114,11 +113,11 @@ func (s *statefulSetSearcher) search(namespace string, conditions *conditions, o
result := make([]*v1.StatefulSet, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = statefulSets
} else {
for _, item := range statefulSets {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -18,19 +18,19 @@
package resources
import (
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"sort"
"strings"
"k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/storage/v1"
)
type storageClassesSearcher struct {
storageClassesLister lister.StorageClassLister
}
// exactly match
// exactly Match
func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool {
for k, v := range match {
switch k {
@@ -45,7 +45,7 @@ func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageCl
return true
}
// fuzzy searchInNamespace
// Fuzzy searchInNamespace
func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageClass) bool {
for k, v := range fuzzy {
switch k {
@@ -86,8 +86,8 @@ func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bo
}
}
func (s *storageClassesSearcher) search(conditions *conditions, orderBy string, reverse bool) ([]interface{}, error) {
storageClasses, err := s.storageClassesLister.List(labels.Everything())
func (s *storageClassesSearcher) search(conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) {
storageClasses, err := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().List(labels.Everything())
if err != nil {
return nil, err
@@ -95,11 +95,11 @@ func (s *storageClassesSearcher) search(conditions *conditions, orderBy string,
result := make([]*v1.StorageClass, 0)
if len(conditions.match) == 0 && len(conditions.fuzzy) == 0 {
if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 {
result = storageClasses
} else {
for _, item := range storageClasses {
if s.match(conditions.match, item) && s.fuzzy(conditions.fuzzy, item) {
if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) {
result = append(result, item)
}
}

View File

@@ -24,29 +24,11 @@ import (
"github.com/golang/glog"
"k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/apps/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/informers"
)
var (
daemonSetLister lister.DaemonSetLister
deploymentLister lister.DeploymentLister
replicaSetLister lister.ReplicaSetLister
statefulSetLister lister.StatefulSetLister
controllerRevisionLister lister.ControllerRevisionLister
)
func init() {
daemonSetLister = informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister()
deploymentLister = informers.SharedInformerFactory().Apps().V1().Deployments().Lister()
replicaSetLister = informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister()
statefulSetLister = informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister()
controllerRevisionLister = informers.SharedInformerFactory().Apps().V1().ControllerRevisions().Lister()
}
func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error) {
deploymentLister := informers.SharedInformerFactory().Apps().V1().Deployments().Lister()
deploy, err := deploymentLister.Deployments(namespace).Get(name)
if err != nil {
glog.Errorf("get deployment %s failed, reason: %s", name, err)
@@ -56,6 +38,7 @@ func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error)
labelMap := deploy.Spec.Template.Labels
labelSelector := labels.Set(labelMap).AsSelector()
replicaSetLister := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister()
rsList, err := replicaSetLister.ReplicaSets(namespace).List(labelSelector)
if err != nil {
return nil, err
@@ -67,11 +50,11 @@ func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error)
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("revision not found %v#%v", name, revision))
return nil, fmt.Errorf("revision not found %v#%v", name, revision)
}
func GetDaemonSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) {
daemonSetLister := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister()
ds, err := daemonSetLister.DaemonSets(namespace).Get(name)
if err != nil {
@@ -84,7 +67,7 @@ func GetDaemonSetRevision(namespace, name string, revisionInt int) (*v1.Controll
}
func GetStatefulSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) {
statefulSetLister := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister()
st, err := statefulSetLister.StatefulSets(namespace).Get(name)
if err != nil {
@@ -97,7 +80,7 @@ func GetStatefulSetRevision(namespace, name string, revisionInt int) (*v1.Contro
func getControllerRevision(namespace, name string, labelMap map[string]string, revision int) (*v1.ControllerRevision, error) {
labelSelector := labels.Set(labelMap).AsSelector()
controllerRevisionLister := informers.SharedInformerFactory().Apps().V1().ControllerRevisions().Lister()
revisions, err := controllerRevisionLister.ControllerRevisions(namespace).List(labelSelector)
if err != nil {
@@ -110,5 +93,5 @@ func getControllerRevision(namespace, name string, labelMap map[string]string, r
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("revision not found %v#%v", name, revision))
return nil, fmt.Errorf("revision not found %v#%v", name, revision)
}

View File

@@ -23,15 +23,12 @@ import (
"io/ioutil"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/informers"
"github.com/golang/glog"
coreV1 "k8s.io/api/core/v1"
extensionsV1beta1 "k8s.io/api/extensions/v1beta1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/api/rbac/v1"
@@ -43,18 +40,10 @@ import (
"kubesphere.io/kubesphere/pkg/models/iam"
)
var (
serviceLister v12.ServiceLister
)
func init() {
serviceLister = informers.SharedInformerFactory().Core().V1().Services().Lister()
}
func GetAllRouters() ([]*coreV1.Service, error) {
func GetAllRouters() ([]*corev1.Service, error) {
selector := labels.SelectorFromSet(labels.Set{"app": "kubesphere", "component": "ks-router", "tier": "backend"})
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
services, err := serviceLister.Services(constants.IngressControllerNamespace).List(selector)
if err != nil {
@@ -65,7 +54,7 @@ func GetAllRouters() ([]*coreV1.Service, error) {
return services, nil
}
func GetAllRoutersOfUser(username string) ([]*coreV1.Service, error) {
func GetAllRoutersOfUser(username string) ([]*corev1.Service, error) {
allNamespace, namespaces, err := iam.GetUserNamespaces(username, v1.PolicyRule{
Verbs: []string{"get", "list"},
APIGroups: []string{""},
@@ -82,7 +71,7 @@ func GetAllRoutersOfUser(username string) ([]*coreV1.Service, error) {
return GetAllRouters()
}
routers := make([]*coreV1.Service, 0)
routers := make([]*corev1.Service, 0)
for _, namespace := range namespaces {
router, err := GetRouter(namespace)
@@ -99,11 +88,11 @@ func GetAllRoutersOfUser(username string) ([]*coreV1.Service, error) {
}
// Get router from a namespace
func GetRouter(namespace string) (*coreV1.Service, error) {
func GetRouter(namespace string) (*corev1.Service, error) {
serviceName := constants.IngressControllerPrefix + namespace
selector := labels.SelectorFromSet(labels.Set{"app": "kubesphere", "component": "ks-router", "tier": "backend", "project": namespace})
serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister()
services, err := serviceLister.Services(constants.IngressControllerNamespace).List(selector)
if err != nil {
@@ -116,7 +105,7 @@ func GetRouter(namespace string) (*coreV1.Service, error) {
}
}
return nil, errors.New(errors.NotFound, fmt.Sprintf("resources not found %s", serviceName))
return nil, fmt.Errorf("resources not found %s", serviceName)
}
// Load all resource yamls
@@ -148,11 +137,11 @@ func LoadYamls() ([]string, error) {
}
// Create a ingress controller in a namespace
func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) {
func CreateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
k8sClient := client.K8sClient()
var router *coreV1.Service
var router *corev1.Service
yamls, err := LoadYamls()
@@ -170,8 +159,8 @@ func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations m
}
switch obj.(type) {
case *coreV1.Service:
service := obj.(*coreV1.Service)
case *corev1.Service:
service := obj.(*corev1.Service)
service.SetAnnotations(annotations)
service.Spec.Type = routerType
@@ -190,8 +179,8 @@ func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations m
router = service
case *extensionsV1beta1.Deployment:
deployment := obj.(*extensionsV1beta1.Deployment)
case *extensionsv1beta1.Deployment:
deployment := obj.(*extensionsv1beta1.Deployment)
deployment.Name = constants.IngressControllerPrefix + namespace
// Add project label
@@ -204,7 +193,7 @@ func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations m
// Choose self as master
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--election-id="+deployment.Name)
if routerType == coreV1.ServiceTypeLoadBalancer {
if routerType == corev1.ServiceTypeLoadBalancer {
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--publish-service="+constants.IngressControllerNamespace+"/"+constants.IngressControllerPrefix+namespace)
} else {
deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--report-node-internal-ip-address")
@@ -224,11 +213,11 @@ func CreateRouter(namespace string, routerType coreV1.ServiceType, annotations m
// DeleteRouter is used to delete ingress controller related resources in namespace
// It will not delete ClusterRole resource cause it maybe used by other controllers
func DeleteRouter(namespace string) (*coreV1.Service, error) {
func DeleteRouter(namespace string) (*corev1.Service, error) {
k8sClient := client.K8sClient()
var err error
var router *coreV1.Service
var router *corev1.Service
if err != nil {
glog.Error(err)
@@ -236,9 +225,9 @@ func DeleteRouter(namespace string) (*coreV1.Service, error) {
// delete controller service
serviceName := constants.IngressControllerPrefix + namespace
deleteOptions := metaV1.DeleteOptions{}
deleteOptions := meta_v1.DeleteOptions{}
listOptions := metaV1.ListOptions{
listOptions := meta_v1.ListOptions{
LabelSelector: "app=kubesphere,component=ks-router,tier=backend,project=" + namespace,
FieldSelector: "metadata.name=" + serviceName}
@@ -259,7 +248,7 @@ func DeleteRouter(namespace string) (*coreV1.Service, error) {
// delete controller deployment
deploymentName := constants.IngressControllerPrefix + namespace
listOptions = metaV1.ListOptions{
listOptions = meta_v1.ListOptions{
LabelSelector: "app=kubesphere,component=ks-router,tier=backend,project=" + namespace,
}
deployments, err := k8sClient.ExtensionsV1beta1().Deployments(constants.IngressControllerNamespace).List(listOptions)
@@ -279,10 +268,10 @@ func DeleteRouter(namespace string) (*coreV1.Service, error) {
}
// Update Ingress Controller Service, change type from NodePort to Loadbalancer or vice versa.
func UpdateRouter(namespace string, routerType coreV1.ServiceType, annotations map[string]string) (*coreV1.Service, error) {
func UpdateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) {
k8sClient := client.K8sClient()
var router *coreV1.Service
var router *corev1.Service
router, err := GetRouter(namespace)
@@ -293,7 +282,7 @@ func UpdateRouter(namespace string, routerType coreV1.ServiceType, annotations m
if router == nil {
glog.Error("Trying to update a non-existed router")
return nil, errors.New(errors.Internal, "router not created yet")
return nil, fmt.Errorf("router not created yet")
}
// from LoadBalancer to NodePort, or vice-versa

View File

@@ -18,7 +18,8 @@
package status
import (
"fmt"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/models/resources"
)
@@ -31,7 +32,7 @@ type workLoadStatus struct {
func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
res := workLoadStatus{Count: make(map[string]int), Namespace: namespace, Items: make(map[string]interface{})}
var notReadyList *resources.ResourceList
var notReadyList *models.PageableResponse
var err error
for _, resource := range []string{resources.Deployments, resources.StatefulSets, resources.DaemonSets, resources.PersistentVolumeClaims} {
notReadyStatus := "updating"
@@ -39,7 +40,7 @@ func GetNamespacesResourceStatus(namespace string) (*workLoadStatus, error) {
notReadyStatus = "pending"
}
notReadyList, err = resources.ListNamespaceResource(namespace, resource, fmt.Sprintf("status=%s", notReadyStatus), "", false, -1, 0)
notReadyList, err = resources.ListNamespaceResource(namespace, resource, &params.Conditions{Match: map[string]string{"status": notReadyStatus}}, "", false, -1, 0)
if err != nil {
return nil, err

View File

@@ -24,9 +24,6 @@ import (
storageV1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/labels"
lister "k8s.io/client-go/listers/core/v1"
lister2 "k8s.io/client-go/listers/storage/v1"
"kubesphere.io/kubesphere/pkg/informers"
)
@@ -36,18 +33,12 @@ type ScMetrics struct {
PvcNumber string `json:"pvcNumber"`
}
var (
persistentVolumeClaimLister lister.PersistentVolumeClaimLister
persistentVolumeLister lister.PersistentVolumeLister
sotrageClassesLister lister2.StorageClassLister
)
func init() {
persistentVolumeClaimLister = informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister()
persistentVolumeLister = informers.SharedInformerFactory().Core().V1().PersistentVolumes().Lister()
}
func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) {
persistentVolumeClaimLister := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister()
all, err := persistentVolumeClaimLister.List(labels.Everything())
if err != nil {
@@ -70,6 +61,7 @@ func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) {
// Get info of metrics
func GetScMetrics(scName string) (*ScMetrics, error) {
persistentVolumeLister := informers.SharedInformerFactory().Core().V1().PersistentVolumes().Lister()
pvList, err := persistentVolumeLister.List(labels.Everything())
if err != nil {
return nil, err
@@ -102,7 +94,7 @@ func GetScMetrics(scName string) (*ScMetrics, error) {
func GetScList() ([]*storageV1.StorageClass, error) {
// Get StorageClass list
scList, err := sotrageClassesLister.List(labels.Everything())
scList, err := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().List(labels.Everything())
if err != nil {
return nil, err

View File

@@ -20,19 +20,12 @@ package storage
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
v12 "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/informers"
)
var podLister v12.PodLister
func init() {
podLister = informers.SharedInformerFactory().Core().V1().Pods().Lister()
}
// List pods of a specific persistent volume claims
func GetPodListByPvc(pvc string, ns string) (res []*v1.Pod, err error) {
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
podList, err := podLister.Pods(ns).List(labels.Everything())
if err != nil {
return nil, err

View File

@@ -17,11 +17,121 @@
*/
package models
type MessageResponse struct {
Message string `json:"message"`
}
import (
v12 "k8s.io/api/core/v1"
"time"
"k8s.io/api/rbac/v1"
)
type PageableResponse struct {
Items []interface{} `json:"items"`
TotalCount int `json:"total_count"`
}
type Workspace struct {
Group `json:",inline"`
Admin string `json:"admin,omitempty"`
Namespaces []string `json:"namespaces"`
DevopsProjects []string `json:"devops_projects"`
}
type UserInvite struct {
Username string `json:"username"`
Role string `json:"role"`
}
func (g Group) GetCreateTime() (time.Time, error) {
return time.Parse("2006-01-02T15:04:05Z", g.CreateTime)
}
type WorkspaceDPBinding struct {
Workspace string `gorm:"primary_key"`
DevOpsProject string `gorm:"primary_key"`
}
type DevopsProject struct {
ProjectId *string `json:"project_id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Creator string `json:"creator"`
CreateTime *time.Time `json:"create_time,omitempty"`
Status *string `json:"status"`
Visibility *string `json:"visibility,omitempty"`
}
type Action struct {
Name string `json:"name"`
Rules []v1.PolicyRule `json:"rules"`
}
type Rule struct {
Name string `json:"name"`
Actions []Action `json:"actions"`
}
type SimpleRule struct {
Name string `json:"name"`
Actions []string `json:"actions"`
}
type User struct {
Username string `json:"username"`
//UID string `json:"uid"`
Groups []string `json:"groups,omitempty"`
Password string `json:"password,omitempty"`
CurrentPassword string `json:"current_password,omitempty"`
//Extra map[string]interface{} `json:"extra"`
AvatarUrl string `json:"avatar_url"`
Description string `json:"description"`
Email string `json:"email"`
LastLoginTime string `json:"last_login_time"`
Status int `json:"status"`
ClusterRole string `json:"cluster_role"`
ClusterRules []SimpleRule `json:"cluster_rules"`
Roles map[string]string `json:"roles,omitempty"`
Rules map[string][]SimpleRule `json:"rules,omitempty"`
Role string `json:"role,omitempty"`
RoleBinding string `json:"role_binding,omitempty"`
Lang string `json:"lang,omitempty"`
WorkspaceRoles map[string]string `json:"workspace_roles,omitempty"`
WorkspaceRole string `json:"workspace_role,omitempty"`
WorkspaceRules map[string][]SimpleRule `json:"workspace_rules,omitempty"`
}
type Group struct {
Path string `json:"path"`
Name string `json:"name"`
Gid string `json:"gid"`
Members []string `json:"members"`
Logo string `json:"logo"`
Creator string `json:"creator"`
CreateTime string `json:"create_time"`
ChildGroups []string `json:"child_groups"`
Description string `json:"description"`
}
type Component struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
SelfLink string `json:"selfLink"`
Label interface{} `json:"label"`
StartedAt time.Time `json:"startedAt"`
TotalBackends int `json:"totalBackends"`
HealthyBackends int `json:"healthyBackends"`
}
type PodInfo struct {
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Container string `json:"container"`
}
type Token struct {
Token string `json:"access_token"`
}
type ResourceQuota struct {
Namespace string `json:"namespace"`
Data v12.ResourceQuotaStatus `json:"data"`
}

View File

@@ -1,63 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package workspaces
import "time"
type Workspace struct {
Group `json:",inline"`
Admin string `json:"admin,omitempty"`
Namespaces []string `json:"namespaces"`
DevopsProjects []string `json:"devops_projects"`
}
type UserInvite struct {
Username string `json:"username"`
Role string `json:"role"`
}
type Group struct {
Path string `json:"path"`
Name string `json:"name"`
Gid string `json:"gid"`
Members []string `json:"members"`
Logo string `json:"logo"`
Creator string `json:"creator"`
CreateTime string `json:"create_time"`
ChildGroups []string `json:"child_groups,omitempty"`
Description string `json:"description"`
}
func (g Group) GetCreateTime() (time.Time, error) {
return time.Parse("2006-01-02T15:04:05Z", g.CreateTime)
}
type WorkspaceDPBinding struct {
Workspace string `gorm:"primary_key"`
DevOpsProject string `gorm:"primary_key"`
}
type DevopsProject struct {
ProjectId *string `json:"project_id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Creator string `json:"creator"`
CreateTime *time.Time `json:"create_time,omitempty"`
Status *string `json:"status"`
Visibility *string `json:"visibility,omitempty"`
}

View File

@@ -24,10 +24,9 @@ import (
"io/ioutil"
"net/http"
lister "k8s.io/client-go/listers/core/v1"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam"
"log"
@@ -50,26 +49,14 @@ import (
"sort"
lister2 "k8s.io/client-go/listers/rbac/v1"
"kubesphere.io/kubesphere/pkg/client"
ksErr "kubesphere.io/kubesphere/pkg/errors"
kserr "kubesphere.io/kubesphere/pkg/errors"
)
var (
namespaceLister lister.NamespaceLister
clusterRoleLister lister2.ClusterRoleLister
)
func init() {
namespaceLister = informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
clusterRoleLister = informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
}
func UnBindDevopsProject(workspace string, devops string) error {
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
return db.Delete(&WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
return db.Delete(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
}
func DeleteDevopsProject(username string, devops string) error {
@@ -87,12 +74,12 @@ func DeleteDevopsProject(username string, devops string) error {
return err
}
if result.StatusCode > 200 {
return ksErr.Wrap(data)
return kserr.Parse(data)
}
return nil
}
func CreateDevopsProject(username string, workspace string, devops DevopsProject) (*DevopsProject, error) {
func CreateDevopsProject(username string, workspace string, devops models.DevopsProject) (*models.DevopsProject, error) {
data, err := json.Marshal(devops)
@@ -117,10 +104,10 @@ func CreateDevopsProject(username string, workspace string, devops DevopsProject
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var project DevopsProject
var project models.DevopsProject
err = json.Unmarshal(data, &project)
@@ -140,7 +127,7 @@ func CreateDevopsProject(username string, workspace string, devops DevopsProject
return &project, nil
}
func createDefaultDevopsRoleBinding(workspace string, project DevopsProject) error {
func createDefaultDevopsRoleBinding(workspace string, project models.DevopsProject) error {
admins, err := iam.GetWorkspaceUsers(workspace, constants.WorkspaceAdmin)
if err != nil {
@@ -296,7 +283,7 @@ func ListNamespaceByUser(workspaceName string, username string, keyword string,
}
func Namespaces(workspaceName string) ([]*core.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspaceName}))
if err != nil {
@@ -317,11 +304,9 @@ func Namespaces(workspaceName string) ([]*core.Namespace, error) {
}
func BindingDevopsProject(workspace string, devops string) error {
//db := client.NewSharedDBClient()
//defer db.Close()
//return db.Create(&WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
// TODO FIX
return nil
db := client.DBClient()
defer db.Close()
return db.Create(&models.WorkspaceDPBinding{Workspace: workspace, DevOpsProject: devops}).Error
}
func DeleteNamespace(workspace string, namespaceName string) error {
@@ -338,7 +323,7 @@ func DeleteNamespace(workspace string, namespaceName string) error {
}
func Delete(workspace *Workspace) error {
func Delete(workspace *models.Workspace) error {
err := release(workspace)
@@ -365,13 +350,13 @@ func Delete(workspace *Workspace) error {
}
if result.StatusCode > 200 {
return ksErr.Wrap(data)
return kserr.Parse(data)
}
return nil
}
func release(workspace *Workspace) error {
func release(workspace *models.Workspace) error {
for _, namespace := range workspace.Namespaces {
err := DeleteNamespace(workspace.Name, namespace)
if err != nil && !apierrors.IsNotFound(err) {
@@ -413,7 +398,7 @@ func workspaceRoleRelease(workspace string) error {
return nil
}
func Create(workspace *Workspace) (*Workspace, error) {
func Create(workspace *models.Workspace) (*models.Workspace, error) {
data, err := json.Marshal(workspace)
@@ -434,10 +419,10 @@ func Create(workspace *Workspace) (*Workspace, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var created Workspace
var created models.Workspace
err = json.Unmarshal(data, &created)
@@ -458,7 +443,7 @@ func Create(workspace *Workspace) (*Workspace, error) {
return &created, nil
}
func Edit(workspace *Workspace) (*Workspace, error) {
func Edit(workspace *models.Workspace) (*models.Workspace, error) {
data, err := json.Marshal(workspace)
@@ -487,10 +472,10 @@ func Edit(workspace *Workspace) (*Workspace, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var edited Workspace
var edited models.Workspace
err = json.Unmarshal(data, &edited)
@@ -501,7 +486,7 @@ func Edit(workspace *Workspace) (*Workspace, error) {
return &edited, nil
}
func Detail(name string) (*Workspace, error) {
func Detail(name string) (*models.Workspace, error) {
result, err := http.Get(fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups/%s", constants.AccountAPIServer, name))
@@ -517,10 +502,10 @@ func Detail(name string) (*Workspace, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var group Group
var group models.Group
err = json.Unmarshal(data, &group)
@@ -528,7 +513,7 @@ func Detail(name string) (*Workspace, error) {
return nil, err
}
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
workspace, err := convertGroupToWorkspace(db, group)
@@ -541,7 +526,7 @@ func Detail(name string) (*Workspace, error) {
}
// List all workspaces for the current user
func ListWorkspaceByUser(username string, keyword string) ([]*Workspace, error) {
func ListWorkspaceByUser(username string, keyword string) ([]*models.Workspace, error) {
clusterRoles, err := iam.GetClusterRoles(username)
if err != nil {
@@ -556,7 +541,7 @@ func ListWorkspaceByUser(username string, keyword string) ([]*Workspace, error)
workspacesManager := v1.PolicyRule{APIGroups: []string{"kubesphere.io"}, Verbs: []string{"list", "get"}, Resources: []string{"workspaces"}}
var workspaces []*Workspace
var workspaces []*models.Workspace
if iam.RulesMatchesRequired(rules, workspacesManager) {
workspaces, err = fetch(nil)
} else {
@@ -582,13 +567,13 @@ func ListWorkspaceByUser(username string, keyword string) ([]*Workspace, error)
return workspaces, err
}
func fetch(names []string) ([]*Workspace, error) {
func fetch(names []string) ([]*models.Workspace, error) {
url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/groups", constants.AccountAPIServer)
if names != nil {
if len(names) == 0 {
return make([]*Workspace, 0), nil
return make([]*models.Workspace, 0), nil
} else {
url = url + "?path=" + strings.Join(names, ",")
}
@@ -608,10 +593,10 @@ func fetch(names []string) ([]*Workspace, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var groups []Group
var groups []models.Group
err = json.Unmarshal(data, &groups)
@@ -619,11 +604,11 @@ func fetch(names []string) ([]*Workspace, error) {
return nil, err
}
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
workspaces := make([]*Workspace, 0)
workspaces := make([]*models.Workspace, 0)
for _, group := range groups {
workspace, err := convertGroupToWorkspace(db, group)
if err != nil {
@@ -635,21 +620,21 @@ func fetch(names []string) ([]*Workspace, error) {
return workspaces, nil
}
func ListDevopsProjectsByUser(username string, workspace string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []DevopsProject, error) {
func ListDevopsProjectsByUser(username string, workspace string, keyword string, orderBy string, reverse bool, limit int, offset int) (int, []models.DevopsProject, error) {
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
var workspaceDOPBindings []WorkspaceDPBinding
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", workspace).Find(&workspaceDOPBindings).Error; err != nil {
return 0, nil, err
}
devOpsProjects := make([]DevopsProject, 0)
devOpsProjects := make([]models.DevopsProject, 0)
request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects", constants.DevopsAPIServer), nil)
request.Header.Add("X-Token-Username", username)
request.Header.Add(constants.UserNameHeader, username)
result, err := http.DefaultClient.Do(request)
if err != nil {
@@ -662,15 +647,8 @@ func ListDevopsProjectsByUser(username string, workspace string, keyword string,
return 0, nil, err
}
//if result.StatusCode == 403 || result.StatusCode == 404 {
// if err := db.Delete(&workspaceDOPBinding).Error; err != nil {
// return nil, err
// }
// continue
//}
if result.StatusCode > 200 {
return 0, nil, ksErr.Wrap(data)
return 0, nil, kserr.Parse(data)
}
err = json.Unmarshal(data, &devOpsProjects)
@@ -720,14 +698,14 @@ func ListDevopsProjectsByUser(username string, workspace string, keyword string,
}
if len(devOpsProjects) < offset {
return len(devOpsProjects), make([]DevopsProject, 0), nil
return len(devOpsProjects), make([]models.DevopsProject, 0), nil
} else if len(devOpsProjects) < limit+offset {
return len(devOpsProjects), devOpsProjects[offset:], nil
} else {
return len(devOpsProjects), devOpsProjects[offset : limit+offset], nil
}
}
func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) {
func convertGroupToWorkspace(db *gorm.DB, group models.Group) (*models.Workspace, error) {
namespaces, err := Namespaces(group.Name)
if err != nil {
@@ -740,7 +718,7 @@ func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) {
namespacesNames = append(namespacesNames, namespace.Name)
}
var workspaceDOPBindings []WorkspaceDPBinding
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", group.Name).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
@@ -752,7 +730,7 @@ func convertGroupToWorkspace(db *gorm.DB, group Group) (*Workspace, error) {
devOpsProjects = append(devOpsProjects, workspaceDOPBinding.DevOpsProject)
}
workspace := Workspace{Group: group}
workspace := models.Workspace{Group: group}
workspace.Namespaces = namespacesNames
workspace.DevopsProjects = devOpsProjects
return &workspace, nil
@@ -769,7 +747,7 @@ func CreateNamespace(namespace *core.Namespace) (*core.Namespace, error) {
return ns, nil
}
func Invite(workspaceName string, users []UserInvite) error {
func Invite(workspaceName string, users []models.UserInvite) error {
for _, user := range users {
if !slice.ContainsString(constants.WorkSpaceRoles, user.Role, nil) {
return fmt.Errorf("role %s not exist", user.Role)
@@ -848,9 +826,9 @@ func RemoveMembers(workspaceName string, users []string) error {
return nil
}
func Roles(workspace *Workspace) ([]*v1.ClusterRole, error) {
func Roles(workspace *models.Workspace) ([]*v1.ClusterRole, error) {
roles := make([]*v1.ClusterRole, 0)
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
for _, name := range constants.WorkSpaceRoles {
clusterRole, err := clusterRoleLister.Get(fmt.Sprintf("system:%s:%s", workspace.Name, name))
@@ -871,7 +849,7 @@ func Roles(workspace *Workspace) ([]*v1.ClusterRole, error) {
return roles, nil
}
func GetWorkspaceMembers(workspace string, keyword string) ([]iam.User, error) {
func GetWorkspaceMembers(workspace string, keyword string) ([]models.User, error) {
url := fmt.Sprintf("http://%s/apis/account.kubesphere.io/v1alpha1/workspaces/%s/members", constants.AccountAPIServer, workspace)
@@ -893,10 +871,10 @@ func GetWorkspaceMembers(workspace string, keyword string) ([]iam.User, error) {
}
if result.StatusCode > 200 {
return nil, ksErr.Wrap(data)
return nil, kserr.Parse(data)
}
var users []iam.User
var users []models.User
err = json.Unmarshal(data, &users)
@@ -908,7 +886,7 @@ func GetWorkspaceMembers(workspace string, keyword string) ([]iam.User, error) {
}
func WorkspaceRoleInit(workspace *Workspace) error {
func WorkspaceRoleInit(workspace *models.Workspace) error {
k8sClient := client.K8sClient()
admin := new(v1.ClusterRole)
@@ -1169,7 +1147,7 @@ func unbindNamespacesRole(namespaces []string, users []string) error {
return nil
}
func UnbindWorkspace(workspace *Workspace, users []string) error {
func UnbindWorkspace(workspace *models.Workspace, users []string) error {
err := unbindNamespacesRole(workspace.Namespaces, users)
@@ -1186,7 +1164,7 @@ func UnbindWorkspace(workspace *Workspace, users []string) error {
return nil
}
func CreateWorkspaceRoleBinding(workspace *Workspace, username string, role string) error {
func CreateWorkspaceRoleBinding(workspace *models.Workspace, username string, role string) error {
k8sClient := client.K8sClient()
@@ -1242,10 +1220,10 @@ func CreateWorkspaceRoleBinding(workspace *Workspace, username string, role stri
func GetDevOpsProjects(workspaceName string) ([]string, error) {
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
var workspaceDOPBindings []WorkspaceDPBinding
var workspaceDOPBindings []models.WorkspaceDPBinding
if err := db.Where("workspace = ?", workspaceName).Find(&workspaceDOPBindings).Error; err != nil {
return nil, err
@@ -1304,7 +1282,7 @@ func Count() (int, error) {
}
if result.StatusCode > 200 {
return 0, ksErr.Wrap(data)
return 0, kserr.Parse(data)
}
var count map[string]json.Number
@@ -1319,13 +1297,14 @@ func Count() (int, error) {
v, err := value.Int64()
if err != nil {
return 0, ksErr.New(ksErr.Internal, err.Error())
return 0, err
}
return int(v), nil
}
func GetAllProjectNums() (int, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
list, err := namespaceLister.List(labels.Everything())
if err != nil {
return 0, err
@@ -1334,11 +1313,11 @@ func GetAllProjectNums() (int, error) {
}
func GetAllDevOpsProjectsNums() (int, error) {
db := client.NewSharedDBClient()
db := client.DBClient()
defer db.Close()
var count int
if err := db.Model(&WorkspaceDPBinding{}).Count(&count).Error; err != nil {
if err := db.Model(&models.WorkspaceDPBinding{}).Count(&count).Error; err != nil {
return 0, err
}
return count, nil
@@ -1357,7 +1336,7 @@ func GetAllAccountNums() (int, error) {
return 0, err
}
if result.StatusCode > 200 {
return 0, ksErr.Wrap(data)
return 0, kserr.Parse(data)
}
var count map[string]json.Number

View File

@@ -1,397 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package monitoring
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"kubesphere.io/kubesphere/pkg/client"
"kubesphere.io/kubesphere/pkg/models/metrics"
)
func Route(ws *restful.WebService) {
u := Monitor{}
ws.Route(ws.GET("/clusters").To(u.monitorCluster).
Doc("monitor cluster level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("cluster_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "cluster"}))
ws.Route(ws.GET("/nodes").To(u.monitorNode).
Doc("monitor nodes level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("node_cpu_utilisation")).
Param(ws.QueryParameter("nodes_filter", "node re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "node"}))
ws.Route(ws.GET("/nodes/{node_id}").To(u.monitorNode).
Doc("monitor specific node level metrics").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("node_cpu_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "node"}))
ws.Route(ws.GET("/namespaces").To(u.monitorNamespace).
Doc("monitor namespaces level metrics").
Param(ws.QueryParameter("namespaces_filter", "namespaces re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "namespace"}))
ws.Route(ws.GET("/namespaces/{ns_name}").To(u.monitorNamespace).
Doc("monitor specific namespace level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("namespace_memory_utilisation")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "namespace"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods").To(u.monitorPod).
Doc("monitor pods level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}").To(u.monitorPod).
Doc("monitor specific pod level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods").To(u.monitorPod).
Doc("monitor pods level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods/{pod_name}").To(u.monitorPod).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true).DefaultValue("i-k89a62il")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "pod"}))
ws.Route(ws.GET("/nodes/{node_id}/pods/{pod_name}/containers").To(u.monitorContainer).
Doc("monitor specific pod level metrics by nodeid").
Param(ws.PathParameter("node_id", "specific node").DataType("string").Required(true)).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true)).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("pod_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"}))
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}/containers").To(u.monitorContainer).
Doc("monitor containers level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("containers_filter", "container re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"})).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{ns_name}/pods/{pod_name}/containers/{container_name}").To(u.monitorContainer).
Doc("monitor specific container level metrics").
Param(ws.PathParameter("ns_name", "specific namespace").DataType("string").Required(true).DefaultValue("monitoring")).
Param(ws.PathParameter("pod_name", "specific pod").DataType("string").Required(true).DefaultValue("")).
Param(ws.PathParameter("container_name", "specific container").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("metrics_name", "metrics name cpu memory...").DataType("string").Required(true).DefaultValue("container_memory_utilisation_wo_cache")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "container"})).
Consumes(restful.MIME_JSON, restful.MIME_XML).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("/namespaces/{ns_name}/workloads/{workload_kind}").To(u.monitorWorkload).
Doc("monitor specific workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.PathParameter("workload_kind", "workload kind").DataType("string").Required(false).DefaultValue("daemonset")).
Param(ws.QueryParameter("workload_name", "workload name").DataType("string").Required(true).DefaultValue("")).
Param(ws.QueryParameter("pods_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("openpitrix.*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "max metric items in a page").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workload"}))
ws.Route(ws.GET("/namespaces/{ns_name}/workloads").To(u.monitorWorkload).
Doc("monitor all workload level metrics").
Param(ws.PathParameter("ns_name", "namespace").DataType("string").Required(true).DefaultValue("kube-system")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...").DataType("string").Required(false)).
Param(ws.QueryParameter("workloads_filter", "pod re2 expression filter").DataType("string").Required(false).DefaultValue("")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workload"}))
// list all namespace in this workspace by selected metrics
ws.Route(ws.GET("/workspaces/{workspace_name}").To(u.monitorOneWorkspace).
Doc("monitor workspaces level metrics").
Param(ws.PathParameter("workspace_name", "workspace name").DataType("string").Required(true)).
Param(ws.QueryParameter("namespaces_filter", "namespaces filter").DataType("string").Required(false).DefaultValue("k.*")).
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("namespace_memory_utilisation_wo_cache")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workspace"}))
ws.Route(ws.GET("/workspaces").To(u.monitorAllWorkspaces).
Doc("monitor workspaces level metrics").
Param(ws.QueryParameter("metrics_filter", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("workspace_memory_utilisation")).
Param(ws.QueryParameter("workspaces_filter", "workspaces re2 expression filter").DataType("string").Required(false).DefaultValue(".*")).
Param(ws.QueryParameter("sort_metric", "sort metric").DataType("string").Required(false)).
Param(ws.QueryParameter("sort_type", "ascending descending order").DataType("string").Required(false)).
Param(ws.QueryParameter("page", "page number").DataType("string").Required(false).DefaultValue("1")).
Param(ws.QueryParameter("limit", "metrics name cpu memory...in re2 regex").DataType("string").Required(false).DefaultValue("4")).
Param(ws.QueryParameter("type", "rank, statistic").DataType("string").Required(false).DefaultValue("rank")).
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "workspace"}))
ws.Route(ws.GET("/components").To(u.monitorComponentStatus).
Doc("monitor k8s components status").
Metadata(restfulspec.KeyOpenAPITags, []string{"monitoring", "components"}))
}
func (u Monitor) monitorPod(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
podName := requestParams.PodName
metricName := requestParams.MetricsName
if podName != "" {
// single pod single metric
queryType, params, nullRule := metrics.AssemblePodMetricRequestInfo(requestParams, metricName)
var res *metrics.FormatedMetric
if !nullRule {
res = metrics.GetMetric(queryType, params, metricName)
}
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelPod)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func (u Monitor) monitorContainer(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if requestParams.MetricsFilter != "" {
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelContainer)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelContainerName)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorContainer(requestParams, metricName)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorWorkload(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkload)
var sortedMetrics *metrics.FormatedLevelMetric
var maxMetricCount int
wlKind := requestParams.WorkloadKind
// sorting
if wlKind == "" {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkload)
} else {
sortedMetrics, maxMetricCount = metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelPodName)
}
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
func (u Monitor) monitorAllWorkspaces(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "_statistics" {
// merge multiple metric: all-devops, all-roles, all-projects...this api is designed for admin
res := metrics.MonitorAllWorkspacesStatistics()
response.WriteAsJson(res)
} else if tp == "rank" {
rawMetrics := metrics.MonitorAllWorkspaces(requestParams)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelWorkspace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorOneWorkspace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
tp := requestParams.Tp
if tp == "rank" {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
} else if tp == "_statistics" {
wsName := requestParams.WsName
// merge multiple metric: devops, roles, projects...
res := metrics.MonitorOneWorkspaceStatistics(wsName)
response.WriteAsJson(res)
} else {
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelWorkspace)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorNamespace(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
nsName := requestParams.NsName
if nsName != "" {
// single
queryType, params := metrics.AssembleNamespaceMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNamespace)
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNamespace)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
func (u Monitor) monitorCluster(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleClusterMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
response.WriteAsJson(res)
} else {
// multiple
res := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelCluster)
response.WriteAsJson(res)
}
}
func (u Monitor) monitorNode(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
metricName := requestParams.MetricsName
if metricName != "" {
// single
queryType, params := metrics.AssembleNodeMetricRequestInfo(requestParams, metricName)
res := metrics.GetMetric(queryType, params, metricName)
nodeAddress := metrics.GetNodeAddressInfo()
metrics.AddNodeAddressMetric(res, nodeAddress)
response.WriteAsJson(res)
} else {
// multiple
rawMetrics := metrics.MonitorAllMetrics(requestParams, metrics.MetricLevelNode)
nodeAddress := metrics.GetNodeAddressInfo()
for i := 0; i < len(rawMetrics.Results); i++ {
metrics.AddNodeAddressMetric(&rawMetrics.Results[i], nodeAddress)
}
// sorting
sortedMetrics, maxMetricCount := metrics.Sort(requestParams.SortMetricName, requestParams.SortType, rawMetrics, metrics.MetricLevelNode)
// paging
pagedMetrics := metrics.Page(requestParams.PageNum, requestParams.LimitNum, sortedMetrics, maxMetricCount)
response.WriteAsJson(pagedMetrics)
}
}
// k8s component(controller, scheduler, etcd) status
func (u Monitor) monitorComponentStatus(request *restful.Request, response *restful.Response) {
requestParams := client.ParseMonitoringRequestParams(request)
status := metrics.MonitorComponentStatus(requestParams)
response.WriteAsJson(status)
}
type Monitor struct {
}

63
pkg/options/options.go Normal file
View File

@@ -0,0 +1,63 @@
/*
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 options
import (
"github.com/spf13/pflag"
)
var SharedOptions = NewServerRunOptions()
type ServerRunOptions struct {
// server bind address
BindAddress string
// insecure port number
InsecurePort int
// secure port number
SecurePort int
// tls cert file
TlsCertFile string
// tls private key file
TlsPrivateKey string
CommandLine *pflag.FlagSet
}
func NewServerRunOptions() *ServerRunOptions {
// create default server run options
s := ServerRunOptions{
BindAddress: "0.0.0.0",
InsecurePort: 9090,
SecurePort: 0,
TlsCertFile: "",
TlsPrivateKey: "",
CommandLine: &pflag.FlagSet{},
}
s.CommandLine.StringVar(&s.BindAddress, "bind-address", "0.0.0.0", "server bind address")
s.CommandLine.IntVar(&s.InsecurePort, "insecure-port", 9090, "insecure port number")
s.CommandLine.IntVar(&s.SecurePort, "secure-port", 0, "secure port number")
s.CommandLine.StringVar(&s.TlsCertFile, "tls-cert-file", "", "tls cert file")
s.CommandLine.StringVar(&s.TlsPrivateKey, "tls-private-key", "", "tls private key")
return &s
}

View File

@@ -1,54 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package params
import (
"regexp"
"strconv"
"golang.org/x/tools/container/intsets"
)
const (
Paging = "paging"
OrderBy = "orderBy"
Conditions = "conditions"
Reserve = "reserve"
)
func ParsePaging(paging string) (limit, offset int) {
limit = intsets.MaxInt
offset = 0
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
return
}
func ParseReserve(reserve string) bool {
b, err := strconv.ParseBool(reserve)
if err != nil {
return false
}
return b
}

89
pkg/params/params.go Normal file
View File

@@ -0,0 +1,89 @@
/*
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 params
import (
"fmt"
"github.com/emicklei/go-restful"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/container/intsets"
)
const (
PagingParam = "paging"
OrderByParam = "orderBy"
ConditionsParam = "conditions"
ReverseParam = "reverse"
)
func ParsePaging(req *restful.Request) (limit, offset int) {
paging := req.QueryParameter(PagingParam)
limit = intsets.MaxInt
offset = 0
if groups := regexp.MustCompile(`^limit=(\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 {
limit, _ = strconv.Atoi(groups[1])
page, _ := strconv.Atoi(groups[2])
if page < 0 {
page = 1
}
offset = (page - 1) * limit
}
return
}
func ParseConditions(req *restful.Request) (*Conditions, error) {
conditionsStr := req.QueryParameter(ConditionsParam)
conditions := &Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)}
if conditionsStr == "" {
return conditions, nil
}
for _, item := range strings.Split(conditionsStr, ",") {
if strings.Count(item, "=") > 1 || strings.Count(item, "~") > 1 {
return nil, fmt.Errorf("invalid conditions")
}
if groups := regexp.MustCompile(`(\S+)([=~])(\S+)`).FindStringSubmatch(item); len(groups) == 4 {
if groups[2] == "=" {
conditions.Match[groups[1]] = groups[3]
} else {
conditions.Fuzzy[groups[1]] = groups[3]
}
} else {
return nil, fmt.Errorf("invalid conditions")
}
}
return conditions, nil
}
func ParseReverse(req *restful.Request) bool {
reverse := req.QueryParameter(ReverseParam)
b, err := strconv.ParseBool(reverse)
if err != nil {
return false
}
return b
}
type Conditions struct {
Match map[string]string
Fuzzy map[string]string
}

48
pkg/utils/iputils.go Normal file
View File

@@ -0,0 +1,48 @@
/*
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 utils
import (
"net"
"net/http"
)
const (
XForwardedFor = "X-Forwarded-For"
XRealIP = "X-Real-IP"
XClientIP = "x-client-ip"
)
func RemoteIp(req *http.Request) string {
remoteAddr := req.RemoteAddr
if ip := req.Header.Get(XClientIP); ip != "" {
remoteAddr = ip
} else if ip := req.Header.Get(XRealIP); ip != "" {
remoteAddr = ip
} else if ip = req.Header.Get(XForwardedFor); ip != "" {
remoteAddr = ip
} else {
remoteAddr, _, _ = net.SplitHostPort(remoteAddr)
}
if remoteAddr == "::1" {
remoteAddr = "127.0.0.1"
}
return remoteAddr
}

59
pkg/utils/jwt/jwt.go Normal file
View File

@@ -0,0 +1,59 @@
/*
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 jwt
import (
"fmt"
"os"
"github.com/dgrijalva/jwt-go"
)
const secretEnv = "JWT_SECRET"
var Secret []byte
func init() {
if env := os.Getenv(secretEnv); env != "" {
Secret = []byte(env)
} else {
fmt.Printf("Environment variable %s not set\n", secretEnv)
}
}
func provideKey(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
return Secret, nil
} else {
return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"])
}
}
func ValidateToken(uToken string) (*jwt.Token, error) {
if len(uToken) == 0 {
return nil, fmt.Errorf("token length is zero")
}
token, err := jwt.Parse(uToken, provideKey)
if err != nil {
return nil, err
}
return token, nil
}

View File

@@ -15,12 +15,23 @@
limitations under the License.
*/
package v1alpha2
package utils
import (
"kubesphere.io/kubesphere/pkg/monitoring/v1alpha2/monitoring"
)
func init() {
addToWebServiceFuncs = append(addToWebServiceFuncs, monitoring.Route)
func RemoveString(slice []string, remove func(item string) bool) []string {
for i := 0; i < len(slice); i++ {
if remove(slice[i]) {
slice = append(slice[:i], slice[i+1:]...)
i--
}
}
return slice
}
func HasString(slice []string, str string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}

View File

@@ -1,40 +1,19 @@
/*
Copyright 2018 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Copyright 2019 The KubeSphere Authors.
http://www.apache.org/licenses/LICENSE-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package version
import (
"fmt"
"os"
"github.com/spf13/pflag"
"kubesphere.io/kubesphere/pkg/constants"
)
var (
versionFlag = pflag.Bool("version", false, "print the version of kubesphere")
)
// PrintAndExitIfRequested will check if the -version flag was passed
// and, if so, print the version and exit.
func PrintAndExitIfRequested() {
if *versionFlag {
fmt.Printf("Kubesphere %s\n", constants.APIVersion)
os.Exit(0)
}
}