diff --git a/pkg/apiserver/openpitrix/attachments.go b/pkg/apiserver/openpitrix/attachments.go deleted file mode 100644 index 908a95df0..000000000 --- a/pkg/apiserver/openpitrix/attachments.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - -package openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "kubesphere.io/kubesphere/pkg/models/openpitrix/attachment" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func DescribeAttachment(req *restful.Request, resp *restful.Response) { - attachmentId := req.PathParameter("attachment") - fileName := req.QueryParameter("filename") - result, err := attachment.DescribeAttachment(attachmentId) - // file raw - if fileName != "" { - data := result.AttachmentContent[fileName] - resp.Write(data) - resp.Header().Set("Content-Type", "text/plain") - return - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/resources/user.go b/pkg/apiserver/resources/user.go deleted file mode 100644 index 81175ff1f..000000000 --- a/pkg/apiserver/resources/user.go +++ /dev/null @@ -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)) -} diff --git a/pkg/apiserver/tenant/tenant.go b/pkg/apiserver/tenant/tenant.go deleted file mode 100644 index 71d3f47a6..000000000 --- a/pkg/apiserver/tenant/tenant.go +++ /dev/null @@ -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 -} diff --git a/pkg/kapis/devops/v1alpha2/register.go b/pkg/kapis/devops/v1alpha2/register.go index f491aa955..c6430212a 100644 --- a/pkg/kapis/devops/v1alpha2/register.go +++ b/pkg/kapis/devops/v1alpha2/register.go @@ -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 diff --git a/pkg/kapis/resources/v1alpha2/handler.go b/pkg/kapis/resources/v1alpha2/handler.go index 29be473fa..5e7d48035 100644 --- a/pkg/kapis/resources/v1alpha2/handler.go +++ b/pkg/kapis/resources/v1alpha2/handler.go @@ -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)) } diff --git a/pkg/kapis/resources/v1alpha2/register.go b/pkg/kapis/resources/v1alpha2/register.go index 30ea37fa9..62452b3af 100644 --- a/pkg/kapis/resources/v1alpha2/register.go +++ b/pkg/kapis/resources/v1alpha2/register.go @@ -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")). diff --git a/pkg/models/iam/am.go b/pkg/models/iam/am.go index 01b8c5343..75a45ce57 100644 --- a/pkg/models/iam/am.go +++ b/pkg/models/iam/am.go @@ -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{} diff --git a/pkg/models/kubeconfig/kubeconfig.go b/pkg/models/kubeconfig/kubeconfig.go index e1a2bd8ed..6eacf850e 100644 --- a/pkg/models/kubeconfig/kubeconfig.go +++ b/pkg/models/kubeconfig/kubeconfig.go @@ -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{} } diff --git a/pkg/models/kubectl/kubectl.go b/pkg/models/kubectl/kubectl.go index f37567d1d..5d91cf80d 100644 --- a/pkg/models/kubectl/kubectl.go +++ b/pkg/models/kubectl/kubectl.go @@ -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) {