@@ -1,54 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2019 The KubeSphere Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* /
|
||||
*/
|
||||
|
||||
package openpitrix
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"kubesphere.io/kubesphere/pkg/models/openpitrix/attachment"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func DescribeAttachment(req *restful.Request, resp *restful.Response) {
|
||||
attachmentId := req.PathParameter("attachment")
|
||||
fileName := req.QueryParameter("filename")
|
||||
result, err := attachment.DescribeAttachment(attachmentId)
|
||||
// file raw
|
||||
if fileName != "" {
|
||||
data := result.AttachmentContent[fileName]
|
||||
resp.Write(data)
|
||||
resp.Header().Set("Content-Type", "text/plain")
|
||||
return
|
||||
}
|
||||
|
||||
if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if status.Code(err) == codes.NotFound {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
@@ -1,65 +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 resources
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
k8serr "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog"
|
||||
"net/http"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
)
|
||||
|
||||
func GetKubectl(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
kubectlPod, err := kubectl.GetKubectlPod(user)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(kubectlPod)
|
||||
}
|
||||
|
||||
func GetKubeconfig(req *restful.Request, resp *restful.Response) {
|
||||
|
||||
user := req.PathParameter("user")
|
||||
|
||||
kubectlConfig, err := kubeconfig.GetKubeConfig(user)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if k8serr.IsNotFound(err) {
|
||||
// recreate
|
||||
kubeconfig.CreateKubeConfig(user)
|
||||
resp.WriteHeaderAndJson(http.StatusNotFound, errors.Wrap(err), restful.MIME_JSON)
|
||||
} else {
|
||||
resp.WriteHeaderAndJson(http.StatusInternalServerError, errors.Wrap(err), restful.MIME_JSON)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resp.Write([]byte(kubectlConfig))
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
package tenant
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
k8serr "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/klog"
|
||||
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
|
||||
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/logging"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models/metrics"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/models/tenant"
|
||||
"kubesphere.io/kubesphere/pkg/models/workspaces"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ListWorkspaceRules(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("workspace")
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
|
||||
rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(rules)
|
||||
}
|
||||
|
||||
func ListWorkspaces(req *restful.Request, resp *restful.Response) {
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
|
||||
orderBy := req.QueryParameter(params.OrderByParam)
|
||||
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
|
||||
reverse := params.ParseReverse(req)
|
||||
|
||||
if orderBy == "" {
|
||||
orderBy = v1alpha2.CreateTime
|
||||
reverse = true
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(result)
|
||||
}
|
||||
|
||||
func DescribeWorkspace(req *restful.Request, resp *restful.Response) {
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
|
||||
result, err := tenant.DescribeWorkspace(username, workspaceName)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorf("describe workspace failed: %+v", err)
|
||||
if k8serr.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(result)
|
||||
}
|
||||
func ListNamespacesByUsername(req *restful.Request, resp *restful.Response) {
|
||||
ListNamespaces(req, resp)
|
||||
}
|
||||
|
||||
func ListNamespaces(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("workspace")
|
||||
username := req.PathParameter("member")
|
||||
// /workspaces/{workspace}/members/{username}/namespaces
|
||||
if username == "" {
|
||||
// /workspaces/{workspace}/namespaces
|
||||
username = req.HeaderParameter(constants.UserNameHeader)
|
||||
}
|
||||
|
||||
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
|
||||
orderBy := req.QueryParameter(params.OrderByParam)
|
||||
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
|
||||
reverse := params.ParseReverse(req)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
conditions.Match[constants.WorkspaceLabelKey] = workspace
|
||||
|
||||
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
namespaces := make([]*v1.Namespace, 0)
|
||||
|
||||
for _, item := range result.Items {
|
||||
namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy())
|
||||
}
|
||||
|
||||
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
|
||||
|
||||
items := make([]interface{}, 0)
|
||||
|
||||
for _, item := range namespaces {
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
result.Items = items
|
||||
|
||||
resp.WriteAsJson(result)
|
||||
}
|
||||
|
||||
func CreateNamespace(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
var namespace v1.Namespace
|
||||
err := req.ReadEntity(&namespace)
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
workspace, err := tenant.GetWorkspace(workspaceName)
|
||||
|
||||
err = checkResourceQuotas(workspace)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if k8serr.IsNotFound(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
created, err := tenant.CreateNamespace(workspaceName, &namespace, username)
|
||||
|
||||
if err != nil {
|
||||
if k8serr.IsAlreadyExists(err) {
|
||||
resp.WriteHeaderAndEntity(http.StatusConflict, err)
|
||||
} else {
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
resp.WriteAsJson(created)
|
||||
}
|
||||
|
||||
func DeleteNamespace(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
namespaceName := req.PathParameter("namespace")
|
||||
|
||||
err := workspaces.DeleteNamespace(workspaceName, namespaceName)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(errors.None)
|
||||
}
|
||||
|
||||
func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
|
||||
namespace := req.PathParameter("namespace")
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
|
||||
rules, err := iam.GetUserNamespaceSimpleRules(namespace, username)
|
||||
|
||||
if err != nil {
|
||||
resp.WriteError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteAsJson(rules)
|
||||
}
|
||||
|
||||
func LogQuery(req *restful.Request, resp *restful.Response) {
|
||||
operation := req.QueryParameter("operation")
|
||||
req, err := regenerateLoggingRequest(req)
|
||||
switch {
|
||||
case err != nil:
|
||||
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
case req != nil:
|
||||
logging.LoggingQueryCluster(req, resp)
|
||||
default:
|
||||
if operation == "export" {
|
||||
resp.Header().Set(restful.HEADER_ContentType, "text/plain")
|
||||
resp.Header().Set("Content-Disposition", "attachment")
|
||||
resp.Write(nil)
|
||||
} else {
|
||||
resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// override namespace query conditions
|
||||
func regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) {
|
||||
|
||||
username := req.HeaderParameter(constants.UserNameHeader)
|
||||
|
||||
// regenerate the request for log query
|
||||
newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster")
|
||||
values := req.Request.URL.Query()
|
||||
|
||||
clusterRules, err := iam.GetUserClusterRules(username)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}})
|
||||
// if the user is not a cluster admin
|
||||
if !hasClusterLogAccess {
|
||||
queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",")
|
||||
// then the user can only view logs of namespaces he belongs to
|
||||
namespaces := make([]string, 0)
|
||||
roles, err := iam.GetUserRoles("", username)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
for _, role := range roles {
|
||||
if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) {
|
||||
namespaces = append(namespaces, role.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
// if the user belongs to no namespace
|
||||
// then no log visible
|
||||
if len(namespaces) == 0 {
|
||||
return nil, nil
|
||||
} else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" {
|
||||
values.Set("namespaces", strings.Join(namespaces, ","))
|
||||
} else {
|
||||
inter := intersection(queryNamespaces, namespaces)
|
||||
if len(inter) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
values.Set("namespaces", strings.Join(inter, ","))
|
||||
}
|
||||
}
|
||||
|
||||
newUrl.RawQuery = values.Encode()
|
||||
|
||||
// forward the request to logging model
|
||||
newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil)
|
||||
return restful.NewRequest(newHttpRequest), nil
|
||||
}
|
||||
|
||||
func intersection(s1, s2 []string) (inter []string) {
|
||||
hash := make(map[string]bool)
|
||||
for _, e := range s1 {
|
||||
hash[e] = true
|
||||
}
|
||||
for _, e := range s2 {
|
||||
// If elements present in the hashmap then append intersection list.
|
||||
if hash[e] {
|
||||
inter = append(inter, e)
|
||||
}
|
||||
}
|
||||
//Remove dups from slice.
|
||||
inter = removeDups(inter)
|
||||
return
|
||||
}
|
||||
|
||||
//Remove dups from slice.
|
||||
func removeDups(elements []string) (nodups []string) {
|
||||
encountered := make(map[string]bool)
|
||||
for _, element := range elements {
|
||||
if !encountered[element] {
|
||||
nodups = append(nodups, element)
|
||||
encountered[element] = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -78,7 +78,7 @@ func addWebService(c *restful.Container, devopsClient devops.Interface,
|
||||
Writes([]sonarqube.SonarStatus{}))
|
||||
}
|
||||
|
||||
if projectPipleineEnable{
|
||||
if projectPipleineEnable {
|
||||
projectPipelineHander := NewProjectPipelineHandler(devopsClient, dbClient)
|
||||
webservice.Route(webservice.GET("/devops/{devops}").
|
||||
To(projectPipelineHander.GetDevOpsProjectHandler).
|
||||
@@ -190,8 +190,6 @@ func addWebService(c *restful.Container, devopsClient devops.Interface,
|
||||
Returns(http.StatusOK, RespOK, devops.ProjectPipeline{}).
|
||||
Writes(devops.ProjectPipeline{}))
|
||||
|
||||
|
||||
|
||||
webservice.Route(webservice.POST("/devops/{devops}/credentials").
|
||||
To(projectPipelineHander.CreateDevOpsProjectCredentialHandler).
|
||||
Doc("Create a credential in the specified DevOps project").
|
||||
@@ -785,7 +783,6 @@ The last one is encrypted info, such as the password of the username-password ty
|
||||
Writes(devops.ResJson{}))
|
||||
}
|
||||
|
||||
|
||||
if s2iEnable {
|
||||
s2iHandler := NewS2iBinaryHandler(ksClient, ksInformer, s3Client)
|
||||
webservice.Route(webservice.PUT("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file").
|
||||
@@ -808,7 +805,7 @@ The last one is encrypted info, such as the password of the username-password ty
|
||||
Param(webservice.PathParameter("file", "the name of binary file")).
|
||||
Returns(http.StatusOK, RespOK, nil))
|
||||
}
|
||||
|
||||
|
||||
c.Add(webservice)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -5,10 +5,13 @@ import (
|
||||
"github.com/emicklei/go-restful"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
k8serr "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/informers"
|
||||
"kubesphere.io/kubesphere/pkg/models/components"
|
||||
"kubesphere.io/kubesphere/pkg/models/git"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubeconfig"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
"kubesphere.io/kubesphere/pkg/models/quotas"
|
||||
"kubesphere.io/kubesphere/pkg/models/registries"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
@@ -31,6 +34,8 @@ type resourceHandler struct {
|
||||
routerOperator routers.RouterOperator
|
||||
gitVerifier git.GitVerifier
|
||||
registryGetter registries.RegistryGetter
|
||||
kubeconfigOperator kubeconfig.Interface
|
||||
kubectlOperator kubectl.Interface
|
||||
}
|
||||
|
||||
func newResourceHandler(client k8s.Client) *resourceHandler {
|
||||
@@ -45,6 +50,8 @@ func newResourceHandler(client k8s.Client) *resourceHandler {
|
||||
routerOperator: routers.NewRouterOperator(client.Kubernetes(), factory.KubernetesSharedInformerFactory()),
|
||||
gitVerifier: git.NewGitVerifier(factory.KubernetesSharedInformerFactory()),
|
||||
registryGetter: registries.NewRegistryGetter(factory.KubernetesSharedInformerFactory()),
|
||||
kubeconfigOperator: kubeconfig.NewKubeconfigOperator(),
|
||||
kubectlOperator: kubectl.NewKubectlOperator(client.Kubernetes(), factory.KubernetesSharedInformerFactory()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +347,35 @@ func (r *resourceHandler) handleGetNamespacedAbnormalWorkloads(request *restful.
|
||||
|
||||
}
|
||||
|
||||
func (r *resourceHandler) handleGetAbnormalWorkloads(request *restful.Request, response *restful.Response) {
|
||||
r.handleGetNamespacedAbnormalWorkloads(request, response)
|
||||
func (r *resourceHandler) GetKubectlPod(request *restful.Request, response *restful.Response) {
|
||||
user := request.PathParameter("user")
|
||||
|
||||
kubectlPod, err := r.kubectlOperator.GetKubectlPod(user)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(kubectlPod)
|
||||
}
|
||||
|
||||
func (r *resourceHandler) GetKubeconfig(request *restful.Request, response *restful.Response) {
|
||||
user := request.PathParameter("user")
|
||||
|
||||
kubectlConfig, err := r.kubeconfigOperator.GetKubeConfig(user)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if k8serr.IsNotFound(err) {
|
||||
// recreate
|
||||
response.WriteHeaderAndJson(http.StatusNotFound, errors.Wrap(err), restful.MIME_JSON)
|
||||
} else {
|
||||
response.WriteHeaderAndJson(http.StatusInternalServerError, errors.Wrap(err), restful.MIME_JSON)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
response.Write([]byte(kubectlConfig))
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/api/resource/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/resources"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
@@ -82,7 +81,7 @@ func AddToContainer(c *restful.Container, client k8s.Client) error {
|
||||
Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")))
|
||||
|
||||
webservice.Route(webservice.GET("/users/{user}/kubectl").
|
||||
To(resources.GetKubectl).
|
||||
To(handler.GetKubectlPod).
|
||||
Doc("get user's kubectl pod").
|
||||
Param(webservice.PathParameter("user", "username")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserResourcesTag}).
|
||||
@@ -90,7 +89,7 @@ func AddToContainer(c *restful.Container, client k8s.Client) error {
|
||||
|
||||
webservice.Route(webservice.GET("/users/{user}/kubeconfig").
|
||||
Produces("text/plain", restful.MIME_JSON).
|
||||
To(resources.GetKubeconfig).
|
||||
To(handler.GetKubeconfig).
|
||||
Doc("get users' kubeconfig").
|
||||
Param(webservice.PathParameter("user", "username")).
|
||||
Returns(http.StatusOK, api.StatusOK, "").
|
||||
@@ -214,7 +213,7 @@ func AddToContainer(c *restful.Container, client k8s.Client) error {
|
||||
Doc("get abnormal workloads' count of whole cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}).
|
||||
Returns(http.StatusOK, api.StatusOK, api.Workloads{}).
|
||||
To(handler.handleGetAbnormalWorkloads))
|
||||
To(handler.handleGetNamespacedAbnormalWorkloads))
|
||||
webservice.Route(webservice.GET("/namespaces/{namespace}/abnormalworkloads").
|
||||
Doc("get abnormal workloads' count of specified namespace").
|
||||
Param(webservice.PathParameter("namespace", "the name of the project")).
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/policy"
|
||||
"kubesphere.io/kubesphere/pkg/models/kubectl"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/clusterrole"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource"
|
||||
@@ -538,17 +537,18 @@ func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO move to user controller
|
||||
if clusterRoleName == constants.ClusterAdmin {
|
||||
// create kubectl pod if cluster role is cluster-admin
|
||||
if err := kubectl.CreateKubectlDeploy(username); err != nil {
|
||||
klog.Error("create user terminal pod failed", username, err)
|
||||
}
|
||||
//if err := kubectl.CreateKubectlDeploy(username); err != nil {
|
||||
// klog.Error("create user terminal pod failed", username, err)
|
||||
//}
|
||||
|
||||
} else {
|
||||
// delete kubectl pod if cluster role is not cluster-admin, whether it exists or not
|
||||
if err := kubectl.DelKubectlDeploy(username); err != nil {
|
||||
klog.Error("delete user terminal pod failed", username, err)
|
||||
}
|
||||
//if err := kubectl.DelKubectlDeploy(username); err != nil {
|
||||
// klog.Error("delete user terminal pod failed", username, err)
|
||||
//}
|
||||
}
|
||||
|
||||
clusterRoleBinding := &rbacv1.ClusterRoleBinding{}
|
||||
|
||||
@@ -18,298 +18,17 @@
|
||||
|
||||
package kubeconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client"
|
||||
"math/big"
|
||||
rd "math/rand"
|
||||
"time"
|
||||
|
||||
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
)
|
||||
|
||||
const (
|
||||
caPath = "/etc/kubernetes/pki/ca.crt"
|
||||
keyPath = "/etc/kubernetes/pki/ca.key"
|
||||
clusterName = "kubernetes"
|
||||
kubectlConfigKey = "config"
|
||||
defaultNamespace = "default"
|
||||
)
|
||||
|
||||
type clusterInfo struct {
|
||||
CertificateAuthorityData string `yaml:"certificate-authority-data"`
|
||||
Server string `yaml:"server"`
|
||||
type Interface interface {
|
||||
GetKubeConfig(username string) (string, error)
|
||||
}
|
||||
|
||||
type cluster struct {
|
||||
Cluster clusterInfo `yaml:"cluster"`
|
||||
Name string `yaml:"name"`
|
||||
type operator struct {
|
||||
}
|
||||
|
||||
type contextInfo struct {
|
||||
Cluster string `yaml:"cluster"`
|
||||
User string `yaml:"user"`
|
||||
NameSpace string `yaml:"namespace"`
|
||||
func (o operator) GetKubeConfig(username string) (string, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
type contextObject struct {
|
||||
Context contextInfo `yaml:"context"`
|
||||
Name string `yaml:"name"`
|
||||
}
|
||||
|
||||
type userInfo struct {
|
||||
CaData string `yaml:"client-certificate-data"`
|
||||
KeyData string `yaml:"client-key-data"`
|
||||
}
|
||||
|
||||
type user struct {
|
||||
Name string `yaml:"name"`
|
||||
User userInfo `yaml:"user"`
|
||||
}
|
||||
|
||||
type kubeConfig struct {
|
||||
ApiVersion string `yaml:"apiVersion"`
|
||||
Clusters []cluster `yaml:"clusters"`
|
||||
Contexts []contextObject `yaml:"contexts"`
|
||||
CurrentContext string `yaml:"current-context"`
|
||||
Kind string `yaml:"kind"`
|
||||
Preferences map[string]string `yaml:"preferences"`
|
||||
Users []user `yaml:"users"`
|
||||
}
|
||||
|
||||
type CertInformation struct {
|
||||
Country []string
|
||||
Organization []string
|
||||
OrganizationalUnit []string
|
||||
EmailAddress []string
|
||||
Province []string
|
||||
Locality []string
|
||||
CommonName string
|
||||
CrtName, KeyName string
|
||||
IsCA bool
|
||||
Names []pkix.AttributeTypeAndValue
|
||||
}
|
||||
|
||||
func createCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) ([]byte, []byte, error) {
|
||||
var cert, key bytes.Buffer
|
||||
Crt := newCertificate(info)
|
||||
Key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
|
||||
buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: buf})
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
buf = x509.MarshalPKCS1PrivateKey(Key)
|
||||
pem.Encode(&key, &pem.Block{Type: "PRIVATE KEY", Bytes: buf})
|
||||
|
||||
return cert.Bytes(), key.Bytes(), nil
|
||||
}
|
||||
|
||||
func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) {
|
||||
rootcertificate, err = parseCrt(crtPath)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
rootPrivateKey, err = parseKey(keyPath)
|
||||
return rootcertificate, rootPrivateKey, nil
|
||||
}
|
||||
|
||||
func parseCrt(path string) (*x509.Certificate, error) {
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
p := &pem.Block{}
|
||||
p, buf = pem.Decode(buf)
|
||||
return x509.ParseCertificate(p.Bytes)
|
||||
}
|
||||
|
||||
func parseKey(path string) (*rsa.PrivateKey, error) {
|
||||
buf, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
p, buf := pem.Decode(buf)
|
||||
return x509.ParsePKCS1PrivateKey(p.Bytes)
|
||||
}
|
||||
|
||||
func newCertificate(info CertInformation) *x509.Certificate {
|
||||
rd.Seed(time.Now().UnixNano())
|
||||
return &x509.Certificate{
|
||||
SerialNumber: big.NewInt(rd.Int63()),
|
||||
Subject: pkix.Name{
|
||||
Country: info.Country,
|
||||
Organization: info.Organization,
|
||||
OrganizationalUnit: info.OrganizationalUnit,
|
||||
Province: info.Province,
|
||||
CommonName: info.CommonName,
|
||||
Locality: info.Locality,
|
||||
ExtraNames: info.Names,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(20, 0, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: info.IsCA,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
EmailAddresses: info.EmailAddress,
|
||||
}
|
||||
}
|
||||
|
||||
func generateCaAndKey(user, caPath, keyPath string) (string, string, error) {
|
||||
crtInfo := CertInformation{CommonName: user, IsCA: false}
|
||||
|
||||
crt, pri, err := Parse(caPath, keyPath)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return "", "", err
|
||||
}
|
||||
cert, key, err := createCRT(crt, pri, crtInfo)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
base64Cert := base64.StdEncoding.EncodeToString(cert)
|
||||
base64Key := base64.StdEncoding.EncodeToString(key)
|
||||
return base64Cert, base64Key, nil
|
||||
}
|
||||
|
||||
func createKubeConfig(username string) (string, error) {
|
||||
tmpKubeConfig := kubeConfig{ApiVersion: "v1", Kind: "Config"}
|
||||
serverCa, err := ioutil.ReadFile(caPath)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return "", err
|
||||
}
|
||||
base64ServerCa := base64.StdEncoding.EncodeToString(serverCa)
|
||||
tmpClusterInfo := clusterInfo{CertificateAuthorityData: base64ServerCa, Server: client.ClientSets().K8s().Master()}
|
||||
tmpCluster := cluster{Cluster: tmpClusterInfo, Name: clusterName}
|
||||
tmpKubeConfig.Clusters = append(tmpKubeConfig.Clusters, tmpCluster)
|
||||
|
||||
contextName := username + "@" + clusterName
|
||||
tmpContext := contextObject{Context: contextInfo{User: username, Cluster: clusterName, NameSpace: defaultNamespace}, Name: contextName}
|
||||
tmpKubeConfig.Contexts = append(tmpKubeConfig.Contexts, tmpContext)
|
||||
|
||||
cert, key, err := generateCaAndKey(username, caPath, keyPath)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tmpUser := user{User: userInfo{CaData: cert, KeyData: key}, Name: username}
|
||||
tmpKubeConfig.Users = append(tmpKubeConfig.Users, tmpUser)
|
||||
tmpKubeConfig.CurrentContext = contextName
|
||||
|
||||
config, err := yaml.Marshal(tmpKubeConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(config), nil
|
||||
}
|
||||
|
||||
func CreateKubeConfig(username string) error {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
configName := fmt.Sprintf("kubeconfig-%s", username)
|
||||
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
|
||||
|
||||
if errors.IsNotFound(err) {
|
||||
config, err := createKubeConfig(username)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
data := map[string]string{"config": config}
|
||||
configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: configName}, Data: data}
|
||||
_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap)
|
||||
if err != nil && !errors.IsAlreadyExists(err) {
|
||||
klog.Errorf("create username %s's kubeConfig failed, reason: %v", username, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func GetKubeConfig(username string) (string, error) {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
configName := fmt.Sprintf("kubeconfig-%s", username)
|
||||
configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.Errorf("cannot get username %s's kubeConfig, reason: %v", username, err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
str := configMap.Data[kubectlConfigKey]
|
||||
var kubeConfig kubeConfig
|
||||
err = yaml.Unmarshal([]byte(str), &kubeConfig)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return "", err
|
||||
}
|
||||
masterURL := client.ClientSets().K8s().Master()
|
||||
for i, cluster := range kubeConfig.Clusters {
|
||||
cluster.Cluster.Server = masterURL
|
||||
kubeConfig.Clusters[i] = cluster
|
||||
}
|
||||
data, err := yaml.Marshal(kubeConfig)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func DelKubeConfig(username string) error {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
configName := fmt.Sprintf("kubeconfig-%s", username)
|
||||
_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
deletePolicy := metaV1.DeletePropagationBackground
|
||||
err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(configName, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy})
|
||||
if err != nil {
|
||||
klog.Errorf("delete username %s's kubeConfig failed, reason: %v", username, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
func NewKubeconfigOperator() Interface {
|
||||
return &operator{}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ package kubectl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
@@ -40,6 +41,21 @@ const (
|
||||
namespace = constants.KubeSphereControlNamespace
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
GetKubectlPod(username string) (models.PodInfo, error)
|
||||
CreateKubectlDeploy(username string) error
|
||||
DelKubectlDeploy(username string) error
|
||||
}
|
||||
|
||||
type operator struct {
|
||||
k8sClient kubernetes.Interface
|
||||
informers informers.SharedInformerFactory
|
||||
}
|
||||
|
||||
func NewKubectlOperator(k8sClient kubernetes.Interface, informers informers.SharedInformerFactory) Interface {
|
||||
return &operator{k8sClient: k8sClient, informers: informers}
|
||||
}
|
||||
|
||||
var DefaultImage = "kubesphere/kubectl:advanced-1.0.0"
|
||||
|
||||
func init() {
|
||||
@@ -48,24 +64,23 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func GetKubectlPod(username string) (models.PodInfo, error) {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
func (o *operator) GetKubectlPod(username string) (models.PodInfo, error) {
|
||||
deployName := fmt.Sprintf("kubectl-%s", username)
|
||||
deploy, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{})
|
||||
deploy, err := o.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(deployName)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return models.PodInfo{}, err
|
||||
}
|
||||
|
||||
selectors := deploy.Spec.Selector.MatchLabels
|
||||
labelSelector := labels.Set(selectors).AsSelector().String()
|
||||
podList, err := k8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector})
|
||||
labelSelector := labels.Set(selectors).AsSelector()
|
||||
pods, err := o.informers.Core().V1().Pods().Lister().Pods(namespace).List(labelSelector)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return models.PodInfo{}, err
|
||||
}
|
||||
|
||||
pod, err := selectCorrectPod(namespace, podList.Items)
|
||||
pod, err := selectCorrectPod(namespace, pods)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return models.PodInfo{}, err
|
||||
@@ -77,9 +92,9 @@ func GetKubectlPod(username string) (models.PodInfo, error) {
|
||||
|
||||
}
|
||||
|
||||
func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err error) {
|
||||
func selectCorrectPod(namespace string, pods []*v1.Pod) (kubectlPod *v1.Pod, err error) {
|
||||
|
||||
var kubectlPodList []v1.Pod
|
||||
var kubectlPodList []*v1.Pod
|
||||
for _, pod := range pods {
|
||||
for _, condition := range pod.Status.Conditions {
|
||||
if condition.Type == "Ready" && condition.Status == "True" {
|
||||
@@ -89,15 +104,16 @@ func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err e
|
||||
}
|
||||
if len(kubectlPodList) < 1 {
|
||||
err = fmt.Errorf("cannot find valid kubectl pod in namespace:%s", namespace)
|
||||
return v1.Pod{}, err
|
||||
return &v1.Pod{}, err
|
||||
}
|
||||
|
||||
random := rand.Intn(len(kubectlPodList))
|
||||
|
||||
return kubectlPodList[random], nil
|
||||
}
|
||||
|
||||
func CreateKubectlDeploy(username string) error {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
func (o *operator) CreateKubectlDeploy(username string) error {
|
||||
k8sClient := o.k8sClient
|
||||
deployName := fmt.Sprintf("kubectl-%s", username)
|
||||
_, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
@@ -136,8 +152,8 @@ func CreateKubectlDeploy(username string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func DelKubectlDeploy(username string) error {
|
||||
k8sClient := client.ClientSets().K8s().Kubernetes()
|
||||
func (o *operator) DelKubectlDeploy(username string) error {
|
||||
k8sClient := o.k8sClient
|
||||
deployName := fmt.Sprintf("kubectl-%s", username)
|
||||
_, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
|
||||
Reference in New Issue
Block a user