feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
252
pkg/kapis/tenant/v1beta1/handler.go
Normal file
252
pkg/kapis/tenant/v1beta1/handler.go
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog/v2"
|
||||
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/models/tenant"
|
||||
servererr "kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/overview"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
tenant tenant.Interface
|
||||
auth authorizer.Authorizer
|
||||
counter overview.Counter
|
||||
client runtimeclient.Client
|
||||
}
|
||||
|
||||
func (h *handler) ListWorkspaces(req *restful.Request, resp *restful.Response) {
|
||||
queryParam := query.ParseQueryParameter(req)
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.tenant.ListWorkspaces(user, queryParam)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) GetWorkspace(request *restful.Request, response *restful.Response) {
|
||||
workspace, err := h.tenant.GetWorkspace(request.PathParameter("workspace"))
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(workspace)
|
||||
}
|
||||
|
||||
func (h *handler) CreateWorkspaceTemplate(req *restful.Request, resp *restful.Response) {
|
||||
var workspace tenantv1beta1.WorkspaceTemplate
|
||||
|
||||
err := req.ReadEntity(&workspace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
requestUser, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, req, err)
|
||||
}
|
||||
|
||||
created, err := h.tenant.CreateWorkspaceTemplate(requestUser, &workspace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(resp, req, err)
|
||||
return
|
||||
}
|
||||
if errors.IsForbidden(err) {
|
||||
api.HandleForbidden(resp, req, err)
|
||||
return
|
||||
}
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(created)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteWorkspaceTemplate(request *restful.Request, response *restful.Response) {
|
||||
workspace := request.PathParameter("workspace")
|
||||
|
||||
opts := metav1.DeleteOptions{}
|
||||
|
||||
err := request.ReadEntity(&opts)
|
||||
if err != nil {
|
||||
opts = *metav1.NewDeleteOptions(0)
|
||||
}
|
||||
|
||||
err = h.tenant.DeleteWorkspaceTemplate(workspace, opts)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(servererr.None)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateWorkspaceTemplate(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
var workspace tenantv1beta1.WorkspaceTemplate
|
||||
|
||||
err := req.ReadEntity(&workspace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
if workspaceName != workspace.Name {
|
||||
err := fmt.Errorf("the name of the object (%s) does not match the name on the URL (%s)", workspace.Name, workspaceName)
|
||||
klog.Errorf("%+v", err)
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
requestUser, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, req, err)
|
||||
}
|
||||
|
||||
updated, err := h.tenant.UpdateWorkspaceTemplate(requestUser, &workspace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(resp, req, err)
|
||||
return
|
||||
}
|
||||
if errors.IsBadRequest(err) {
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
if errors.IsForbidden(err) {
|
||||
api.HandleForbidden(resp, req, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(updated)
|
||||
}
|
||||
|
||||
func (h *handler) DescribeWorkspaceTemplate(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
workspace, err := h.tenant.DescribeWorkspaceTemplate(workspaceName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
response.WriteEntity(workspace)
|
||||
}
|
||||
|
||||
func (h *handler) PatchWorkspaceTemplate(req *restful.Request, resp *restful.Response) {
|
||||
workspaceName := req.PathParameter("workspace")
|
||||
var data json.RawMessage
|
||||
err := req.ReadEntity(&data)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
requestUser, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, req, err)
|
||||
}
|
||||
|
||||
patched, err := h.tenant.PatchWorkspaceTemplate(requestUser, workspaceName, data)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(resp, req, err)
|
||||
return
|
||||
}
|
||||
if errors.IsBadRequest(err) {
|
||||
api.HandleBadRequest(resp, req, err)
|
||||
return
|
||||
}
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleForbidden(resp, req, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(patched)
|
||||
}
|
||||
|
||||
func (h *handler) ListWorkspaceTemplates(req *restful.Request, resp *restful.Response) {
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
queryParam := query.ParseQueryParameter(req)
|
||||
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.tenant.ListWorkspaceTemplates(user, queryParam)
|
||||
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
309
pkg/kapis/tenant/v1beta1/register.go
Normal file
309
pkg/kapis/tenant/v1beta1/register.go
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
quotav1alpha2 "kubesphere.io/api/quota/v1alpha2"
|
||||
"kubesphere.io/api/tenant/v1beta1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/overview"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/rest"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/am"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/im"
|
||||
"kubesphere.io/kubesphere/pkg/models/tenant"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/utils/clusterclient"
|
||||
)
|
||||
|
||||
const (
|
||||
GroupName = "tenant.kubesphere.io"
|
||||
)
|
||||
|
||||
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
|
||||
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return GroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
func NewHandler(client runtimeclient.Client, k8sVersion *semver.Version, clusterClient clusterclient.Interface, am am.AccessManagementInterface,
|
||||
im im.IdentityManagementInterface, authorizer authorizer.Authorizer, counter overview.Counter) rest.Handler {
|
||||
return &handler{
|
||||
auth: authorizer,
|
||||
client: client,
|
||||
counter: counter,
|
||||
tenant: tenant.New(client, k8sVersion, clusterClient, am, im, authorizer),
|
||||
}
|
||||
}
|
||||
|
||||
func NewFakeHandler() rest.Handler {
|
||||
return &handler{}
|
||||
}
|
||||
|
||||
func (h *handler) AddToContainer(c *restful.Container) error {
|
||||
mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson}
|
||||
ws := runtime.NewWebService(GroupVersion)
|
||||
|
||||
ws.Route(ws.POST("/workspacetemplates").
|
||||
To(h.CreateWorkspaceTemplate).
|
||||
Doc("Create workspace template").
|
||||
Operation("create-workspace-template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.DELETE("/workspacetemplates/{workspace}").
|
||||
To(h.DeleteWorkspaceTemplate).
|
||||
Doc("Delete workspace template").
|
||||
Operation("delete-workspace-template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, errors.None))
|
||||
|
||||
ws.Route(ws.PUT("/workspacetemplates/{workspace}").
|
||||
To(h.UpdateWorkspaceTemplate).
|
||||
Doc("Update workspace template").
|
||||
Operation("update-workspace-template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.PATCH("/workspacetemplates/{workspace}").
|
||||
To(h.PatchWorkspaceTemplate).
|
||||
Consumes(mimePatch...).
|
||||
Doc("Patch workspace template").
|
||||
Operation("patch-workspace-template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.GET("/workspacetemplates").
|
||||
To(h.ListWorkspaceTemplates).
|
||||
Doc("List all workspace templates").
|
||||
Operation("list-workspace-templates").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/workspacetemplates/{workspace}").
|
||||
To(h.DescribeWorkspaceTemplate).
|
||||
Doc("Get workspace template").
|
||||
Operation("get-workspace-template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces").
|
||||
To(h.ListWorkspaces).
|
||||
Doc("List all workspaces").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}").
|
||||
To(h.GetWorkspace).
|
||||
Doc("Get workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, v1beta1.Workspace{}))
|
||||
|
||||
ws.Route(ws.GET("/clusters").
|
||||
To(h.ListClusters).
|
||||
Doc("List clusters available to users").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Operation("user-related-clusters").
|
||||
Returns(http.StatusOK, api.StatusOK, api.ListResult{}))
|
||||
|
||||
ws.Route(ws.POST("/workspaces").
|
||||
To(h.CreateWorkspaceTemplate).
|
||||
Doc("Create workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Operation("create-workspace").
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.DELETE("/workspaces/{workspace}").
|
||||
To(h.DeleteWorkspaceTemplate).
|
||||
Doc("Delete workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Operation("delete-workspace").
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, errors.None))
|
||||
|
||||
ws.Route(ws.PUT("/workspaces/{workspace}").
|
||||
To(h.UpdateWorkspaceTemplate).
|
||||
Doc("Update workspace").
|
||||
Operation("update-workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.PATCH("/workspaces/{workspace}").
|
||||
To(h.PatchWorkspaceTemplate).
|
||||
Consumes(mimePatch...).
|
||||
Reads(tenantv1beta1.WorkspaceTemplate{}).
|
||||
Doc("Update workspace").
|
||||
Operation("patch-workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces").
|
||||
To(h.ListWorkspaceTemplates).
|
||||
Doc("List workspaces").
|
||||
Operation("list-workspaces").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}").
|
||||
To(h.DescribeWorkspaceTemplate).
|
||||
Doc("Get workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Operation("get-workspace").
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, tenantv1beta1.WorkspaceTemplate{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/clusters").
|
||||
To(h.ListWorkspaceClusters).
|
||||
Doc("List clusters authorized to the specified workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, api.ListResult{}))
|
||||
|
||||
ws.Route(ws.GET("/namespaces").
|
||||
To(h.ListNamespaces).
|
||||
Doc("List the namespaces for the current user").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Operation("list-namespaces").
|
||||
Returns(http.StatusOK, api.StatusOK, api.ListResult{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
|
||||
To(h.ListNamespaces).
|
||||
Doc("List the namespaces in workspace").
|
||||
Operation("list-namespaces-workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, api.ListResult{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/namespaces/{namespace}").
|
||||
To(h.DescribeNamespace).
|
||||
Doc("Get namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("namespace", "The specified namespace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}))
|
||||
|
||||
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
|
||||
To(h.DeleteNamespace).
|
||||
Doc("Delete namespace from workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("namespace", "The specified namespace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, errors.None))
|
||||
|
||||
ws.Route(ws.POST("/workspaces/{workspace}/namespaces").
|
||||
To(h.CreateNamespace).
|
||||
Doc("Create namespace in workspace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Reads(corev1.Namespace{}).
|
||||
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/workspacemembers/{workspacemember}/namespaces").
|
||||
To(h.ListNamespaces).
|
||||
Doc("List namespaces in workspace of the member").
|
||||
Operation("list-namespaces-workspace-member").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("workspacemember", "workspacemember username")).
|
||||
Reads(corev1.Namespace{}).
|
||||
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}))
|
||||
|
||||
ws.Route(ws.PUT("/workspaces/{workspace}/namespaces/{namespace}").
|
||||
To(h.UpdateNamespace).
|
||||
Doc("Update namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Notes("Update namespace").
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("namespace", "The specified namespace.")).
|
||||
Reads(corev1.Namespace{}).
|
||||
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}))
|
||||
|
||||
ws.Route(ws.PATCH("/workspaces/{workspace}/namespaces/{namespace}").
|
||||
To(h.PatchNamespace).
|
||||
Consumes(mimePatch...).
|
||||
Doc("Patch namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Notes("Patch the specified namespace in workspace.").
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("namespace", "The specified namespace.")).
|
||||
Reads(corev1.Namespace{}).
|
||||
Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}))
|
||||
|
||||
ws.Route(ws.POST("/workspaces/{workspace}/resourcequotas").
|
||||
To(h.CreateWorkspaceResourceQuota).
|
||||
Doc("Create workspace resource quota").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Reads(quotav1alpha2.ResourceQuota{}).
|
||||
Returns(http.StatusOK, api.StatusOK, quotav1alpha2.ResourceQuota{}))
|
||||
|
||||
ws.Route(ws.DELETE("/workspaces/{workspace}/resourcequotas/{resourcequota}").
|
||||
To(h.DeleteWorkspaceResourceQuota).
|
||||
Doc("Delete workspace resource quota.").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("resourcequota", "resource quota name")).
|
||||
Returns(http.StatusOK, api.StatusOK, errors.None))
|
||||
|
||||
ws.Route(ws.PUT("/workspaces/{workspace}/resourcequotas/{resourcequota}").
|
||||
To(h.UpdateWorkspaceResourceQuota).
|
||||
Doc("Update workspace resource quota").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("resourcequota", "Resource quota name")).
|
||||
Reads(quotav1alpha2.ResourceQuota{}).
|
||||
Returns(http.StatusOK, api.StatusOK, quotav1alpha2.ResourceQuota{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/resourcequotas/{resourcequota}").
|
||||
To(h.DescribeWorkspaceResourceQuota).
|
||||
Doc("Get workspace resource quota").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Param(ws.PathParameter("resourcequota", "Resource quota name")).
|
||||
Returns(http.StatusOK, api.StatusOK, quotav1alpha2.ResourceQuota{}))
|
||||
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/metrics").
|
||||
To(h.GetWorkspaceMetrics).
|
||||
Doc("Get workspace metrics").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Param(ws.PathParameter("workspace", "The specified workspace.")).
|
||||
Returns(http.StatusOK, api.StatusOK, overview.MetricResults{}))
|
||||
|
||||
ws.Route(ws.GET("/metrics").
|
||||
To(h.GetPlatformMetrics).
|
||||
Doc("Get platform metrics").
|
||||
Metadata(restfulspec.KeyOpenAPITags, []string{api.TagUserRelatedResources}).
|
||||
Returns(http.StatusOK, api.StatusOK, overview.MetricResults{}))
|
||||
|
||||
c.Add(ws)
|
||||
return nil
|
||||
}
|
||||
409
pkg/kapis/tenant/v1beta1/resource_handler.go
Normal file
409
pkg/kapis/tenant/v1beta1/resource_handler.go
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/klog/v2"
|
||||
corev1alpha1 "kubesphere.io/api/core/v1alpha1"
|
||||
quotav1alpha2 "kubesphere.io/api/quota/v1alpha2"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
servererr "kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/overview"
|
||||
)
|
||||
|
||||
func (h *handler) ListNamespaces(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("workspace")
|
||||
queryParam := query.ParseQueryParameter(req)
|
||||
|
||||
var workspaceMember user.Info
|
||||
if username := req.PathParameter("workspacemember"); username != "" {
|
||||
workspaceMember = &user.DefaultInfo{
|
||||
Name: username,
|
||||
}
|
||||
} else {
|
||||
requestUser, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
workspaceMember = requestUser
|
||||
}
|
||||
|
||||
result, err := h.tenant.ListNamespaces(workspaceMember, workspace, queryParam)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) CreateNamespace(request *restful.Request, response *restful.Response) {
|
||||
workspace := request.PathParameter("workspace")
|
||||
var namespace corev1.Namespace
|
||||
|
||||
err := request.ReadEntity(&namespace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
created, err := h.tenant.CreateNamespace(workspace, &namespace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(created)
|
||||
}
|
||||
|
||||
func (h *handler) ListWorkspaceClusters(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
|
||||
result, err := h.tenant.ListWorkspaceClusters(workspaceName)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) DescribeNamespace(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
namespaceName := request.PathParameter("namespace")
|
||||
ns, err := h.tenant.DescribeNamespace(workspaceName, namespaceName)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(ns)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteNamespace(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
namespaceName := request.PathParameter("namespace")
|
||||
|
||||
err := h.tenant.DeleteNamespace(workspaceName, namespaceName)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(servererr.None)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateNamespace(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
namespaceName := request.PathParameter("namespace")
|
||||
|
||||
var namespace corev1.Namespace
|
||||
err := request.ReadEntity(&namespace)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
if namespaceName != namespace.Name {
|
||||
err := fmt.Errorf("the name of the object (%s) does not match the name on the URL (%s)", namespace.Name, namespaceName)
|
||||
klog.Errorf("%+v", err)
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
updated, err := h.tenant.UpdateNamespace(workspaceName, &namespace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
if errors.IsBadRequest(err) {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(updated)
|
||||
}
|
||||
|
||||
func (h *handler) PatchNamespace(request *restful.Request, response *restful.Response) {
|
||||
workspaceName := request.PathParameter("workspace")
|
||||
namespaceName := request.PathParameter("namespace")
|
||||
|
||||
var namespace corev1.Namespace
|
||||
err := request.ReadEntity(&namespace)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
namespace.Name = namespaceName
|
||||
|
||||
patched, err := h.tenant.PatchNamespace(workspaceName, &namespace)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, request, err)
|
||||
return
|
||||
}
|
||||
if errors.IsBadRequest(err) {
|
||||
api.HandleBadRequest(response, request, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(patched)
|
||||
}
|
||||
|
||||
func (h *handler) ListClusters(r *restful.Request, response *restful.Response) {
|
||||
user, ok := request.UserFrom(r.Request.Context())
|
||||
|
||||
if !ok {
|
||||
response.WriteEntity([]interface{}{})
|
||||
return
|
||||
}
|
||||
|
||||
queryParam := query.ParseQueryParameter(r)
|
||||
result, err := h.tenant.ListClusters(user, queryParam)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, r, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) CreateWorkspaceResourceQuota(r *restful.Request, response *restful.Response) {
|
||||
workspaceName := r.PathParameter("workspace")
|
||||
resourceQuota := "av1alpha2.ResourceQuota{}
|
||||
err := r.ReadEntity(resourceQuota)
|
||||
if err != nil {
|
||||
api.HandleBadRequest(response, r, err)
|
||||
return
|
||||
}
|
||||
result, err := h.tenant.CreateWorkspaceResourceQuota(workspaceName, resourceQuota)
|
||||
if err != nil {
|
||||
api.HandleInternalError(response, r, err)
|
||||
return
|
||||
}
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) DeleteWorkspaceResourceQuota(r *restful.Request, response *restful.Response) {
|
||||
workspace := r.PathParameter("workspace")
|
||||
resourceQuota := r.PathParameter("resourcequota")
|
||||
|
||||
if err := h.tenant.DeleteWorkspaceResourceQuota(workspace, resourceQuota); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, r, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(servererr.None)
|
||||
}
|
||||
|
||||
func (h *handler) UpdateWorkspaceResourceQuota(r *restful.Request, response *restful.Response) {
|
||||
workspaceName := r.PathParameter("workspace")
|
||||
resourceQuotaName := r.PathParameter("resourcequota")
|
||||
resourceQuota := "av1alpha2.ResourceQuota{}
|
||||
err := r.ReadEntity(resourceQuota)
|
||||
if err != nil {
|
||||
api.HandleBadRequest(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resourceQuotaName != resourceQuota.Name {
|
||||
err := fmt.Errorf("the name of the object (%s) does not match the name on the URL (%s)", resourceQuota.Name, resourceQuotaName)
|
||||
klog.Errorf("%+v", err)
|
||||
api.HandleBadRequest(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.tenant.UpdateWorkspaceResourceQuota(workspaceName, resourceQuota)
|
||||
if err != nil {
|
||||
api.HandleInternalError(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) DescribeWorkspaceResourceQuota(r *restful.Request, response *restful.Response) {
|
||||
workspaceName := r.PathParameter("workspace")
|
||||
resourceQuotaName := r.PathParameter("resourcequota")
|
||||
|
||||
resourceQuota, err := h.tenant.DescribeWorkspaceResourceQuota(workspaceName, resourceQuotaName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
api.HandleNotFound(response, r, err)
|
||||
return
|
||||
}
|
||||
api.HandleInternalError(response, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
response.WriteEntity(resourceQuota)
|
||||
}
|
||||
|
||||
func (h *handler) GetWorkspaceMetrics(req *restful.Request, resp *restful.Response) {
|
||||
workspace := req.PathParameter("workspace")
|
||||
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
parameter := query.ParseQueryParameter(req)
|
||||
prefix := "workspace"
|
||||
|
||||
namespaces, err := h.tenant.ListNamespaces(user, workspace, parameter)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
metrics, err := h.counter.GetMetrics([]string{overview.WorkspaceRoleBindingCount, overview.WorkspaceRoleCount},
|
||||
"", workspace, prefix)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
metrics.AddMetric(overview.CustomMetric(overview.NamespaceCount, prefix, namespaces.TotalItems))
|
||||
|
||||
_ = resp.WriteEntity(metrics)
|
||||
|
||||
}
|
||||
|
||||
func (h *handler) GetPlatformMetrics(req *restful.Request, resp *restful.Response) {
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
parameter := query.ParseQueryParameter(req)
|
||||
prefix := "platform"
|
||||
|
||||
metricNames := []string{overview.UserCount}
|
||||
metrics, err := h.counter.GetMetrics(metricNames, "", "", prefix)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check if the user has permission to visit extensions
|
||||
attr := authorizer.AttributesRecord{
|
||||
User: user,
|
||||
Verb: "list",
|
||||
APIGroup: corev1alpha1.GroupName,
|
||||
APIVersion: corev1alpha1.SchemeGroupVersion.Version,
|
||||
Resource: "installplans",
|
||||
ResourceRequest: true,
|
||||
ResourceScope: request.GlobalScope,
|
||||
}
|
||||
|
||||
decision, _, err := h.auth.Authorize(attr)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
if decision == authorizer.DecisionAllow {
|
||||
// get installed extension count
|
||||
installPlanList := &corev1alpha1.InstallPlanList{}
|
||||
err = h.client.List(req.Request.Context(), installPlanList)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
metrics.AddMetric(overview.CustomMetric(overview.InstallPlanCount, prefix, len(installPlanList.Items)))
|
||||
}
|
||||
|
||||
// get count of workspaces a tenant can access
|
||||
workspaces, err := h.tenant.ListWorkspaceTemplates(user, parameter)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
if workspaces.TotalItems != 0 {
|
||||
metrics.AddMetric(overview.CustomMetric(overview.WorkspaceCount, prefix, workspaces.TotalItems))
|
||||
}
|
||||
|
||||
// get count of clusters a tenant can access
|
||||
if parameter.LabelSelector == "" {
|
||||
parameter.LabelSelector = "kubesphere.io/managed=true"
|
||||
} else {
|
||||
parameter.LabelSelector = fmt.Sprintf("%s,%s", parameter.LabelSelector, "kubesphere.io/managed=true")
|
||||
}
|
||||
clusters, err := h.tenant.ListClusters(user, parameter)
|
||||
if err != nil {
|
||||
api.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
if clusters.TotalItems != 0 {
|
||||
metrics.AddMetric(overview.CustomMetric(overview.ClusterCount, prefix, clusters.TotalItems))
|
||||
}
|
||||
|
||||
_ = resp.WriteEntity(metrics)
|
||||
}
|
||||
Reference in New Issue
Block a user