diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 47925f5a..5c604834 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -4297,6 +4297,64 @@ const docTemplate = `{ } } }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get k8s resources for project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get k8s resources for project namespace", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Stack ID", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.GetProjectNamespaceK8sResourcesResponse" + } + } + } + } + }, "/organizations/{organizationId}/stacks": { "get": { "security": [ @@ -7413,6 +7471,14 @@ const docTemplate = `{ } } }, + "domain.GetProjectNamespaceK8sResourcesResponse": { + "type": "object", + "properties": { + "k8sResources": { + "$ref": "#/definitions/domain.ProjectNamespaceK8sResources" + } + } + }, "domain.GetProjectNamespaceResponse": { "type": "object", "properties": { @@ -7960,6 +8026,38 @@ const docTemplate = `{ } } }, + "domain.ProjectNamespaceK8sResources": { + "type": "object", + "properties": { + "cronjobs": { + "type": "integer" + }, + "demonsets": { + "type": "integer" + }, + "deployments": { + "type": "integer" + }, + "ingresses": { + "type": "integer" + }, + "jobs": { + "type": "integer" + }, + "pods": { + "type": "integer" + }, + "pvcs": { + "type": "integer" + }, + "services": { + "type": "integer" + }, + "statefulsets": { + "type": "integer" + } + } + }, "domain.ProjectNamespaceResponse": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index da3b1bc9..6cd5537e 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -4291,6 +4291,64 @@ } } }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get k8s resources for project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get k8s resources for project namespace", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Stack ID", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.GetProjectNamespaceK8sResourcesResponse" + } + } + } + } + }, "/organizations/{organizationId}/stacks": { "get": { "security": [ @@ -7407,6 +7465,14 @@ } } }, + "domain.GetProjectNamespaceK8sResourcesResponse": { + "type": "object", + "properties": { + "k8sResources": { + "$ref": "#/definitions/domain.ProjectNamespaceK8sResources" + } + } + }, "domain.GetProjectNamespaceResponse": { "type": "object", "properties": { @@ -7954,6 +8020,38 @@ } } }, + "domain.ProjectNamespaceK8sResources": { + "type": "object", + "properties": { + "cronjobs": { + "type": "integer" + }, + "demonsets": { + "type": "integer" + }, + "deployments": { + "type": "integer" + }, + "ingresses": { + "type": "integer" + }, + "jobs": { + "type": "integer" + }, + "pods": { + "type": "integer" + }, + "pvcs": { + "type": "integer" + }, + "services": { + "type": "integer" + }, + "statefulsets": { + "type": "integer" + } + } + }, "domain.ProjectNamespaceResponse": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index e3025176..ed26d241 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1385,6 +1385,11 @@ definitions: $ref: '#/definitions/domain.ProjectMemberResponse' type: array type: object + domain.GetProjectNamespaceK8sResourcesResponse: + properties: + k8sResources: + $ref: '#/definitions/domain.ProjectNamespaceK8sResources' + type: object domain.GetProjectNamespaceResponse: properties: projectNamespace: @@ -1750,6 +1755,27 @@ definitions: updatedAt: type: string type: object + domain.ProjectNamespaceK8sResources: + properties: + cronjobs: + type: integer + demonsets: + type: integer + deployments: + type: integer + ingresses: + type: integer + jobs: + type: integer + pods: + type: integer + pvcs: + type: integer + services: + type: integer + statefulsets: + type: integer + type: object domain.ProjectNamespaceResponse: properties: appCount: @@ -5118,6 +5144,44 @@ paths: summary: Check project namespace exist tags: - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources: + get: + consumes: + - application/json + description: Get k8s resources for project namespace + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Stack ID + in: path + name: stackId + required: true + type: string + - description: Project Namespace + in: path + name: projectNamespace + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.GetProjectNamespaceK8sResourcesResponse' + security: + - JWT: [] + summary: Get k8s resources for project namespace + tags: + - Projects /organizations/{organizationId}/projects/existence: get: consumes: diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index 390088ef..36edf66a 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -150,6 +150,7 @@ const ( UnSetFavoriteProject UnSetFavoriteProjectNamespace GetProjectKubeconfig + GetProjectNamespaceK8sResources // Audit GetAudits @@ -618,6 +619,10 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "GetProjectKubeconfig", Group: "Project", }, + GetProjectNamespaceK8sResources: { + Name: "GetProjectNamespaceK8sResources", + Group: "Project", + }, GetAudits: { Name: "GetAudits", Group: "Audit", @@ -864,6 +869,8 @@ func (e Endpoint) String() string { return "UnSetFavoriteProjectNamespace" case GetProjectKubeconfig: return "GetProjectKubeconfig" + case GetProjectNamespaceK8sResources: + return "GetProjectNamespaceK8sResources" case GetAudits: return "GetAudits" case GetAudit: @@ -1106,6 +1113,8 @@ func GetEndpoint(name string) Endpoint { return UnSetFavoriteProjectNamespace case "GetProjectKubeconfig": return GetProjectKubeconfig + case "GetProjectNamespaceK8sResources": + return GetProjectNamespaceK8sResources case "GetAudits": return GetAudits case "GetAudit": diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go index 55f3f5de..7e0cc416 100644 --- a/internal/delivery/http/project.go +++ b/internal/delivery/http/project.go @@ -49,6 +49,7 @@ type IProjectHandler interface { UnSetFavoriteProjectNamespace(w http.ResponseWriter, r *http.Request) GetProjectKubeconfig(w http.ResponseWriter, r *http.Request) + GetProjectNamespaceK8sResources(w http.ResponseWriter, r *http.Request) } type ProjectHandler struct { @@ -1498,3 +1499,55 @@ func (p ProjectHandler) GetProjectKubeconfig(w http.ResponseWriter, r *http.Requ ResponseJSON(w, r, http.StatusOK, out) } + +// GetProjectNamespaceK8sResources godoc +// @Tags Projects +// @Summary Get k8s resources for project namespace +// @Description Get k8s resources for project namespace +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param stackId path string true "Stack ID" +// @Param projectNamespace path string true "Project Namespace" +// @Success 200 {object} domain.GetProjectNamespaceK8sResourcesResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources [get] +// @Security JWT +func (p ProjectHandler) GetProjectNamespaceK8sResources(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("projectId not found in path"), "C_INVALID_PROJECT_ID", "")) + return + } + + projectNamespace, ok := vars["projectNamespace"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectNamespace"), "C_INVALID_PROJECT_NAMESPACE", "")) + return + } + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), "C_INVALID_STACK_ID", "")) + return + } + + k8sResources, err := p.usecase.GetK8sResources(r.Context(), organizationId, projectId, projectNamespace, stackId) + if err != nil { + log.ErrorWithContext(r.Context(), "Failed to get project resources.", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectNamespaceK8sResourcesResponse + if err = serializer.Map(k8sResources, &out.K8sResources); err != nil { + log.Error(err) + } + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/route/route.go b/internal/route/route.go index 7b0a7a14..cdb36272 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -218,6 +218,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role", customMiddleware.Handle(internalApi.UpdateProjectMemberRole, http.HandlerFunc(projectHandler.UpdateProjectMemberRole))).Methods(http.MethodPut) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces", customMiddleware.Handle(internalApi.CreateProjectNamespace, http.HandlerFunc(projectHandler.CreateProjectNamespace))).Methods(http.MethodPost) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.IsProjectNamespaceExist))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources", customMiddleware.Handle(internalApi.GetProjectNamespaceK8sResources, http.HandlerFunc(projectHandler.GetProjectNamespaceK8sResources))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces", customMiddleware.Handle(internalApi.GetProjectNamespaces, http.HandlerFunc(projectHandler.GetProjectNamespaces))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.GetProjectNamespace))).Methods(http.MethodGet) r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}", customMiddleware.Handle(internalApi.UpdateProjectNamespace, http.HandlerFunc(projectHandler.UpdateProjectNamespace))).Methods(http.MethodPut) diff --git a/internal/usecase/project.go b/internal/usecase/project.go index 67f9f476..bc85e1bd 100644 --- a/internal/usecase/project.go +++ b/internal/usecase/project.go @@ -1,6 +1,8 @@ package usecase import ( + "context" + "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/kubernetes" @@ -49,6 +51,7 @@ type IProjectUsecase interface { CreateK8SNSRoleBinding(organizationId string, projectId string, stackId string, namespace string) error DeleteK8SNSRoleBinding(organizationId string, projectId string, stackId string, namespace string) error GetProjectKubeconfig(organizationId string, projectId string) (string, error) + GetK8sResources(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) (out domain.ProjectNamespaceK8sResources, err error) AssignKeycloakClientRoleToMember(organizationId string, projectId string, stackId string, projectMemberId string) error UnassignKeycloakClientRoleToMember(organizationId string, projectId string, stackId string, projectMemberId string) error @@ -670,3 +673,19 @@ func (u *ProjectUsecase) GetProjectKubeconfig(organizationId string, projectId s return kubernetes.MergeKubeconfigsWithSingleUser(kubeconfigs) } + +func (u *ProjectUsecase) GetK8sResources(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) (out domain.ProjectNamespaceK8sResources, err error) { + + // to be implemented + out.Pods = 1 + out.Deployments = 2 + out.Statefulsets = 3 + out.Demonsets = 4 + out.Jobs = 5 + out.Cronjobs = 6 + out.PVCs = 7 + out.Services = 8 + out.Ingresses = 9 + + return +} diff --git a/pkg/domain/project.go b/pkg/domain/project.go index 10e5d9a4..3fc02157 100644 --- a/pkg/domain/project.go +++ b/pkg/domain/project.go @@ -254,3 +254,19 @@ type UpdateProjectNamespaceRequest struct { type GetProjectKubeconfigResponse struct { Kubeconfig string `json:"kubeconfig"` } + +type ProjectNamespaceK8sResources struct { + Pods int `json:"pods"` + Deployments int `json:"deployments"` + Statefulsets int `json:"statefulsets"` + Demonsets int `json:"demonsets"` + Jobs int `json:"jobs"` + Cronjobs int `json:"cronjobs"` + PVCs int `json:"pvcs"` + Services int `json:"services"` + Ingresses int `json:"ingresses"` +} + +type GetProjectNamespaceK8sResourcesResponse struct { + K8sResources ProjectNamespaceK8sResources `json:"k8sResources"` +}