diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8644715..1ce1986a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,14 +11,16 @@ env: TAG: ${{github.sha}} jobs: - build-and-push-image: + build-image: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub uses: docker/login-action@v1 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5df838e9..f889b7a3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,27 +17,19 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.51.0 - args: --timeout=5m - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the action will use pre-installed Go. - # skip-go-installation: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true + go-version: "1.21" + cache: false + - name: Install golangci-lint + # Install golangci-lint from source instead of using + # golangci-lint-action to ensure the golangci-lint binary is built with + # the same Go version we're targeting. + # Avoids incompatibility issues such as: + # - https://github.com/golangci/golangci-lint/issues/2922 + # - https://github.com/golangci/golangci-lint/issues/2673 + # - https://github.com/golangci/golangci-lint-action/issues/442 + run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.2 + - name: Run golangci-lint + run: golangci-lint run --verbose --out-format=github-actions diff --git a/.gitignore b/.gitignore index 7da128bd..6c2919aa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ web main output vendor +go.work +go.work.sum diff --git a/Dockerfile b/Dockerfile index f1d1e192..3de05bf6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,13 @@ # Build - backend -FROM --platform=linux/amd64 docker.io/library/golang:1.18-buster AS backend-build +FROM --platform=linux/amd64 docker.io/library/golang:1.21 AS backend-build RUN DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata -RUN wget https://github.com/swaggo/swag/releases/download/v1.7.1/swag_linux_amd64.tar.gz -O - | tar -xz -C /tmp && cp /tmp/swag_linux_amd64/swag /usr/local/bin +RUN wget https://github.com/swaggo/swag/releases/download/v1.16.3/swag_1.16.3_Linux_amd64.tar.gz -O - | tar -xz -C /tmp && cp /tmp/swag /usr/local/bin WORKDIR /app/backend COPY ./ . + RUN go mod tidy +RUN swag init -g ./cmd/server/main.go --parseDependency --parseInternal -o ./api/swagger RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/server ./cmd/server/main.go ENV TZ=Asia/Seoul diff --git a/Makefile b/Makefile index 390be8d4..40fe7741 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ .PHONY: docs docs: - swag init -g ./cmd/server/main.go -o ./api/swagger + swag init -g ./cmd/server/main.go --parseDependency --parseInternal -o ./api/swagger + swag fmt .PHONY: build build: @@ -17,6 +18,7 @@ test: .PHONY: dev_run dev_run: - swag init -g ./cmd/server/main.go -o ./api/swagger + swag init -g ./cmd/server/main.go --parseDependency --parseInternal -o ./api/swagger + swag fmt go build ./cmd/server/main.go ./main diff --git a/api/swagger/docs.go b/api/swagger/docs.go index ff2be9e4..42716929 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -1,5 +1,4 @@ -// Package swagger GENERATED BY SWAG; DO NOT EDIT -// This file was generated by swaggo/swag +// Package swagger Code generated by swaggo/swag. DO NOT EDIT package swagger import "github.com/swaggo/swag" @@ -23,14 +22,14 @@ const docTemplate = `{ "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/app-groups": { + "/admin/audits": { "get": { "security": [ { "JWT": [] } ], - "description": "Get appGroup list by giving params", + "description": "Get Audits", "consumes": [ "application/json" ], @@ -38,26 +37,20 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Get appGroup list", + "summary": "Get Audits", "parameters": [ - { - "type": "string", - "description": "clusterId", - "name": "clusterId", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -77,8 +70,19 @@ const docTemplate = `{ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", - "name": "filters", + "name": "filter", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "or", "in": "query" } ], @@ -86,7 +90,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAppGroupsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse" } } } @@ -97,7 +101,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Install appGroup", + "description": "Create Audit", "consumes": [ "application/json" ], @@ -105,17 +109,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Install appGroup", + "summary": "Create Audit", "parameters": [ { - "description": "create appgroup request", + "description": "create audit request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateAppGroupRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest" } } ], @@ -123,18 +127,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateAppGroupResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse" } } } - }, - "delete": { + } + }, + "/admin/audits/{auditId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Uninstall appGroup", + "description": "Get Audit", "consumes": [ "application/json" ], @@ -142,35 +148,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Uninstall appGroup", + "summary": "Get Audit", "parameters": [ { - "description": "body", - "name": "object", - "in": "body", - "required": true, - "schema": { - "type": "string" - } + "type": "string", + "description": "auditId", + "name": "auditId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse" + } } } - } - }, - "/app-groups/{appGroupId}": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get appGroup detail by appGroupId", + "description": "Delete Audit", "consumes": [ "application/json" ], @@ -178,36 +183,33 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Get appGroup detail", + "summary": "Delete Audit 'NOT IMPLEMENTED'", "parameters": [ { "type": "string", - "description": "appGroupId", - "name": "appGroupId", + "description": "auditId", + "name": "auditId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetAppGroupResponse" - } + "description": "OK" } } } }, - "/app-groups/{appGroupId}/applications": { - "get": { + "/admin/organizations": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get applications", + "description": "Create organization in Admin portal", "consumes": [ "application/json" ], @@ -215,41 +217,38 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "Organizations" ], - "summary": "Get applications", + "summary": "Create organization in Admin portal", "parameters": [ { - "type": "string", - "description": "appGroupId", - "name": "appGroupId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "applicationType", - "name": "applicationType", - "in": "query", - "required": true + "description": "create organization request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetApplicationsResponse" + "type": "object" } } } - }, - "post": { + } + }, + "/admin/organizations/{organizationId}/policyTemplates": { + "put": { "security": [ { "JWT": [] } ], - "description": "Create application", + "description": "특정 조직에 대해 허용된 tks 템플릿의 허용 상태를 해제(제거)한다. tks 우형 템플릿에 대해서만 동작하고 organization 유형 템플릿에 대해서는 거부된다.", "consumes": [ "application/json" ], @@ -257,17 +256,24 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppGroups" + "PolicyTemplate" ], - "summary": "Create application", + "summary": "[Admin_DeletePermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 제거", "parameters": [ { - "description": "body", - "name": "object", + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "delete pemitted policy template request", + "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateApplicationRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest" } } ], @@ -276,11 +282,14 @@ const docTemplate = `{ "description": "OK" } } - } - }, - "/auth/find-id/code": { + }, "post": { - "description": "This API allows users to verify their identity for lost id by submitting required information", + "security": [ + { + "JWT": [] + } + ], + "description": "특정 조직에 대해 허용된 tks 템플릿 목록을 추가한다.", "consumes": [ "application/json" ], @@ -288,39 +297,42 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Auth" + "PolicyTemplate" ], - "summary": "Request to verify identity for lost id", + "summary": "[Admin_AddPermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 추가", "parameters": [ { - "description": "Request body for verifying identity for lost id including {organization ID, email, username}", + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update pemitted policy template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostIdRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostIdResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" - } + "description": "OK" } } } }, - "/auth/find-id/verification": { - "post": { - "description": "This API allows users to find their account ID by submitting required information", + "/admin/organizations/{organizationId}/projects": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects as admin", "consumes": [ "application/json" ], @@ -328,116 +340,117 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Auth" + "Projects" ], - "summary": "Request to find forgotten ID", + "summary": "Get projects as admin", "parameters": [ { - "description": "Request body for finding the account ID including {organization ID, email, username, 6 digit code}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FindIdRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Name", + "name": "projectName", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.FindIdResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse" } } } } }, - "/auth/find-password/code": { - "post": { - "description": "This API allows users to verify their identity for lost password by submitting required information", - "consumes": [ - "application/json" + "/admin/organizations/{organizationId}/roles": { + "get": { + "security": [ + { + "JWT": [] + } ], + "description": "Admin List Tks Roles", "produces": [ "application/json" ], "tags": [ - "Auth" + "Roles" ], - "summary": "Request to verify identity for lost password", + "summary": "Admin List Tks Roles", "parameters": [ { - "description": "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostPasswordRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostPasswordResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse" } } } } }, - "/auth/find-password/verification": { - "post": { - "description": "This API allows users to reset their forgotten password by submitting required information", - "consumes": [ - "application/json" + "/admin/organizations/{organizationId}/roles/{roleId}": { + "get": { + "security": [ + { + "JWT": [] + } ], + "description": "Admin Get Tks Role", "produces": [ "application/json" ], "tags": [ - "Auth" + "Roles" ], - "summary": "Request to find forgotten password", + "summary": "Admin Get Tks Role", "parameters": [ { - "description": "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FindPasswordRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request", + "description": "OK", "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" } } } } }, - "/auth/login": { + "/admin/organizations/{organizationId}/users": { "post": { - "description": "login", + "security": [ + { + "JWT": [] + } + ], + "description": "Create user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -445,38 +458,45 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "login", + "summary": "Create user by admin in Admin Portal", "parameters": [ { - "description": "account info", + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create user request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.LoginRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest" } } ], "responses": { "200": { - "description": "user detail", + "description": "create user response", "schema": { - "$ref": "#/definitions/domain.LoginResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse" } } } } }, - "/auth/logout": { - "post": { + "/admin/organizations/{organizationId}/users/{accountId}": { + "put": { "security": [ { "JWT": [] } ], - "description": "logout", + "description": "Update user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -484,22 +504,50 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "logout", - "responses": { - "200": { - "description": "OK", + "summary": "Update user by admin in Admin Portal", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/domain.LogoutResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse" } } } - } - }, - "/auth/ping": { - "post": { - "description": "ping with token", + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -507,18 +555,32 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "ping with token", + "summary": "Delete user by admin in Admin Portal", "parameters": [ { - "description": "token info", + "description": "input", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.PingTokenRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest" } + }, + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true } ], "responses": { @@ -528,14 +590,14 @@ const docTemplate = `{ } } }, - "/clusters": { + "/admin/policy-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster list", + "description": "정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다.", "consumes": [ "application/json" ], @@ -543,32 +605,26 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get clusters", + "summary": "[Admin_ListPolicyTemplate] 정책 템플릿 목록 조회", "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { "type": "string", "description": "sortColumn", - "name": "soertColumn", + "name": "sortColumn", "in": "query" }, { @@ -582,6 +638,7 @@ const docTemplate = `{ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -591,7 +648,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetClustersResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse" } } } @@ -602,7 +659,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Create cluster", + "description": "정책 템플릿을 신규 생성(v1.0.0을 생성)한다.", "consumes": [ "application/json" ], @@ -610,17 +667,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Create cluster", + "summary": "[Admin_CreatePolicyTemplate] 정책 템플릿 신규 생성", "parameters": [ { - "description": "create cluster request", + "description": "create policy template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateClusterRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest" } } ], @@ -628,20 +685,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateClusterResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse" } } } } }, - "/clusters/import": { - "post": { + "/admin/policy-templates/kind/{policyTemplateKind}/existence": { + "get": { "security": [ { "JWT": [] } ], - "description": "Import cluster", + "description": "해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다.", "consumes": [ "application/json" ], @@ -649,38 +706,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Import cluster", + "summary": "[Admin_ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인", "parameters": [ { - "description": "import cluster request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ImportClusterRequest" - } + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateKind", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.ImportClusterResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse" } } } } }, - "/clusters/{clusterId}": { + "/admin/policy-templates/name/{policyTemplateName}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster detail", + "description": "해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다.", "consumes": [ "application/json" ], @@ -688,14 +743,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get cluster", + "summary": "[Admin_ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 이름", + "name": "policyTemplateName", "in": "path", "required": true } @@ -704,18 +759,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Cluster" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse" } } } - }, - "delete": { + } + }, + "/admin/policy-templates/{policyTemplateId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete cluster", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", "consumes": [ "application/json" ], @@ -723,14 +780,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Delete cluster", + "summary": "[Admin_GetPolicyTemplate] 정책 템플릿 조회(최신 버전)", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -739,20 +796,18 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Cluster" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse" } } } - } - }, - "/clusters/{clusterId}/bootstrap-kubeconfig": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get bootstrap kubeconfig for BYOH", + "description": "정책 템플릿을 삭제한다.", "consumes": [ "application/json" ], @@ -760,53 +815,31 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get bootstrap kubeconfig for BYOH", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetBootstrapKubeconfigResponse" - } - } - } - }, - "post": { - "security": [ + "summary": "[Admin_DeletePolicyTemplate] 정책 템플릿 삭제", + "parameters": [ { - "JWT": [] + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true } ], - "description": "Create bootstrap kubeconfig for BYOH", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Clusters" - ], - "summary": "Create bootstrap kubeconfig for BYOH", "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateBootstrapKubeconfigResponse" - } + "description": "OK" } } - } - }, - "/clusters/{clusterId}/install": { - "post": { + }, + "patch": { "security": [ { "JWT": [] } ], - "description": "Install cluster on tks cluster", + "description": "정책 템플릿의 업데이트 가능한 필드들을 업데이트한다.", "consumes": [ "application/json" ], @@ -814,16 +847,25 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Install cluster on tks cluster", + "summary": "[Admin_UpdatePolicyTemplate] 정책 템플릿 업데이트", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true + }, + { + "description": "update policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest" + } } ], "responses": { @@ -833,14 +875,14 @@ const docTemplate = `{ } } }, - "/clusters/{clusterId}/nodes": { + "/admin/policy-templates/{policyTemplateId}/deploy": { "get": { "security": [ { "JWT": [] } ], - "description": "Get nodes information for BYOH", + "description": "해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다.", "consumes": [ "application/json" ], @@ -848,14 +890,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get nodes information for BYOH", + "summary": "[Admin_GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -864,20 +906,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetClusterNodesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse" } } } } }, - "/clusters/{clusterId}/site-values": { + "/admin/policy-templates/{policyTemplateId}/statistics": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster site values for creating", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다.", "consumes": [ "application/json" ], @@ -885,14 +927,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get cluster site values for creating", + "summary": "[Admin_ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -901,20 +943,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.ClusterSiteValuesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse" } } } } }, - "/organizations": { + "/admin/policy-templates/{policyTemplateId}/versions": { "get": { "security": [ { "JWT": [] } ], - "description": "Get organization list", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", "consumes": [ "application/json" ], @@ -922,52 +964,23 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Get organization list", + "summary": "[Admin_ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회", "parameters": [ { "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", - "in": "query" - }, - { - "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" - }, - { - "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ListOrganizationBody" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse" } } } @@ -978,7 +991,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Create organization", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다.", "consumes": [ "application/json" ], @@ -986,17 +999,24 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Create organization", + "summary": "[Admin_CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장", "parameters": [ { - "description": "create organization request", + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "create policy template version request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateOrganizationRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest" } } ], @@ -1004,20 +1024,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "object" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse" } } } } }, - "/organizations/{organizationId}": { + "/admin/policy-templates/{policyTemplateId}/versions/{version}": { "get": { "security": [ { "JWT": [] } ], - "description": "Get organization detail", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다.", "consumes": [ "application/json" ], @@ -1025,14 +1045,21 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Get organization detail", + "summary": "[Admin_GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "조회할 버전(v0.0.0 형식)", + "name": "version", "in": "path", "required": true } @@ -1041,18 +1068,18 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetOrganizationResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse" } } } }, - "put": { + "delete": { "security": [ { "JWT": [] } ], - "description": "Update organization detail", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다.", "consumes": [ "application/json" ], @@ -1060,43 +1087,40 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Update organization detail", + "summary": "[Admin_DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true }, { - "description": "update organization request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateOrganizationRequest" - } + "type": "string", + "description": "삭제할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.UpdateOrganizationResponse" - } + "description": "OK" } } - }, - "delete": { + } + }, + "/admin/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters": { + "post": { "security": [ { "JWT": [] } ], - "description": "Delete organization", + "description": "정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다.", "consumes": [ "application/json" ], @@ -1104,36 +1128,52 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Delete organization", + "summary": "[Admin_ExtractParameters] 정책 템플릿 파라미터 추출", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "버전(v0.0.0 형식)", + "name": "version", "in": "path", "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Organization" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse" } } } } }, - "/organizations/{organizationId}/alerts": { + "/admin/stack-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Alerts", + "description": "Get StackTemplates", "consumes": [ "application/json" ], @@ -1141,27 +1181,20 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Get Alerts", + "summary": "Get StackTemplates", "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -1181,6 +1214,7 @@ const docTemplate = `{ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -1190,20 +1224,57 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAlertsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Create StackTemplate", + "parameters": [ + { + "description": "create stack template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse" } } } } }, - "/organizations/{organizationId}/alerts/{alertId}": { + "/admin/stack-templates/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Alert", + "description": "Check name for stackTemplate", "consumes": [ "application/json" ], @@ -1211,21 +1282,107 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Get Alert", + "summary": "Check name for stackTemplate", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true - }, + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse" + } + } + } + } + }, + "/admin/stack-templates/services": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get GetStackTemplateServices", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get GetStackTemplateServices", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse" + } + } + } + } + }, + "/admin/stack-templates/template-ids": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get GetStackTemplateTemplateIds", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get GetStackTemplateTemplateIds", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse" + } + } + } + } + }, + "/admin/stack-templates/{stackTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get StackTemplate", + "parameters": [ { "type": "string", - "description": "alertId", - "name": "alertId", + "description": "stackTemplateId", + "name": "stackTemplateId", "in": "path", "required": true } @@ -1234,7 +1391,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAlertResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse" } } } @@ -1245,7 +1402,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Update Alert", + "description": "Update StackTemplate", "consumes": [ "application/json" ], @@ -1253,24 +1410,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Update Alert", + "summary": "Update StackTemplate", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Update cloud setting request", + "description": "Update stack template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAlertRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest" } } ], @@ -1286,7 +1436,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Delete Alert", + "description": "Delete StackTemplate", "consumes": [ "application/json" ], @@ -1294,21 +1444,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Delete Alert", + "summary": "Delete StackTemplate", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "alertId", - "name": "alertId", + "description": "stackTemplateId", + "name": "stackTemplateId", "in": "path", "required": true } @@ -1320,14 +1463,14 @@ const docTemplate = `{ } } }, - "/organizations/{organizationId}/alerts/{alertId}/actions": { - "post": { + "/admin/stack-templates/{stackTemplateId}/organizations": { + "put": { "security": [ { "JWT": [] } ], - "description": "Create alert action", + "description": "Update StackTemplate organizations", "consumes": [ "application/json" ], @@ -1335,16 +1478,18 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Create alert action", + "summary": "Update StackTemplate organizations", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true + "description": "Update stack template organizations request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest" + } } ], "responses": { @@ -1354,14 +1499,14 @@ const docTemplate = `{ } } }, - "/organizations/{organizationId}/app-serve-apps": { + "/admin/system-notification-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp list by giving params", + "description": "Get SystemNotificationTemplates", "consumes": [ "application/json" ], @@ -1369,33 +1514,20 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get appServeApp list", + "summary": "Get SystemNotificationTemplates", "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "boolean", - "description": "Show all apps including deleted apps", - "name": "showAll", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -1415,6 +1547,7 @@ const docTemplate = `{ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -1424,10 +1557,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AppServeApp" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse" } } } @@ -1438,7 +1568,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Install appServeApp", + "description": "Create alert template. ADMIN ONLY", "consumes": [ "application/json" ], @@ -1446,45 +1576,27 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" - ], - "summary": "Install appServeApp", - "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Request body to create app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateAppServeAppRequest" - } - } + "SystemNotificationTemplates" ], + "summary": "Create alert template. ADMIN ONLY", "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest" } } } } }, - "/organizations/{organizationId}/app-serve-apps/count": { + "/admin/system-notification-templates/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get number of apps on given stack", + "description": "Check name for systemNotificationTemplate", "consumes": [ "application/json" ], @@ -1492,43 +1604,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get number of apps on given stack", + "summary": "Check name for systemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true - }, - { - "type": "string", - "description": "Stack ID", - "name": "stackId", - "in": "query", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "integer" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse" } } } } }, - "/organizations/{organizationId}/app-serve-apps/name/{name}/existence": { + "/admin/system-notification-templates/{systemNotificationTemplateId}": { "get": { "security": [ { "JWT": [] } ], - "description": "Check duplicate appServeAppName by giving params", + "description": "Get SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1536,21 +1641,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Check duplicate appServeAppName", + "summary": "Get SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true } @@ -1559,20 +1657,18 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "boolean" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}": { - "get": { + }, + "put": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp by giving params", + "description": "Update SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1580,41 +1676,40 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get appServeApp", + "summary": "Update SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true }, { - "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true + "description": "Update alert template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest" + } } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetAppServeAppResponse" - } + "description": "OK" } } }, - "put": { + "delete": { "security": [ { "JWT": [] } ], - "description": "Update appServeApp", + "description": "Delete SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1622,50 +1717,33 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Update appServeApp", + "summary": "Delete SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true - }, - { - "description": "Request body to update app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppRequest" - } } ], "responses": { "200": { - "description": "OK", - "schema": { - "type": "string" - } + "description": "OK" } } - }, - "delete": { + } + }, + "/app-groups": { + "get": { "security": [ { "JWT": [] } ], - "description": "Uninstall appServeApp", + "description": "Get appGroup list by giving params", "consumes": [ "application/json" ], @@ -1673,43 +1751,67 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Uninstall appServeApp", + "summary": "Get appGroup list", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true + "description": "clusterId", + "name": "clusterId", + "in": "query" }, { "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/endpoint": { - "patch": { + }, + "post": { "security": [ { "JWT": [] } ], - "description": "Update app endpoint", + "description": "Install appGroup", "consumes": [ "application/json" ], @@ -1717,31 +1819,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Update app endpoint", + "summary": "Install appGroup", "parameters": [ { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "appId", - "name": "appId", - "in": "path", - "required": true - }, - { - "description": "Request body to update app endpoint", + "description": "create appgroup request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppEndpointRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest" } } ], @@ -1749,20 +1837,18 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/exist": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp by giving params", + "description": "Uninstall appGroup", "consumes": [ "application/json" ], @@ -1770,27 +1856,35 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Get appServeApp", - "responses": { - "200": { - "description": "OK", + "summary": "Uninstall appGroup", + "parameters": [ + { + "description": "body", + "name": "object", + "in": "body", + "required": true, "schema": { - "type": "boolean" + "type": "string" } } + ], + "responses": { + "200": { + "description": "OK" + } } } }, - "/organizations/{organizationId}/app-serve-apps/{appId}/latest-task": { + "/app-groups/{appGroupId}": { "get": { "security": [ { "JWT": [] } ], - "description": "Get latest task from appServeApp", + "description": "Get appGroup detail by appGroupId", "consumes": [ "application/json" ], @@ -1798,21 +1892,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Get latest task from appServeApp", + "summary": "Get appGroup detail", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", + "description": "appGroupId", + "name": "appGroupId", "in": "path", "required": true } @@ -1821,20 +1908,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAppServeAppTaskResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse" } } } } }, - "/organizations/{organizationId}/app-serve-apps/{appId}/rollback": { - "post": { + "/app-groups/{appGroupId}/applications": { + "get": { "security": [ { "JWT": [] } ], - "description": "Rollback appServeApp", + "description": "Get applications", "consumes": [ "application/json" ], @@ -1842,52 +1929,41 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Rollback appServeApp", + "summary": "Get applications", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "appGroupId", + "name": "appGroupId", "in": "path", "required": true }, { "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", + "description": "applicationType", + "name": "applicationType", + "in": "query", "required": true - }, - { - "description": "Request body to rollback app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RollbackAppServeAppRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/status": { - "patch": { + }, + "post": { "security": [ { "JWT": [] } ], - "description": "Update app status", + "description": "Create application", "consumes": [ "application/json" ], @@ -1895,52 +1971,30 @@ const docTemplate = `{ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Update app status", + "summary": "Create application", "parameters": [ { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true - }, - { - "description": "Request body to update app status", - "name": "body", + "description": "body", + "name": "object", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppStatusRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "type": "string" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/cloud-accounts": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Get CloudAccounts", + "/auth/find-id/code": { + "post": { + "description": "This API allows users to verify their identity for lost id by submitting required information", "consumes": [ "application/json" ], @@ -1948,67 +2002,39 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Get CloudAccounts", + "summary": "Request to verify identity for lost id", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", - "in": "query" - }, - { - "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" - }, - { - "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "Request body for verifying identity for lost id including {organization ID, email, username}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetCloudAccountsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } - }, + } + }, + "/auth/find-id/verification": { "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Create CloudAccount", + "description": "This API allows users to find their account ID by submitting required information", "consumes": [ "application/json" ], @@ -2016,24 +2042,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Create CloudAccount", + "summary": "Request to find forgotten ID", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "create cloud setting request", + "description": "Request body for finding the account ID including {organization ID, email, username, 6 digit code}", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdRequest" } } ], @@ -2041,20 +2060,21 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateCloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } } }, - "/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Check awsAccountId for cloudAccount", + "/auth/find-password/code": { + "post": { + "description": "This API allows users to verify their identity for lost password by submitting required information", "consumes": [ "application/json" ], @@ -2062,43 +2082,76 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Check awsAccountId for cloudAccount", + "summary": "Request to verify identity for lost password", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "awsAccountId", - "name": "awsAccountId", - "in": "path", - "required": true + "description": "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckCloudAccountAwsAccountIdResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } } }, - "/organizations/{organizationId}/cloud-accounts/name/{name}/existence": { - "get": { - "security": [ + "/auth/find-password/verification": { + "post": { + "description": "This API allows users to reset their forgotten password by submitting required information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Request to find forgotten password", + "parameters": [ { - "JWT": [] + "description": "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest" + } } ], - "description": "Check name for cloudAccount", + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "login", "consumes": [ "application/json" ], @@ -2106,43 +2159,59 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Check name for cloudAccount", + "summary": "login", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", - "in": "path", - "required": true + "description": "account info", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginRequest" + } } ], "responses": { "200": { - "description": "OK", + "description": "user detail", "schema": { - "$ref": "#/definitions/domain.CheckCloudAccountNameResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginResponse" } } } } }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}": { + "/auth/logout": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "logout", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "logout", + "responses": {} + } + }, + "/clusters": { "get": { "security": [ { "JWT": [] } ], - "description": "Get CloudAccount", + "description": "Get cluster list", "consumes": [ "application/json" ], @@ -2150,41 +2219,67 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Get CloudAccount", + "summary": "Get clusters", "parameters": [ { "type": "string", "description": "organizationId", "name": "organizationId", - "in": "path", - "required": true + "in": "query" }, { "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetCloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse" } } } }, - "put": { + "post": { "security": [ { "JWT": [] } ], - "description": "Update CloudAccount", + "description": "Create cluster", "consumes": [ "application/json" ], @@ -2192,40 +2287,38 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Update CloudAccount", + "summary": "Create cluster", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Update cloud setting request", + "description": "create cluster request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse" + } } } - }, - "delete": { + } + }, + "/clusters/import": { + "post": { "security": [ { "JWT": [] } ], - "description": "Delete CloudAccount", + "description": "Import cluster", "consumes": [ "application/json" ], @@ -2233,49 +2326,38 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Delete CloudAccount", + "summary": "Import cluster", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Delete cloud setting request", + "description": "import cluster request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.DeleteCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest" } - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", - "in": "path", - "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse" + } } } } }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error": { - "delete": { + "/clusters/{clusterId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete Force CloudAccount", + "description": "Get cluster detail", "consumes": [ "application/json" ], @@ -2283,40 +2365,34 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Delete Force CloudAccount", + "summary": "Get cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse" + } } } - } - }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get resource quota by cloudAccount", + "description": "Delete cluster", "consumes": [ "application/json" ], @@ -2324,43 +2400,33 @@ const docTemplate = `{ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Get resource quota by cloudAccount", + "summary": "Delete cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCloudAccountResourceQuotaResponse" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/dashboard/charts": { + "/clusters/{clusterId}/bootstrap-kubeconfig": { "get": { "security": [ { "JWT": [] } ], - "description": "Get charts data", + "description": "Get bootstrap kubeconfig for BYOH", "consumes": [ "application/json" ], @@ -2368,56 +2434,53 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get charts data", - "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "chartType", - "name": "chartType", - "in": "query" - }, - { - "type": "string", - "description": "duration", - "name": "duration", - "in": "query", - "required": true - }, + "summary": "Get bootstrap kubeconfig for BYOH", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse" + } + } + } + }, + "post": { + "security": [ { - "type": "string", - "description": "interval", - "name": "interval", - "in": "query", - "required": true + "JWT": [] } ], + "description": "Create bootstrap kubeconfig for BYOH", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Clusters" + ], + "summary": "Create bootstrap kubeconfig for BYOH", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardChartsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse" } } } } }, - "/organizations/{organizationId}/dashboard/charts/{chartType}": { - "get": { + "/clusters/{clusterId}/install": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get chart data", + "description": "Install cluster on tks cluster", "consumes": [ "application/json" ], @@ -2425,57 +2488,33 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get chart data", + "summary": "Install cluster on tks cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "chartType", - "name": "chartType", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true - }, - { - "type": "string", - "description": "duration", - "name": "duration", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "interval", - "name": "interval", - "in": "query", - "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetDashboardChartResponse" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/dashboard/resources": { + "/clusters/{clusterId}/nodes": { "get": { "security": [ { "JWT": [] } ], - "description": "Get resources", + "description": "Get nodes information for BYOH", "consumes": [ "application/json" ], @@ -2483,14 +2522,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get resources", + "summary": "Get nodes information for BYOH", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } @@ -2499,20 +2538,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardResourcesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse" } } } } }, - "/organizations/{organizationId}/dashboard/stacks": { + "/clusters/{clusterId}/site-values": { "get": { "security": [ { "JWT": [] } ], - "description": "Get stacks", + "description": "Get cluster site values for creating", "consumes": [ "application/json" ], @@ -2520,14 +2559,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get stacks", + "summary": "Get cluster site values for creating", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } @@ -2536,20 +2575,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardStacksResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse" } } } } }, - "/organizations/{organizationId}/my-profile": { + "/organizations": { "get": { "security": [ { "JWT": [] } ], - "description": "Get my profile detail", + "description": "Get organization list", "consumes": [ "application/json" ], @@ -2557,34 +2596,66 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Get my profile detail", + "summary": "Get organization list", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetMyProfileResponse" + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse" + } } } } - }, - "put": { + } + }, + "/organizations/name/{name}/existence": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update my profile detail", + "description": "Check name for organization", "consumes": [ "application/json" ], @@ -2592,58 +2663,14 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update my profile detail", + "summary": "Check name for organization", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Required fields: password due to double-check", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateMyProfileRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.UpdateMyProfileResponse" - } - } - } - }, - "delete": { - "security": [ - { - "JWT": [] - } - ], - "description": "Delete myProfile", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "My-profile" - ], - "summary": "Delete myProfile", - "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true } @@ -2651,21 +2678,18 @@ const docTemplate = `{ "responses": { "200": { "description": "OK" - }, - "400": { - "description": "Bad Request" } } } }, - "/organizations/{organizationId}/my-profile/next-password-change": { - "put": { + "/organizations/{organizationId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update user's password expired date to current date", + "description": "Get organization detail", "consumes": [ "application/json" ], @@ -2673,9 +2697,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update user's password expired date to current date", + "summary": "Get organization detail", "parameters": [ { "type": "string", @@ -2687,25 +2711,20 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request", + "description": "OK", "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse" } } } - } - }, - "/organizations/{organizationId}/my-profile/password": { + }, "put": { "security": [ { "JWT": [] } ], - "description": "Update user password detail", + "description": "Update organization detail", "consumes": [ "application/json" ], @@ -2713,9 +2732,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update user password detail", + "summary": "Update organization detail", "parameters": [ { "type": "string", @@ -2725,30 +2744,31 @@ const docTemplate = `{ "required": true }, { - "description": "update user password request", + "description": "update organization request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdatePasswordRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse" + } } } - } - }, - "/organizations/{organizationId}/primary-cluster": { - "patch": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Update primary cluster", + "description": "Delete organization", "consumes": [ "application/json" ], @@ -2758,7 +2778,7 @@ const docTemplate = `{ "tags": [ "Organizations" ], - "summary": "Update primary cluster", + "summary": "Delete organization", "parameters": [ { "type": "string", @@ -2766,32 +2786,26 @@ const docTemplate = `{ "name": "organizationId", "in": "path", "required": true - }, - { - "description": "update primary cluster request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdatePrimaryClusterRequest" - } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse" + } } } } }, - "/organizations/{organizationId}/stacks": { + "/organizations/{organizationId}/cloud-accounts": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Stacks", + "description": "Get CloudAccounts", "consumes": [ "application/json" ], @@ -2799,9 +2813,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get Stacks", + "summary": "Get CloudAccounts", "parameters": [ { "type": "string", @@ -2813,13 +2827,13 @@ const docTemplate = `{ { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -2835,9 +2849,13 @@ const docTemplate = `{ "in": "query" }, { - "type": "string", - "description": "combinedFilter", - "name": "combinedFilter", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", "in": "query" } ], @@ -2845,7 +2863,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStacksResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse" } } } @@ -2856,7 +2874,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Create Stack", + "description": "Create CloudAccount", "consumes": [ "application/json" ], @@ -2864,9 +2882,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Create Stack", + "summary": "Create CloudAccount", "parameters": [ { "type": "string", @@ -2881,7 +2899,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateStackRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest" } } ], @@ -2889,20 +2907,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateStackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse" } } } } }, - "/organizations/{organizationId}/stacks/name/{name}/existence": { + "/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Check name for stack", + "description": "Check awsAccountId for cloudAccount", "consumes": [ "application/json" ], @@ -2910,9 +2928,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Check name for stack", + "summary": "Check awsAccountId for cloudAccount", "parameters": [ { "type": "string", @@ -2923,34 +2941,30 @@ const docTemplate = `{ }, { "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", + "description": "awsAccountId", + "name": "awsAccountId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse" + } } } } }, - "/organizations/{organizationId}/stacks/{stackId}": { + "/organizations/{organizationId}/cloud-accounts/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Stack", + "description": "Check name for cloudAccount", "consumes": [ "application/json" ], @@ -2958,9 +2972,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get Stack", + "summary": "Check name for cloudAccount", "parameters": [ { "type": "string", @@ -2971,8 +2985,8 @@ const docTemplate = `{ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "name", + "name": "name", "in": "path", "required": true } @@ -2981,18 +2995,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse" } } } - }, - "put": { + } + }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update Stack", + "description": "Get CloudAccount", "consumes": [ "application/json" ], @@ -3000,9 +3016,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Update Stack", + "summary": "Get CloudAccount", "parameters": [ { "type": "string", @@ -3013,34 +3029,28 @@ const docTemplate = `{ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true - }, - { - "description": "Update cloud setting request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateStackRequest" - } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse" + } } } }, - "delete": { + "put": { "security": [ { "JWT": [] } ], - "description": "Delete Stack", + "description": "Update CloudAccount", "consumes": [ "application/json" ], @@ -3048,9 +3058,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Delete Stack", + "summary": "Update CloudAccount", "parameters": [ { "type": "string", @@ -3060,11 +3070,13 @@ const docTemplate = `{ "required": true }, { - "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest" + } } ], "responses": { @@ -3072,16 +3084,14 @@ const docTemplate = `{ "description": "OK" } } - } - }, - "/organizations/{organizationId}/stacks/{stackId}/favorite": { - "post": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Set favorite stack", + "description": "Delete CloudAccount", "consumes": [ "application/json" ], @@ -3089,9 +3099,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Set favorite stack", + "summary": "Delete CloudAccount", "parameters": [ { "type": "string", @@ -3100,10 +3110,19 @@ const docTemplate = `{ "in": "path", "required": true }, + { + "description": "Delete cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest" + } + }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3113,14 +3132,16 @@ const docTemplate = `{ "description": "OK" } } - }, + } + }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error": { "delete": { "security": [ { "JWT": [] } ], - "description": "Delete favorite stack", + "description": "Delete Force CloudAccount", "consumes": [ "application/json" ], @@ -3128,9 +3149,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Delete favorite stack", + "summary": "Delete Force CloudAccount", "parameters": [ { "type": "string", @@ -3141,8 +3162,8 @@ const docTemplate = `{ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3154,14 +3175,14 @@ const docTemplate = `{ } } }, - "/organizations/{organizationId}/stacks/{stackId}/kube-config": { + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { "get": { "security": [ { "JWT": [] } ], - "description": "Get KubeConfig by stack", + "description": "Get resource quota by cloudAccount", "consumes": [ "application/json" ], @@ -3169,9 +3190,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get KubeConfig by stack", + "summary": "Get resource quota by cloudAccount", "parameters": [ { "type": "string", @@ -3182,8 +3203,8 @@ const docTemplate = `{ }, { "type": "string", - "description": "organizationId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3192,20 +3213,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackKubeConfigResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse" } } } } }, - "/organizations/{organizationId}/stacks/{stackId}/status": { - "get": { + "/organizations/{organizationId}/dashboards": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get Stack Status", + "description": "Create new dashboard", "consumes": [ "application/json" ], @@ -3213,43 +3234,45 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Stacks" + "Dashboards" ], - "summary": "Get Stack Status", + "summary": "Create new dashboard", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true }, { - "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true + "description": "Request body to create dashboard", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackStatusResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse" } } } } }, - "/organizations/{organizationId}/users": { + "/organizations/{organizationId}/dashboards/widgets/charts": { "get": { "security": [ { "JWT": [] } ], - "description": "Get user list", + "description": "Get charts data", "consumes": [ "application/json" ], @@ -3257,9 +3280,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user list", + "summary": "Get charts data", "parameters": [ { "type": "string", @@ -3270,57 +3293,43 @@ const docTemplate = `{ }, { "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", + "description": "chartType", + "name": "chartType", "in": "query" }, { "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" + "description": "duration", + "name": "duration", + "in": "query", + "required": true }, { "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "interval", + "name": "interval", + "in": "query", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ListUserBody" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse" } } } - }, - "post": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/charts/{chartType}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create user", + "description": "Get chart data", "consumes": [ "application/json" ], @@ -3328,9 +3337,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Create user", + "summary": "Get chart data", "parameters": [ { "type": "string", @@ -3339,54 +3348,25 @@ const docTemplate = `{ "in": "path", "required": true }, - { - "description": "create user request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateUserRequest" - } - } - ], - "responses": { - "200": { - "description": "create user response", - "schema": { - "$ref": "#/definitions/domain.CreateUserResponse" - } - } - } - } - }, - "/organizations/{organizationId}/users/account-id/{accountId}/existence": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "return true when accountId exists", - "produces": [ - "application/json" - ], - "tags": [ - "Users" - ], - "summary": "Get user id existence", - "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "chartType", + "name": "chartType", "in": "path", "required": true }, { "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", "required": true } ], @@ -3394,61 +3374,57 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckExistedResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse" } } } } }, - "/organizations/{organizationId}/users/email/{email}/existence": { + "/organizations/{organizationId}/dashboards/widgets/policy-enforcement": { "get": { "security": [ { "JWT": [] } ], - "description": "return true when email exists", + "description": "Get the number of policy enforcement", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user email existence", + "summary": "Get the number of policy enforcement", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "email", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckExistedResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse" } } } } }, - "/organizations/{organizationId}/users/{accountId}": { + "/organizations/{organizationId}/dashboards/widgets/policy-statistics": { "get": { "security": [ { "JWT": [] } ], - "description": "Get user detail", + "description": "Get policy violation log", "consumes": [ "application/json" ], @@ -3456,41 +3432,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user detail", + "summary": "Get policy violation log", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse" } } } - }, - "put": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-status": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update user", + "description": "Get policy status", "consumes": [ "application/json" ], @@ -3498,50 +3469,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Update user", + "summary": "Get policy status", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true - }, - { - "description": "input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateUserRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.UpdateUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse" } } } - }, - "delete": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-update": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete user", + "description": "Get the number of policytemplates that need to be updated", "consumes": [ "application/json" ], @@ -3549,43 +3506,36 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Delete user", + "summary": "Get the number of policytemplates that need to be updated", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.User" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse" } } } } }, - "/organizations/{organizationId}/users/{accountId}/reset-password": { - "put": { + "/organizations/{organizationId}/dashboards/widgets/policy-violation": { + "get": { "security": [ { "JWT": [] } ], - "description": "Reset user's password as temporary password by admin and send email to user", + "description": "Get the number of policy violation", "consumes": [ "application/json" ], @@ -3593,40 +3543,50 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Reset user's password as temporary password by admin", + "summary": "Get the number of policy violation", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true }, { "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse" + } } } } }, - "/stack-templates": { + "/organizations/{organizationId}/dashboards/widgets/policy-violation-log": { "get": { "security": [ { "JWT": [] } ], - "description": "Get StackTemplates", + "description": "Get policy violation log", "consumes": [ "application/json" ], @@ -3634,20 +3594,27 @@ const docTemplate = `{ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Get StackTemplates", + "summary": "Get policy violation log", "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -3667,6 +3634,7 @@ const docTemplate = `{ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -3676,18 +3644,20 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackTemplatesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" } } } - }, - "post": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-violation-top5": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create StackTemplate", + "description": "Get policy violation top5", "consumes": [ "application/json" ], @@ -3695,38 +3665,50 @@ const docTemplate = `{ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Create StackTemplate 'NOT IMPLEMENTED'", + "summary": "Get policy violation top5", "parameters": [ { - "description": "create stack template request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateStackTemplateRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateStackTemplateResponse" - } - } - } - } - }, - "/stack-templates/{stackTemplateId}": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Get StackTemplate", + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/resources": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resources", "consumes": [ "application/json" ], @@ -3734,14 +3716,132 @@ const docTemplate = `{ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Get StackTemplate", + "summary": "Get resources", "parameters": [ { "type": "string", - "description": "stackTemplateId", - "name": "stackTemplateId", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/stacks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get stacks", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboard Widgets" + ], + "summary": "Get stacks", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/workload": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get workloads", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboard Widgets" + ], + "summary": "Get workloads", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/{dashboardKey}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get dashboard", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboards" + ], + "summary": "Get dashboard", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Dashboard Key", + "name": "dashboardKey", "in": "path", "required": true } @@ -3750,7 +3850,10 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackTemplateResponse" + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse" + } } } } @@ -3761,7 +3864,7 @@ const docTemplate = `{ "JWT": [] } ], - "description": "Update StackTemplate", + "description": "Update dashboard", "consumes": [ "application/json" ], @@ -3769,33 +3872,80 @@ const docTemplate = `{ "application/json" ], "tags": [ - "StackTemplates" + "Dashboards" ], - "summary": "Update StackTemplate 'NOT IMPLEMENTED'", + "summary": "Update dashboard", "parameters": [ { - "description": "Update stack template request", - "name": "body", + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Request body to update dashboard", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateStackTemplateRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse" + } + } + } + } + }, + "/organizations/{organizationId}/mandatory-policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse" + } } } }, - "delete": { + "patch": { "security": [ { "JWT": [] } ], - "description": "Delete StackTemplate", + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", "consumes": [ "application/json" ], @@ -3803,16 +3953,25 @@ const docTemplate = `{ "application/json" ], "tags": [ - "StackTemplates" + "Policy" ], - "summary": "Delete StackTemplate 'NOT IMPLEMENTED'", + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", "parameters": [ { "type": "string", - "description": "stackTemplateId", - "name": "stackTemplateId", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", "in": "path", "required": true + }, + { + "description": "update mandatory policy/policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest" + } } ], "responses": { @@ -3822,14 +3981,14 @@ const docTemplate = `{ } } }, - "/system-api/organizations/{organizationId}/alerts": { - "post": { + "/organizations/{organizationId}/my-profile": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create alert. ADMIN ONLY", + "description": "Get my profile detail", "consumes": [ "application/json" ], @@ -3837,9 +3996,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Alerts" + "My-profile" ], - "summary": "Create alert. ADMIN ONLY", + "summary": "Get my profile detail", "parameters": [ { "type": "string", @@ -3851,236 +4010,9367 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse" + } } } - } - } - }, - "definitions": { - "domain.ActionResponse": { - "type": "object", - "properties": { - "body": { - "type": "object", - "additionalProperties": { - "type": "string" + }, + "put": { + "security": [ + { + "JWT": [] } - }, - "method": { - "type": "string" - }, - "name": { - "description": "ENDPOINT (화면보기), PREVIEW (미리보기), PROMOTE (배포), ABORT (중단)", - "type": "string" - }, - "type": { - "description": "LINK, API", - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, - "domain.AlertActionResponse": { - "type": "object", - "properties": { - "alertId": { + ], + "description": "Update my profile detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update my profile detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Required fields: password due to double-check", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete myProfile", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Delete myProfile", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request" + } + } + } + }, + "/organizations/{organizationId}/my-profile/next-password-change": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user's password expired date to current date", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user's password expired date to current date", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/organizations/{organizationId}/my-profile/password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user password detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user password detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update user password request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ListPolicy] 정책 목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "filledParameter", + "name": "filledParameter", + "in": "query" + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[CreatePolicy] 정책 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/name/{policyName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyName] 정책 이름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 이름", + "name": "policyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/resource-name/{policyResourceName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 자원 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyResourceName] 정책 자원 이름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 자원 이름(쿠버네티스 배포 시 자원 이름)", + "name": "policyResourceName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePolicy] 정책 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[UpdatePolicy] 정책을 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}/clusters": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}/edit": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications/{policyNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "policyNotificationId", + "name": "policyNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책의 통계를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicyStatistics] 정책 템플릿, 정책 통계 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplate] 정책 템플릿 목록 조회", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + }, + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿을 신규 생성(v1.0.0을 생성)한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CreatePolicyTemplate] 정책 템플릿 신규 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/kind/{policyTemplateKind}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateKind", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/name/{policyTemplateName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplate] 정책 템플릿 조회(최신 버전)", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿을 삭제한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[DeletePolicyTemplate] 정책 템플릿 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿의 업데이트 가능한 필드들을 업데이트한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[UpdatePolicyTemplate] 정책 템플릿 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "update policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/deploy": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "create policy template version request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "조회할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "삭제할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExtractParameters] 정책 템플릿 파라미터 추출", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse" + } + } + } + } + }, + "/organizations/{organizationId}/primary-cluster": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update primary cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organizations" + ], + "summary": "Update primary cluster", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update primary cluster request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/projects": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get projects", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "(all | only)", + "name": "query", + "in": "query" + }, + { + "type": "string", + "description": "Project Name", + "name": "projectName", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create new project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Create new project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Request body to create project", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check project name exist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Check project name exist", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "type (name)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "value (project name)", + "name": "value", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/project-roles": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project roles by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project roles", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "project role search by query (query=all), (query=leader), (query=member), (query=viewer)", + "name": "query", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/project-roles/{projectRoleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project role by id", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Role ID", + "name": "projectRoleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get projects", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeApp list by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeApp list", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Show all apps including deleted apps", + "name": "showAll", + "in": "query" + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp" + } + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Install appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Install appServeApp", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to create app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/count": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get number of apps on given stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get number of apps on given stack", + "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": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "integer" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check duplicate appServeAppName by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Check duplicate appServeAppName", + "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": "name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Uninstall appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Uninstall appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/endpoint": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update app endpoint", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update app endpoint", + "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": "appId", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app endpoint", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/exist": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeApp by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeApp", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/latest-task": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get latest task from appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get latest task from appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/rollback": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Rollback appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Rollback appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to rollback app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/status": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update app status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update app status", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app status", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeAppTask list by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeAppTask list", + "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": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp" + } + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks/{taskId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get task detail from appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get task detail from appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID", + "name": "taskId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project kubeconfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project kubeconfig", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project members", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project members", + "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": "project member search by query (query=all), (query=leader), (query=member), (query=viewer)", + "name": "query", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project member Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project member Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project member role", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add project member to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Add project member to project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to add project member", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove project members to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Remove project members to project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to remove project member", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/count": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project member count group by project role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project member count group by project role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project member", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project member", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove project members to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Remove project members to project", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project member Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project member Role", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project member role", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespaces", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project namespaces", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Create 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 + }, + { + "description": "Request body to create project namespace", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get 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": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Stack ID", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update 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": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Stack ID", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project namespace", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Delete 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/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check project namespace exist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Check project namespace exist", + "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": "Project 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/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespace kubeconfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project namespace kubeconfig", + "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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/resources-usage": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resources usage for project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get resources usage 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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse" + } + } + } + } + }, + "/organizations/{organizationId}/roles": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "List Tks Roles", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List Tks Roles", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create Tks Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Create Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Create Tks Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Tks Role", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Tks Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Update Tks Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete Tks Role", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Delete Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Role ID", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Permissions By Role ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Update Permissions By Role ID Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}/users": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Users By Role ID", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Users By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Append Users To Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Append Users To Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Append Users To Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove Users From Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Remove Users From Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Remove Users From Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check whether the role name exists", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Check whether the role name exists", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role Name", + "name": "roleName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stack-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization StackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get Organization StackTemplates", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove organization stackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Remove organization stackTemplates", + "parameters": [ + { + "description": "Remove organization stack templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add organization stackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Add organization stackTemplates", + "parameters": [ + { + "description": "Add organization stack templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stack-templates/{stackTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get Organization StackTemplate", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stacks", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stacks", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Create Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check name for stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Check name for stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Update Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Delete Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/favorite": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Set favorite stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Set favorite stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete favorite stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Delete favorite stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get KubeConfig by stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get KubeConfig by stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "organizationId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policies": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "특정 스택에서 정책 식별자로 지정된 정책을 제거한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePoliciesForStack] 특정 스택의 정책 제거", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "delete policies for stack request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest" + } + } + ], + "responses": {} + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "특정 스택의 정책 목록을 정책 식별자 리스트로 지정해서 추가한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[AddPoliciesForStack] 특정 스택의 정책 목록 추가", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "add policies for stack request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policy-status": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿의 클러스터 버전 등 상태를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[GetStackPolicyTemplateStatus] 클러스터 템플릿 상태 상세 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 템플릿의 버전 업데이트 및 연관된 정책의 새 기본값을 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[UpdateStackPolicyTemplateStatus] 템플릿 버전 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "update stack policy template status request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/status": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stack Status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stack Status", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotificationRules", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Get SystemNotificationRules", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Create SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create stack template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/default-system-rules": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "MakeDefaultSystemNotificationRules", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "MakeDefaultSystemNotificationRules", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check name for systemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Check name for systemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Get SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Update SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + }, + { + "description": "Update systemNotificationRule request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Delete SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization SystemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Get Organization SystemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove organization systemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Remove organization systemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Remove organization systemNotification templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add organization systemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Add organization systemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Add organization systemNotification templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notification-templates/{systemNotificationTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization SystemNotificationTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Get Organization SystemNotificationTemplate", + "parameters": [ + { + "type": "string", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Get SystemNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notifications/{systemNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Get SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Update SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + }, + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Delete SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notifications/{systemNotificationId}/actions": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create systemNotification action", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Create systemNotification action", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/users": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get user list", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user list", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListUserBody" + } + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update multiple users", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update multiple users", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest" + } + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create user request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "create user response", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/account-id/{accountId}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "return true when accountId exists", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user id existence", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/email/{email}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "return true when email exists", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user email existence", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "email", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get user detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUserResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Account ID", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get Permissions By Account ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}/reset-password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Reset user's password as temporary password by admin and send email to user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Reset user's password as temporary password by admin", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/permissions/templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permission Templates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Permission" + ], + "summary": "Get Permission Templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse" + } + } + } + } + }, + "/policy-templates/rego-compile": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Rego 코드 컴파일 및 파라미터 파싱을 수행한다. 파라미터 파싱을 위해서는 먼저 컴파일이 성공해야 하며, parseParameter를 false로 하면 컴파일만 수행할 수 있다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CompileRego] Rego 코드 컴파일 및 파라미터 파싱", + "parameters": [ + { + "type": "boolean", + "description": "파라미터 파싱 여부", + "name": "parseParameter", + "in": "query", + "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse" + } + } + } + } + }, + "/system-api/organizations/{organizationId}/system-notifications": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create systemNotification. ADMIN ONLY", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Create systemNotification. ADMIN ONLY", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "definitions": { + "github_com_openinfradev_tks-api_internal_model.AppServeApp": { + "type": "object", + "properties": { + "appType": { + "description": "appType (spring/springboot)", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "description": "description for application", + "type": "string" + }, + "endpointUrl": { + "description": "endpoint URL of deployed app", + "type": "string" + }, + "grafanaUrl": { + "description": "grafana dashboard URL for deployed app", + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "description": "application name", + "type": "string" + }, + "namespace": { + "description": "application namespace", + "type": "string" + }, + "organizationId": { + "description": "contractId is a contract ID which this app belongs to", + "type": "string" + }, + "previewEndpointUrl": { + "description": "preview svc endpoint URL in B/G deployment", + "type": "string" + }, + "projectId": { + "description": "project ID which this app belongs to", + "type": "string" + }, + "status": { + "description": "status is status of deployed app", + "type": "string" + }, + "targetClusterId": { + "description": "target cluster to which the app is deployed", + "type": "string" + }, + "targetClusterName": { + "description": "target cluster name", + "type": "string" + }, + "type": { + "description": "type (build/deploy/all)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ActionResponse": { + "type": "object", + "properties": { + "body": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "method": { + "type": "string" + }, + "name": { + "description": "ENDPOINT (화면보기), PREVIEW (미리보기), PROMOTE (배포), ABORT (중단)", + "type": "string" + }, + "type": { + "description": "LINK, API", + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest": { + "type": "object", + "required": [ + "stackTemplateIds" + ], + "properties": { + "stackTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest": { + "type": "object", + "required": [ + "systemNotificationTemplateIds" + ], + "properties": { + "systemNotificationTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest": { + "type": "object", + "properties": { + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "adminPassword": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest": { + "type": "object", + "required": [ + "email", + "roles" + ], + "properties": { + "adminPassword": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse": { + "type": "object", + "properties": { + "appGroupType": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupType" + }, + "clusterId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "workflowId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "x-enum-varnames": [ + "AppGroupStatus_PENDING", + "AppGroupStatus_INSTALLING", + "AppGroupStatus_RUNNING", + "AppGroupStatus_DELETING", + "AppGroupStatus_DELETED", + "AppGroupStatus_INSTALL_ERROR", + "AppGroupStatus_DELETE_ERROR" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "AppGroupType_UNSPECIFIED", + "AppGroupType_LMA", + "AppGroupType_SERVICE_MESH" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse": { + "type": "object", + "properties": { + "appType": { + "description": "appType (spring/springboot)", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "description": "description for application", + "type": "string" + }, + "endpointUrl": { + "description": "endpoint URL of deployed app", + "type": "string" + }, + "grafanaUrl": { + "description": "grafana dashboard URL for deployed app", + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "description": "application name", + "type": "string" + }, + "namespace": { + "description": "application namespace", + "type": "string" + }, + "organizationId": { + "description": "contractId is a contract ID which this app belongs to", + "type": "string" + }, + "previewEndpointUrl": { + "description": "preview svc endpoint URL in B/G deployment", + "type": "string" + }, + "projectId": { + "description": "project ID which this app belongs to", + "type": "string" + }, + "status": { + "description": "status is status of deployed app", + "type": "string" + }, + "targetClusterId": { + "description": "target cluster to which the app is deployed", + "type": "string" + }, + "targetClusterName": { + "description": "target cluster name", + "type": "string" + }, + "type": { + "description": "type (build/deploy/all)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse": { + "type": "object", + "properties": { + "appConfig": { + "description": "java app config", + "type": "string" + }, + "appSecret": { + "description": "java app secret", + "type": "string" + }, + "appServeAppId": { + "description": "ID for appServeApp that this task belongs to", + "type": "string" + }, + "artifactUrl": { + "description": "URL of java app artifact (Eg, Jar)", + "type": "string" + }, + "availableRollback": { + "type": "boolean" + }, + "createdAt": { + "description": "createdAt is a creation timestamp for the application", + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "executablePath": { + "description": "Executable path of app image", + "type": "string" + }, + "extraEnv": { + "description": "env variable list for java app", + "type": "string" + }, + "helmRevision": { + "description": "revision of deployed helm release", + "type": "integer" + }, + "id": { + "type": "string" + }, + "imageUrl": { + "description": "URL of built image for app", + "type": "string" + }, + "output": { + "description": "output for task result", + "type": "string" + }, + "port": { + "description": "java app port", + "type": "string" + }, + "profile": { + "description": "java app profile", + "type": "string" + }, + "pvAccessMode": { + "type": "string" + }, + "pvEnabled": { + "type": "boolean" + }, + "pvMountPath": { + "type": "string" + }, + "pvSize": { + "type": "string" + }, + "pvStorageClass": { + "type": "string" + }, + "resourceSpec": { + "description": "resource spec of app pod", + "type": "string" + }, + "rollbackVersion": { + "description": "rollback target version", + "type": "string" + }, + "status": { + "description": "status is app status", + "type": "string" + }, + "strategy": { + "description": "deployment strategy (eg, rolling-update)", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "version": { + "description": "application version", + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest": { + "type": "object", + "required": [ + "users" + ], + "properties": { + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse": { + "type": "object", + "properties": { + "appGroupId": { + "type": "string" + }, + "applicationType": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationType" + }, + "createdAt": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "id": { + "type": "string" + }, + "metadata": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ApplicationType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "x-enum-varnames": [ + "ApplicationType_UNSPECIFIED", + "ApplicationType_THANOS", + "ApplicationType_PROMETHEUS", + "ApplicationType_GRAFANA", + "ApplicationType_KIALI", + "ApplicationType_KIBANA", + "ApplicationType_ELASTICSERCH", + "ApplicationType_CLOUD_CONSOLE", + "ApplicationType_HORIZON", + "ApplicationType_JAEGER", + "ApplicationType_KUBERNETES_DASHBOARD" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AuditResponse": { + "type": "object", + "properties": { + "clientIP": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "group": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "userAccountId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "userRoles": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Axis": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.BarChartData": { + "type": "object", + "properties": { + "series": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UnitNumber" + } + }, + "xAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig": { + "type": "object", + "properties": { + "expiration": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ChartData": { + "type": "object", + "properties": { + "podCounts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PodCount" + } + }, + "series": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Unit" + } + }, + "xAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + }, + "yAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse": { + "type": "object", + "properties": { + "isExist": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse": { + "type": "object", + "properties": { + "awsAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string" + }, + "clusters": { + "type": "integer" + }, + "createdAt": { + "type": "string" + }, + "createdIAM": { + "type": "boolean" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "status": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse": { + "type": "object", + "properties": { + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterHost": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterNode": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "hosts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterHost" + } + }, + "registered": { + "type": "integer" + }, + "registering": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "targeted": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "validity": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterResponse": { + "type": "object", + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse" + }, + "cloudService": { + "type": "string" + }, + "clusterType": { + "type": "string" + }, + "conf": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "favorited": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isStack": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse": { + "type": "object", + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "clusterRegion": { + "type": "string" + }, + "clusterType": { + "type": "string" + }, + "sshKeyName": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse": { + "type": "object", + "properties": { + "result": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse": { + "type": "object", + "properties": { + "result": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest": { + "type": "object", + "required": [ + "clusterId", + "name" + ], + "properties": { + "appGroupType": { + "type": "string", + "enum": [ + "LMA", + "SERVICE_MESH" + ] + }, + "clusterId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest": { + "type": "object", + "required": [ + "name", + "targetClusterId" + ], + "properties": { + "appConfig": { + "type": "string" + }, + "appSecret": { + "type": "string" + }, + "appType": { + "description": "springboot spring", + "type": "string" + }, + "artifactUrl": { + "type": "string" + }, + "executablePath": { + "type": "string" + }, + "extraEnv": { + "type": "string" + }, + "imageUrl": { + "type": "string" + }, + "name": { + "description": "App", + "type": "string" + }, + "namespace": { + "type": "string" + }, + "port": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "pvAccessMode": { + "type": "string" + }, + "pvEnabled": { + "type": "boolean" + }, + "pvMountPath": { + "type": "string" + }, + "pvSize": { + "type": "string" + }, + "pvStorageClass": { + "type": "string" + }, + "resourceSpec": { + "description": "tiny medium large", + "type": "string" + }, + "strategy": { + "description": "rolling-update blue-green canary", + "type": "string" + }, + "targetClusterId": { + "type": "string" + }, + "type": { + "description": "build deploy all", + "type": "string" + }, + "version": { + "description": "Task", + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest": { + "type": "object", + "properties": { + "applicationType": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "metadata": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest": { + "type": "object" + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse": { + "type": "object" + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest": { + "type": "object", + "required": [ + "accessKeyId", + "awsAccountId", + "name", + "secretAccessKey" + ], + "properties": { + "accessKeyId": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "awsAccountId": { + "type": "string", + "maxLength": 12, + "minLength": 12 + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "AZZURE", + "GCP" + ] + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "secretAccessKey": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "sessionToken": { + "type": "string", + "maxLength": 2000 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest": { + "type": "object", + "required": [ + "cloudService", + "name", + "organizationId", + "stackTemplateId" + ], + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "cloudAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "clusterType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "isStack": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "stackTemplateId": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest": { + "type": "object", + "properties": { + "contents": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardContents" + } + }, + "dashboardKey": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse": { + "type": "object", + "properties": { + "dashboardId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest": { + "type": "object", + "required": [ + "adminAccountId", + "adminEmail", + "name" + ], + "properties": { + "adminAccountId": { + "type": "string" + }, + "adminEmail": { + "type": "string" + }, + "adminName": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest": { + "type": "object", + "required": [ + "enforcementAction", + "policyName" + ], + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string", + "enum": [ + "deny", + "dryrun", + "warn" + ], + "example": "deny" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" + }, + "matchYaml": { + "type": "string", + "example": "namespaces:\r\n- testns1" + }, + "parameters": { + "type": "string", + "example": "{\"key\":\"value\"}" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "policyResourceName": { + "type": "string", + "example": "labelpolicy" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest": { + "type": "object", + "required": [ + "kind", + "rego", + "severity", + "templateName" + ], + "properties": { + "deprecated": { + "type": "boolean", + "example": false + }, + "description": { + "type": "string", + "example": "이 정책은 ..." + }, + "kind": { + "type": "string", + "example": "K8sRequiredLabels" + }, + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest": { + "type": "object", + "required": [ + "currentVersion", + "expectedVersion", + "rego", + "versionUpType" + ], + "properties": { + "currentVersion": { + "type": "string", + "example": "v1.0.0" + }, + "expectedVersion": { + "type": "string", + "example": "v1.1.0" + }, + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "versionUpType": { + "type": "string", + "enum": [ + "major", + "minor", + "patch" + ], + "example": "minor" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse": { + "type": "object", + "properties": { + "version": { + "type": "string", + "example": "v1.1.1" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "stackId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectLeaderId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest": { + "type": "object", + "required": [ + "cloudService", + "name", + "stackTemplateId" + ], + "properties": { + "cloudAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "clusterId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "stackTemplateId": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + }, + "userClusterEndpoint": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest": { + "type": "object", + "required": [ + "kubeType", + "kubeVersion", + "name", + "organizationIds", + "platform", + "serviceIds", + "template", + "version" + ], + "properties": { + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "description": { + "type": "string" + }, + "kubeType": { + "type": "string" + }, + "kubeVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "platform": { + "type": "string" + }, + "serviceIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "string" + }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, + "version": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest": { + "type": "object", + "required": [ + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" + ], + "properties": { + "description": { + "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "systemNotificationCondition": { + "type": "object", + "properties": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest": { + "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], + "properties": { + "description": { + "type": "string" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "password", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "month": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "year": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardContents": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "sizeX": { + "type": "integer" + }, + "sizeY": { + "type": "integer" + }, + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus": { + "type": "object", + "properties": { + "error": { + "type": "integer" + }, + "normal": { + "type": "integer" + }, + "warning": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate": { + "type": "object", + "properties": { + "policy": { + "type": "integer" + }, + "policyTemplate": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardResource": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "stack": { + "type": "object", + "properties": { + "abnormal": { + "type": "string" + }, + "normal": { + "type": "string" + } + } + }, + "storage": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "storage": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest": { + "type": "object", + "required": [ + "accessKeyId", + "secretAccessKey" + ], + "properties": { + "accessKeyId": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "secretAccessKey": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "sessionToken": { + "type": "string", + "maxLength": 2000 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest": { + "type": "object", + "properties": { + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest": { + "type": "object", + "properties": { + "adminPassword": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.EndpointResponse": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest": { + "type": "object", + "required": [ + "rego" + ], + "properties": { + "libs": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { + "type": "string", + "example": "Rego 코드" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { + "type": "object", + "properties": { + "column": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "releation": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindIdRequest": { + "type": "object", + "required": [ + "code", + "email", + "organizationId", + "userName" + ], + "properties": { + "code": { + "type": "string" + }, + "email": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "userName": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindIdResponse": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest": { + "type": "object", + "required": [ + "accountId", + "code", + "email", + "organizationId", + "userName" + ], + "properties": { + "accountId": { + "type": "string" + }, + "code": { + "type": "string" + }, + "email": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "userName": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse": { + "type": "object", + "properties": { + "appGroup": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse": { + "type": "object", + "properties": { + "appGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse": { + "type": "object", + "properties": { + "appServeApp": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse" + }, + "appServeAppTask": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse" + }, + "stages": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StageResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse": { + "type": "object", + "properties": { + "applications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse": { + "type": "object", + "properties": { + "audit": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse": { + "type": "object", + "properties": { + "audits": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "resourceQuota": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuota" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse": { + "type": "object", + "properties": { + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse": { + "type": "object", + "properties": { + "cloudAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse": { + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterNode" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse": { + "type": "object", + "properties": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse": { + "type": "object", + "properties": { + "clusters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse": { + "type": "object", + "properties": { + "chart": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse": { + "type": "object", + "properties": { + "charts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse": { + "type": "object", + "properties": { + "policyCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount" + }, + "templateCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse": { + "type": "object", + "properties": { + "statuses": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse": { + "type": "object", + "properties": { + "updatedResources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse": { + "type": "object", + "properties": { + "resources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardResource" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "sizeX": { + "type": "integer" + }, + "sizeY": { + "type": "integer" + }, + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse": { + "type": "object", + "properties": { + "stacks": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WorkloadData" + } + }, + "title": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse": { + "type": "object", + "properties": { + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *PermissionTemplateResponse ` + "`" + `json:\"permissions\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *PermissionSetResponse ` + "`" + `json:\"permissions\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse": { + "type": "object", + "properties": { + "policyNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "content": { + "policyNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { + "type": "object", + "properties": { + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse": { + "type": "object", + "properties": { + "deployVersion": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse": { + "type": "object", + "properties": { + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse": { + "type": "object", + "properties": { + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse": { + "type": "object", + "properties": { + "projectLeaderCount": { + "type": "integer" }, - "createdAt": { - "type": "string" + "projectMemberAllCount": { + "type": "integer" }, - "id": { + "projectMemberCount": { + "type": "integer" + }, + "projectViewerCount": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse": { + "type": "object", + "properties": { + "projectMember": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "projectMembers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse": { + "type": "object", + "properties": { + "k8sResources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse": { + "type": "object", + "properties": { + "kubeConfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse": { + "type": "object", + "properties": { + "resourcesUsage": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse": { + "type": "object", + "properties": { + "projectNamespace": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse": { + "type": "object", + "properties": { + "projectNamespaces": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse": { + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse": { + "type": "object", + "properties": { + "projectRole": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse": { + "type": "object", + "properties": { + "projectRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "status": { + "projects": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse": { + "type": "object", + "properties": { + "kubeConfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse": { + "type": "object", + "properties": { + "affectedPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatus" + } }, - "taker": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" }, - "updatedAt": { + "templateDescription": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateLatestVersion": { + "type": "string", + "example": "v1.0.3" + }, + "templateLatestVersionReleaseDate": { + "type": "string", + "format": "date-time" + }, + "templateMandatory": { + "type": "boolean" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + }, + "updatedPolicyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackResponse": { + "type": "object", + "properties": { + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse": { + "type": "object", + "properties": { + "stackStatus": { "type": "string" + }, + "stepStatus": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackStepStatus" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse": { + "type": "object", + "properties": { + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse": { + "type": "object", + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse": { + "type": "object", + "properties": { + "templateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "stackTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "stacks": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse": { + "type": "object", + "properties": { + "systemNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "systemNotificationRule": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse" } } }, - "domain.AlertResponse": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse": { "type": "object", "properties": { - "alertActions": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "systemNotificationRules": { "type": "array", "items": { - "$ref": "#/definitions/domain.AlertActionResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse" } - }, - "closedAt": { - "type": "string" - }, - "cluster": { - "$ref": "#/definitions/domain.SimpleClusterResponse" - }, - "code": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "description": { - "type": "string" - }, - "firedAt": { - "type": "string" - }, - "grade": { - "type": "string" - }, - "grafanaUrl": { - "type": "string" - }, - "id": { - "type": "string" - }, - "lastTaker": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node": { - "type": "string" - }, - "organizationId": { - "type": "string" - }, - "processingSec": { - "type": "integer" - }, - "rawData": { - "type": "string" - }, - "status": { - "type": "string" - }, - "takedAt": { - "type": "string" - }, - "takedSec": { - "type": "integer" - }, - "updatedAt": { - "type": "string" } } }, - "domain.AppGroupResponse": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse": { "type": "object", "properties": { - "appGroupType": { - "type": "integer" - }, - "clusterId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "workflowId": { - "type": "string" + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse" } } }, - "domain.AppServeApp": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse": { "type": "object", "properties": { - "appServeAppTasks": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "systemNotificationTemplates": { "type": "array", "items": { - "$ref": "#/definitions/domain.AppServeAppTask" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse" } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "appType": { - "description": "appType (spring/springboot)", - "type": "string" - }, + "systemNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse": { + "type": "object", + "properties": { "createdAt": { "type": "string" }, - "deletedAt": { - "type": "string" - }, - "endpointUrl": { - "description": "endpoint URL of deployed app", + "creator": { "type": "string" }, - "grafanaUrl": { - "description": "grafana dashboard URL for deployed app", + "description": { "type": "string" }, "id": { "type": "string" }, "name": { - "description": "application name", - "type": "string" - }, - "namespace": { - "description": "application namespace", "type": "string" }, "organizationId": { - "description": "contractId is a contract ID which this app belongs to", - "type": "string" - }, - "previewEndpointUrl": { - "description": "preview svc endpoint URL in B/G deployment", - "type": "string" - }, - "status": { - "description": "status is status of deployed app", - "type": "string" - }, - "targetClusterId": { - "description": "target cluster to which the app is deployed", - "type": "string" - }, - "targetClusterName": { - "description": "target cluster name", - "type": "string" - }, - "type": { - "description": "type (build/deploy/all)", "type": "string" }, "updatedAt": { @@ -4088,136 +13378,130 @@ const docTemplate = `{ } } }, - "domain.AppServeAppTask": { + "github_com_openinfradev_tks-api_pkg_domain.GetUserResponse": { "type": "object", "properties": { - "appConfig": { - "description": "java app config", - "type": "string" - }, - "appSecret": { - "description": "java app secret", - "type": "string" - }, - "appServeAppId": { - "description": "ID for appServeApp that this task belongs to", - "type": "string" - }, - "artifactUrl": { - "description": "URL of java app artifact (Eg, Jar)", - "type": "string" - }, - "availableRollback": { - "type": "boolean" - }, - "createdAt": { - "description": "createdAt is a creation timestamp for the application", - "type": "string" - }, - "deletedAt": { - "type": "string" - }, - "executablePath": { - "description": "Executable path of app image", - "type": "string" - }, - "extraEnv": { - "description": "env variable list for java app", - "type": "string" - }, - "helmRevision": { - "description": "revision of deployed helm release", - "type": "integer" - }, - "id": { - "type": "string" - }, - "imageUrl": { - "description": "URL of built image for app", - "type": "string" - }, - "output": { - "description": "output for task result", - "type": "string" - }, - "port": { - "description": "java app port", - "type": "string" - }, - "profile": { - "description": "java app profile", - "type": "string" - }, - "pvAccessMode": { - "type": "string" - }, - "pvEnabled": { - "type": "boolean" - }, - "pvMountPath": { - "type": "string" - }, - "pvSize": { - "type": "string" + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "pvStorageClass": { + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *MergedPermissionSetResponse ` + "`" + `json:\"permissions\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest": { + "type": "object", + "required": [ + "name", + "organizationId", + "stackTemplateId" + ], + "properties": { + "cloudService": { "type": "string" }, - "resourceSpec": { - "description": "resource spec of app pod", + "clusterType": { "type": "string" }, - "rollbackVersion": { - "description": "rollback target version", + "description": { "type": "string" }, - "status": { - "description": "status is app status", - "type": "string" + "kubeconfig": { + "type": "array", + "items": { + "type": "integer" + } }, - "strategy": { - "description": "deployment strategy (eg, rolling-update)", + "name": { "type": "string" }, - "updatedAt": { + "organizationId": { "type": "string" }, - "version": { - "description": "application version", + "stackTemplateId": { "type": "string" } } }, - "domain.ApplicationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse": { "type": "object", "properties": { - "appGroupId": { - "type": "string" - }, - "applicationType": { - "type": "integer" - }, - "createdAt": { - "type": "string" - }, - "endpoint": { - "type": "string" - }, "id": { "type": "string" - }, - "metadata": { - "type": "string" - }, - "updatedAt": { - "type": "string" } } }, - "domain.Axis": { + "github_com_openinfradev_tks-api_pkg_domain.Kinds": { "type": "object", "properties": { - "data": { + "apiGroups": { + "type": "array", + "items": { + "type": "string" + } + }, + "kinds": { "type": "array", "items": { "type": "string" @@ -4225,147 +13509,301 @@ const docTemplate = `{ } } }, - "domain.BootstrapKubeconfig": { + "github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse": { "type": "object", "properties": { - "expiration": { - "type": "integer" + "organizations": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" } } }, - "domain.ChartData": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { "type": "object", "properties": { - "podCounts": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { "type": "array", "items": { - "$ref": "#/definitions/domain.PodCount" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "series": { + "policyTemplates": { "type": "array", "items": { - "$ref": "#/definitions/domain.Unit" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse" } - }, - "xAxis": { - "$ref": "#/definitions/domain.Axis" - }, - "yAxis": { - "$ref": "#/definitions/domain.Axis" } } }, - "domain.CheckCloudAccountAwsAccountIdResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "policyTemplateStatistics": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics" + } } } }, - "domain.CheckCloudAccountNameResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "versions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "v1.1.0", + "v1.0.1", + "v1.0.0" + ] } } }, - "domain.CheckExistedResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "polices": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse" + } } } }, - "domain.CloudAccount": { + "github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse": { "type": "object", "properties": { - "accessKeyId": { - "type": "string" - }, - "awsAccountId": { - "type": "string" + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "cloudService": { + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ListUserBody": { + "type": "object", + "properties": { + "accountId": { "type": "string" }, - "clusters": { - "type": "integer" - }, "createdAt": { "type": "string" }, - "createdIAM": { - "type": "boolean" - }, "creator": { - "$ref": "#/definitions/domain.User" + "type": "string" }, - "creatorId": { + "department": { "type": "string" }, "description": { "type": "string" }, + "email": { + "type": "string" + }, "id": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" }, - "resource": { + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.LoginRequest": { + "type": "object", + "required": [ + "accountId", + "organizationId", + "password" + ], + "properties": { + "accountId": { "type": "string" }, - "secretAccessKey": { + "organizationId": { "type": "string" }, - "sessionToken": { + "password": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.LoginResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "passwordExpired": { + "type": "boolean" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "token": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" }, - "status": { - "type": "integer" + "mandatory": { + "type": "boolean" }, - "statusDesc": { - "type": "string" + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" }, - "updatedAt": { - "type": "string" + "policyName": { + "type": "string", + "example": "org 레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo": { + "type": "object", + "properties": { + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo" + } + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Match": { + "type": "object", + "properties": { + "excludedNamespaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "kinds": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Kinds" + } + }, + "namespaces": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } }, - "updator": { - "$ref": "#/definitions/domain.User" + "isAllowed": { + "type": "boolean" }, - "updatorId": { + "key": { "type": "string" } } }, - "domain.CloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse": { "type": "object", "properties": { - "awsAccountId": { - "type": "string" - }, - "cloudService": { - "type": "string" - }, - "clusters": { - "type": "integer" + "admin": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, "createdAt": { "type": "string" }, - "createdIAM": { - "type": "boolean" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, "description": { "type": "string" }, @@ -4375,1358 +13813,1701 @@ const docTemplate = `{ "name": { "type": "string" }, - "organizationId": { - "type": "string" + "policyTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse" + } }, - "resource": { + "primaryClusterId": { "type": "string" }, + "stackCount": { + "type": "integer" + }, + "stackTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + } + }, "status": { "type": "string" }, - "updatedAt": { + "statusDesc": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "systemNotificationTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + } + }, + "updatedAt": { + "type": "string" } } }, - "domain.Cluster": { + "github_com_openinfradev_tks-api_pkg_domain.PaginationResponse": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" + "filters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FilterResponse" + } }, - "byoClusterEndpointPort": { + "pageNumber": { "type": "integer" }, - "cloudAccount": { - "$ref": "#/definitions/domain.CloudAccount" + "pageSize": { + "type": "integer" }, - "cloudAccountId": { + "sortColumn": { "type": "string" }, - "cloudService": { + "sortOrder": { "type": "string" }, - "clusterType": { + "totalPages": { "type": "integer" }, - "conf": { - "$ref": "#/definitions/domain.ClusterConf" + "totalRows": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ParameterDef": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "createdAt": { + "defaultValue": { "type": "string" }, - "creator": { - "$ref": "#/definitions/domain.User" + "isArray": { + "type": "boolean" }, - "creatorId": { - "type": "string" + "isNew": { + "type": "boolean" }, - "description": { + "key": { "type": "string" }, - "favorited": { - "type": "boolean" - }, - "id": { + "type": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PermissionResponse": { + "type": "object", + "properties": { + "ID": { "type": "string" }, - "isStack": { - "type": "boolean" + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" + } }, - "kubeconfig": { + "endpoints": { "type": "array", "items": { - "type": "integer" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.EndpointResponse" } }, - "name": { - "type": "string" + "isAllowed": { + "type": "boolean" }, - "organizationId": { + "key": { "type": "string" }, - "stackTemplate": { - "$ref": "#/definitions/domain.StackTemplate" - }, - "stackTemplateId": { + "name": { "type": "string" - }, - "status": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PodCount": { + "type": "object", + "properties": { + "day": { "type": "integer" }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.User" - }, - "updatorId": { - "type": "string" + "value": { + "type": "integer" } } }, - "domain.ClusterConf": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyCount": { "type": "object", "properties": { - "tksCpNode": { + "deny": { "type": "integer" }, - "tksCpNodeMax": { + "dryrun": { "type": "integer" }, - "tksCpNodeType": { - "type": "string" - }, - "tksInfraNode": { + "fromOrgTemplate": { "type": "integer" }, - "tksInfraNodeMax": { + "fromTksTemplate": { "type": "integer" }, - "tksInfraNodeType": { - "type": "string" - }, - "tksUserNode": { + "total": { "type": "integer" }, - "tksUserNodeMax": { + "warn": { "type": "integer" - }, - "tksUserNodeType": { - "type": "string" } } }, - "domain.ClusterConfResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse": { "type": "object", "properties": { - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" }, - "tksCpNodeType": { + "createdAt": { "type": "string" }, - "tksInfraNode": { - "type": "integer" - }, - "tksInfraNodeMax": { - "type": "integer" + "grafanaUrl": { + "type": "string" }, - "tksInfraNodeType": { + "id": { "type": "string" }, - "tksUserNode": { - "type": "integer" + "messageActionProposal": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "messageContent": { + "type": "string" }, - "tksUserNodeType": { + "messageTitle": { "type": "string" - } - } - }, - "domain.ClusterHost": { - "type": "object", - "properties": { - "name": { + }, + "notificationType": { "type": "string" }, - "status": { + "organizationId": { "type": "string" - } - } - }, - "domain.ClusterNode": { - "type": "object", - "properties": { - "command": { + }, + "policyName": { "type": "string" }, - "hosts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterHost" - } + "rawData": { + "type": "string" }, - "registered": { - "type": "integer" + "read": { + "type": "boolean" }, - "registering": { - "type": "integer" + "severity": { + "type": "string" }, "status": { "type": "string" }, - "targeted": { - "type": "integer" - }, - "type": { + "updatedAt": { "type": "string" - }, - "validity": { - "type": "integer" } } }, - "domain.ClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyParameter": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" - }, - "byoClusterEndpointPort": { - "type": "integer" - }, - "cloudAccount": { - "$ref": "#/definitions/domain.SimpleCloudAccountResponse" - }, - "cloudService": { + "name": { "type": "string" }, - "clusterType": { + "type": { "type": "string" }, - "conf": { - "$ref": "#/definitions/domain.ClusterConfResponse" + "updatable": { + "type": "boolean" }, - "createdAt": { + "value": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" }, "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, "description": { "type": "string" }, + "enforcementAction": { + "type": "string", + "example": "deny" + }, + "filledParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, "id": { - "type": "string" + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "isStack": { + "mandatory": { "type": "boolean" }, - "name": { - "type": "string" + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" }, - "organizationId": { - "type": "string" + "matchYaml": { + "type": "string", + "example": "namespaces:\r\n- testns1" }, - "stackTemplate": { - "$ref": "#/definitions/domain.SimpleStackTemplateResponse" + "parameters": { + "type": "string", + "example": "{\"key\":\"value\"}" }, - "status": { - "type": "string" + "policyName": { + "type": "string", + "example": "label 정책" }, - "statusDesc": { - "type": "string" + "policyResourceName": { + "type": "string", + "example": "labelpolicy" + }, + "targetClusters": { + "description": "TargetClusterIds []string ` + "`" + `json:\"targetClusterIds\" example:\"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90\"` + "`" + `", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + } + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, "updatedAt": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse": { + "type": "object", + "properties": { + "policyCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "templateCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount" } } }, - "domain.ClusterSiteValuesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyStatus": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" - }, - "byoClusterEndpointPort": { - "type": "integer" + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" }, - "clusterRegion": { + "policyName": { "type": "string" }, - "clusterType": { + "policyPolicyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyParameter" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { + "type": "object", + "properties": { + "createdAt": { "type": "string" }, - "sshKeyName": { - "type": "string" + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "tksCpNode": { - "type": "integer" + "deprecated": { + "type": "boolean", + "example": false }, - "tksCpNodeMax": { - "type": "integer" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "tksCpNodeType": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "tksInfraNode": { - "type": "integer" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "tksInfraNodeMax": { - "type": "integer" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "tksInfraNodeType": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "tksUserNode": { - "type": "integer" + "rego": { + "type": "string", + "example": "rego 코드" }, - "tksUserNodeMax": { - "type": "integer" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "tksUserNodeType": { - "type": "string" - } - } - }, - "domain.CreateAppGroupRequest": { - "type": "object", - "required": [ - "clusterId", - "name" - ], - "properties": { - "appGroupType": { + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "type": { "type": "string", "enum": [ - "LMA", - "SERVICE_MESH" - ] + "tks", + "organization" + ], + "example": "tks" }, - "clusterId": { + "updatedAt": { "type": "string" }, - "description": { - "type": "string" + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "name": { - "type": "string" + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.CreateAppGroupResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics": { "type": "object", "properties": { - "id": { + "organizationId": { + "type": "string" + }, + "organizationName": { "type": "string" + }, + "usageCount": { + "type": "integer" } } }, - "domain.CreateAppServeAppRequest": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse": { "type": "object", - "required": [ - "name", - "targetClusterId" - ], "properties": { - "appConfig": { + "createdAt": { "type": "string" }, - "appSecret": { - "type": "string" + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "appType": { - "description": "springboot spring", - "type": "string" + "currentVersion": { + "type": "string", + "example": "v1.0.1" }, - "artifactUrl": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "executablePath": { - "type": "string" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "extraEnv": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "imageUrl": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "name": { - "description": "App", - "type": "string" + "latestVersion": { + "type": "string", + "example": "v1.0.1" }, - "namespace": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "port": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "profile": { - "type": "string" + "rego": { + "type": "string", + "example": "rego 코드" }, - "pvAccessMode": { + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "type": { + "type": "string", + "enum": [ + "tks", + "organization" + ], + "example": "tks" + }, + "updatedAt": { "type": "string" }, - "pvEnabled": { - "type": "boolean" + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse": { + "type": "object", + "properties": { + "createdAt": { + "description": "AppCount int ` + "`" + `json:\"appCount\"` + "`" + `\nNamespaceCount int ` + "`" + `json:\"namespaceCount\"` + "`" + `\nMemberCount int ` + "`" + `json:\"memberCount\"` + "`" + `", + "type": "string" }, - "pvMountPath": { + "description": { "type": "string" }, - "pvSize": { + "id": { "type": "string" }, - "pvStorageClass": { + "name": { "type": "string" }, - "resourceSpec": { - "description": "tiny medium large", + "organizationId": { "type": "string" }, - "strategy": { - "description": "rolling-update blue-green canary", + "projectLeaderAccountId": { "type": "string" }, - "targetClusterId": { + "projectLeaderDepartment": { "type": "string" }, - "type": { - "description": "build deploy all", + "projectLeaderId": { "type": "string" }, - "version": { - "description": "Task", + "projectLeaderName": { "type": "string" - } - } - }, - "domain.CreateApplicationRequest": { - "type": "object", - "properties": { - "applicationType": { + }, + "projectRoleId": { "type": "string" }, - "endpoint": { + "projectRoleName": { "type": "string" }, - "metadata": { + "updatedAt": { "type": "string" } } }, - "domain.CreateBootstrapKubeconfigResponse": { - "type": "object", - "properties": { - "kubeconfig": { - "$ref": "#/definitions/domain.BootstrapKubeconfig" - } - } - }, - "domain.CreateCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest": { "type": "object", "required": [ - "accessKeyId", - "awsAccountId", - "name", - "secretAccessKey" + "projectRoleId", + "projectUserId" ], "properties": { - "accessKeyId": { - "type": "string", - "maxLength": 128, - "minLength": 16 - }, - "awsAccountId": { - "type": "string", - "maxLength": 12, - "minLength": 12 - }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "AZZURE", - "GCP" - ] - }, - "description": { + "projectRoleId": { "type": "string" }, - "name": { + "projectUserId": { "type": "string" - }, - "secretAccessKey": { - "type": "string", - "maxLength": 128, - "minLength": 16 - }, - "sessionToken": { - "type": "string", - "maxLength": 2000 } } }, - "domain.CreateCloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse": { "type": "object", "properties": { - "id": { + "createdAt": { "type": "string" - } - } - }, - "domain.CreateClusterRequest": { - "type": "object", - "required": [ - "cloudService", - "name", - "organizationId", - "stackTemplateId" - ], - "properties": { - "byoClusterEndpointHost": { + }, + "id": { "type": "string" }, - "byoClusterEndpointPort": { - "type": "integer" + "projectId": { + "type": "string" }, - "cloudAccountId": { + "projectRoleId": { "type": "string" }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "BYOH" - ] + "projectRoleName": { + "type": "string" }, - "clusterType": { + "projectUserAccountId": { "type": "string" }, - "description": { + "projectUserDepartment": { "type": "string" }, - "isStack": { - "type": "boolean" + "projectUserEmail": { + "type": "string" }, - "name": { + "projectUserId": { "type": "string" }, - "organizationId": { + "projectUserName": { "type": "string" }, - "stackTemplateId": { + "updatedAt": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources": { + "type": "object", + "properties": { + "cronjobs": { + "type": "integer" }, - "tksCpNode": { + "daemonsets": { "type": "integer" }, - "tksCpNodeMax": { + "deployments": { "type": "integer" }, - "tksCpNodeType": { - "type": "string" + "ingresses": { + "type": "integer" }, - "tksInfraNode": { + "jobs": { "type": "integer" }, - "tksInfraNodeMax": { + "pods": { "type": "integer" }, - "tksInfraNodeType": { - "type": "string" + "pvcs": { + "type": "integer" }, - "tksUserNode": { + "services": { "type": "integer" }, - "tksUserNodeMax": { + "statefulsets": { "type": "integer" }, - "tksUserNodeType": { + "updatedAt": { "type": "string" } } }, - "domain.CreateClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage": { "type": "object", "properties": { - "id": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "storage": { "type": "string" } } }, - "domain.CreateOrganizationRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse": { "type": "object", - "required": [ - "Email", - "name" - ], "properties": { - "Email": { + "appCount": { + "type": "integer" + }, + "createdAt": { "type": "string" }, "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "type": "string" }, - "name": { + "grafanaUrl": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "stackId": { + "type": "string" + }, + "stackName": { + "type": "string" + }, + "status": { "type": "string" }, - "phone": { + "updatedAt": { "type": "string" } } }, - "domain.CreateStackRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectResponse": { "type": "object", - "required": [ - "cloudService", - "name", - "stackTemplateId" - ], "properties": { - "cloudAccountId": { - "type": "string" - }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "BYOH" - ] + "appCount": { + "type": "integer" }, - "clusterId": { + "createdAt": { "type": "string" }, "description": { "type": "string" }, - "name": { + "id": { "type": "string" }, - "stackTemplateId": { + "isMyProject": { "type": "string" }, - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { + "memberCount": { "type": "integer" }, - "tksCpNodeType": { + "name": { "type": "string" }, - "tksInfraNode": { + "namespaceCount": { "type": "integer" }, - "tksInfraNodeMax": { - "type": "integer" + "organizationId": { + "type": "string" }, - "tksInfraNodeType": { + "projectLeaderId": { "type": "string" }, - "tksUserNode": { - "type": "integer" + "projectLeaderName": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "projectRoleId": { + "type": "string" }, - "tksUserNodeType": { + "projectRoleName": { "type": "string" }, - "userClusterEndpoint": { + "updatedAt": { "type": "string" } } }, - "domain.CreateStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "type": "string" + }, "id": { "type": "string" + }, + "name": { + "description": "project-leader, project-member, project-viewer", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RegoCompieError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "P_INVALID_REGO_SYNTAX" + }, + "message": { + "type": "string", + "example": "Invalid rego syntax" + }, + "status": { + "type": "integer", + "example": 400 + }, + "text": { + "type": "string", + "example": "Rego 문법 에러입니다. 라인:2 컬럼:1 에러메시지: var testnum is not safe" } } }, - "domain.CreateStackTemplateRequest": { + "github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest": { "type": "object", "required": [ - "name", - "platform", - "template", - "version" + "rego" ], "properties": { - "cloudService": { + "libs": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { "type": "string", - "enum": [ - "AWS", - "AZZURE", - "GCP" - ] + "example": "Rego 코드" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } }, - "description": { + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest": { + "type": "object", + "required": [ + "stackTemplateIds" + ], + "properties": { + "stackTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest": { + "type": "object", + "required": [ + "systemNotificationTemplateIds" + ], + "properties": { + "systemNotificationTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectMemberId": { + "type": "string" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest": { + "type": "object", + "required": [ + "users" + ], + "properties": { + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ResourceQuota": { + "type": "object", + "properties": { + "quotas": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr": { + "type": "object", + "properties": { + "quota": { + "type": "integer" + }, + "required": { + "type": "integer" + }, + "type": { "type": "string" }, - "name": { + "usage": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest": { + "type": "object", + "properties": { + "taskId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse": { + "type": "object", + "properties": { + "awsAccountId": { "type": "string" }, - "platform": { + "cloudService": { "type": "string" }, - "template": { + "clusters": { + "type": "integer" + }, + "createdIAM": { + "type": "boolean" + }, + "description": { "type": "string" }, - "templateType": { - "type": "string", - "enum": [ - "STANDARD", - "MSA" - ] + "id": { + "type": "string" }, - "version": { + "name": { + "type": "string" + }, + "organizationId": { "type": "string" } } }, - "domain.CreateStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse": { "type": "object", "properties": { "id": { "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" } } }, - "domain.CreateUserRequest": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse": { "type": "object", - "required": [ - "accountId", - "email", - "password", - "role" - ], "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "type": "string" }, - "email": { + "id": { "type": "string" }, "name": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "password": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, - "role": { + "type": { "type": "string", "enum": [ - "admin", - "user" - ] + "tks", + "organization" + ], + "example": "tks" + }, + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.CreateUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } + "id": { + "type": "string" + }, + "name": { + "type": "string" } } }, - "domain.DashboardChartResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse": { "type": "object", "properties": { - "chartData": { - "$ref": "#/definitions/domain.ChartData" - }, - "chartType": { + "cloudService": { "type": "string" }, "description": { "type": "string" }, - "duration": { + "id": { "type": "string" }, - "interval": { + "kubeType": { "type": "string" }, - "month": { + "kubeVersion": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse" + } }, - "updatedAt": { + "template": { "type": "string" - }, - "year": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse": { + "type": "object", + "properties": { + "type": { "type": "string" } } }, - "domain.DashboardResource": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse": { "type": "object", "properties": { - "cpu": { + "createdAt": { "type": "string" }, - "memory": { + "description": { "type": "string" }, - "stack": { + "id": { "type": "string" }, - "storage": { + "name": { + "type": "string" + }, + "updatedAt": { "type": "string" } } }, - "domain.DashboardStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse": { "type": "object", "properties": { - "cpu": { + "accountId": { "type": "string" }, - "createdAt": { + "department": { "type": "string" }, - "description": { + "email": { "type": "string" }, "id": { "type": "string" }, - "memory": { - "type": "string" - }, "name": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.StackConfResponse": { + "type": "object", + "required": [ + "tksInfraNode", + "tksUserNode" + ], + "properties": { + "tksCpNode": { + "type": "integer" }, - "status": { - "type": "string" + "tksCpNodeMax": { + "type": "integer" }, - "statusDesc": { + "tksCpNodeType": { "type": "string" }, - "storage": { + "tksInfraNode": { + "type": "integer", + "maximum": 3, + "minimum": 1 + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { "type": "string" }, - "updatedAt": { + "tksUserNode": { + "type": "integer", + "maximum": 100, + "minimum": 0 + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { "type": "string" } } }, - "domain.DeleteCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics": { "type": "object", - "required": [ - "accessKeyId", - "secretAccessKey" - ], "properties": { - "accessKeyId": { - "type": "string", - "maxLength": 128, - "minLength": 16 + "outofdatePolicyCount": { + "type": "integer" }, - "secretAccessKey": { - "type": "string", - "maxLength": 128, - "minLength": 16 + "outofdateTemplateCount": { + "type": "integer" }, - "sessionToken": { - "type": "string", - "maxLength": 2000 + "totalPolicyCount": { + "type": "integer" + }, + "totalTemplateCount": { + "type": "integer" + }, + "uptodatePolicyCount": { + "type": "integer" + }, + "uptodateTemplateCount": { + "type": "integer" } } }, - "domain.FilterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse": { "type": "object", "properties": { - "column": { - "type": "string" + "policyDescription": { + "type": "string", + "example": "org 레이블 설정 여부 검사" }, - "values": { - "type": "array", - "items": { - "type": "string" - } + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "policyMandatory": { + "type": "boolean" + }, + "policyName": { + "type": "string", + "example": "org 레이블 요구" + }, + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" + }, + "templateDescription": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateLatestVersion": { + "type": "string", + "example": "v1.0.3" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" } } }, - "domain.FindIdRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackResponse": { "type": "object", - "required": [ - "code", - "email", - "organizationId", - "userName" - ], "properties": { - "code": { + "appServeAppCnt": { + "type": "integer" + }, + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse" + }, + "conf": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackConfResponse" + }, + "createdAt": { "type": "string" }, - "email": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "favorited": { + "type": "boolean" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { "type": "string" }, "organizationId": { "type": "string" }, - "userName": { + "primaryCluster": { + "type": "boolean" + }, + "resource": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse" + }, + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "userClusterEndpoint": { "type": "string" } } }, - "domain.FindIdResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackStepStatus": { "type": "object", "properties": { - "accountId": { + "maxStep": { + "type": "integer" + }, + "stage": { + "type": "string" + }, + "status": { "type": "string" + }, + "step": { + "type": "integer" } } }, - "domain.FindPasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse": { "type": "object", - "required": [ - "accountId", - "code", - "email", - "organizationId", - "userName" - ], "properties": { - "accountId": { + "cloudService": { "type": "string" }, - "code": { + "createdAt": { "type": "string" }, - "email": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { "type": "string" }, - "organizationId": { + "id": { "type": "string" }, - "userName": { + "kubeType": { "type": "string" - } - } - }, - "domain.GetAlertResponse": { - "type": "object", - "properties": { - "alert": { - "$ref": "#/definitions/domain.AlertResponse" - } - } - }, - "domain.GetAlertsResponse": { - "type": "object", - "properties": { - "alerts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AlertResponse" - } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetAppGroupResponse": { - "type": "object", - "properties": { - "appGroup": { - "$ref": "#/definitions/domain.AppGroupResponse" - } - } - }, - "domain.GetAppGroupsResponse": { - "type": "object", - "properties": { - "appGroups": { + "kubeVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizations": { "type": "array", "items": { - "$ref": "#/definitions/domain.AppGroupResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetAppServeAppResponse": { - "type": "object", - "properties": { - "appServeApp": { - "$ref": "#/definitions/domain.AppServeApp" + "platform": { + "type": "string" }, - "stages": { + "services": { "type": "array", "items": { - "$ref": "#/definitions/domain.StageResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse" } + }, + "template": { + "type": "string" + }, + "templateType": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "version": { + "type": "string" } } }, - "domain.GetAppServeAppTaskResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse": { "type": "object", "properties": { - "appServeAppTask": { - "$ref": "#/definitions/domain.AppServeAppTask" + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" } } }, - "domain.GetApplicationsResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse": { "type": "object", "properties": { "applications": { "type": "array", "items": { - "$ref": "#/definitions/domain.ApplicationResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse" } - } - } - }, - "domain.GetBootstrapKubeconfigResponse": { - "type": "object", - "properties": { - "kubeconfig": { - "$ref": "#/definitions/domain.BootstrapKubeconfig" - } - } - }, - "domain.GetCloudAccountResourceQuotaResponse": { - "type": "object", - "properties": { - "available": { - "type": "boolean" }, - "resourceQuota": { - "$ref": "#/definitions/domain.ResourceQuota" - } - } - }, - "domain.GetCloudAccountResponse": { - "type": "object", - "properties": { - "cloudAccount": { - "$ref": "#/definitions/domain.CloudAccountResponse" + "type": { + "type": "string" } } }, - "domain.GetCloudAccountsResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StageResponse": { "type": "object", "properties": { - "cloudAccounts": { + "actions": { "type": "array", "items": { - "$ref": "#/definitions/domain.CloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ActionResponse" } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetClusterNodesResponse": { - "type": "object", - "properties": { - "nodes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterNode" - } - } - } - }, - "domain.GetClustersResponse": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterResponse" - } + "name": { + "description": "BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백)", + "type": "string" }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetDashboardChartResponse": { - "type": "object", - "properties": { - "chart": { - "$ref": "#/definitions/domain.DashboardChartResponse" - } - } - }, - "domain.GetDashboardChartsResponse": { - "type": "object", - "properties": { - "charts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.DashboardChartResponse" - } + "result": { + "type": "string" + }, + "status": { + "type": "string" } } }, - "domain.GetDashboardResourcesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse": { "type": "object", "properties": { - "resources": { - "$ref": "#/definitions/domain.DashboardResource" + "content": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationId": { + "type": "string" + }, + "taker": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "updatedAt": { + "type": "string" } } }, - "domain.GetDashboardStacksResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse": { "type": "object", "properties": { - "stacks": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { "type": "array", "items": { - "$ref": "#/definitions/domain.DashboardStackResponse" - } - } - } - }, - "domain.GetMyProfileResponse": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } - } - } - }, - "domain.GetOrganizationResponse": { - "type": "object", - "properties": { - "organization": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" } + }, + "severity": { + "type": "string" + }, + "systemNotificationRuleId": { + "type": "string" } } }, - "domain.GetStackKubeConfigResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse": { "type": "object", + "required": [ + "key", + "order", + "value" + ], "properties": { - "kubeConfig": { + "key": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { "type": "string" } } }, - "domain.GetStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter": { "type": "object", "properties": { - "stack": { - "$ref": "#/definitions/domain.StackResponse" + "operator": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { + "type": "string" } } }, - "domain.GetStackStatusResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse": { "type": "object", "properties": { - "stackStatus": { + "closedAt": { "type": "string" }, - "stepStatus": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + }, + "createdAt": { + "type": "string" + }, + "firedAt": { + "type": "string" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "lastTaker": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "policyName": { + "type": "string" + }, + "processingSec": { + "type": "integer" + }, + "rawData": { + "type": "string" + }, + "read": { + "type": "boolean" + }, + "severity": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationActions": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackStepStatus" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse" } + }, + "takedAt": { + "type": "string" + }, + "takedSec": { + "type": "integer" + }, + "updatedAt": { + "type": "string" } } }, - "domain.GetStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse": { "type": "object", "properties": { - "stackTemplate": { - "$ref": "#/definitions/domain.StackTemplateResponse" + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isSystem": { + "type": "boolean" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationCondition": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse" + }, + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + }, + "targetUsers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" } } }, - "domain.GetStackTemplatesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse": { "type": "object", + "required": [ + "metricQuery" + ], "properties": { - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" + "createdAt": { + "type": "string" }, - "stackTemplates": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isSystem": { + "type": "boolean" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizations": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackTemplateResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" } + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" } } }, - "domain.GetStacksResponse": { + "github_com_openinfradev_tks-api_pkg_domain.TemplateCount": { "type": "object", "properties": { - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" + "organizationTemplate": { + "type": "integer" }, - "stacks": { + "tksTemplate": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.TemplateResponse": { + "type": "object", + "properties": { + "children": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse" } + }, + "isAllowed": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" } } }, - "domain.GetUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.Unit": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "updatedAt": { - "type": "string" - } + "data": { + "type": "array", + "items": { + "type": "string" } + }, + "name": { + "type": "string" } } }, - "domain.ImportClusterRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UnitNumber": { "type": "object", - "required": [ - "name", - "organizationId", - "stackTemplateId" - ], "properties": { - "cloudService": { - "type": "string" - }, - "clusterType": { - "type": "string" - }, - "description": { - "type": "string" - }, - "kubeconfig": { + "data": { "type": "array", "items": { "type": "integer" @@ -5734,110 +15515,145 @@ const docTemplate = `{ }, "name": { "type": "string" - }, - "organizationId": { - "type": "string" - }, - "stackTemplateId": { - "type": "string" } } }, - "domain.ImportClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest": { "type": "object", + "required": [ + "taskId" + ], "properties": { - "id": { + "endpointUrl": { + "type": "string" + }, + "helmRevision": { + "type": "integer" + }, + "previewEndpointUrl": { + "type": "string" + }, + "taskId": { "type": "string" } } }, - "domain.ListOrganizationBody": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest": { "type": "object", "properties": { - "createdAt": { + "abort": { + "type": "boolean" + }, + "appConfig": { "type": "string" }, - "description": { + "appSecret": { "type": "string" }, - "id": { + "artifactUrl": { + "type": "string" + }, + "executablePath": { + "type": "string" + }, + "extraEnv": { "type": "string" }, - "name": { + "imageUrl": { "type": "string" }, - "phone": { + "port": { "type": "string" }, - "primaryClusterId": { + "profile": { "type": "string" }, - "status": { + "promote": { + "description": "Update Strategy", + "type": "boolean" + }, + "resourceSpec": { "type": "string" }, - "updatedAt": { + "strategy": { + "description": "Task", "type": "string" } } }, - "domain.ListUserBody": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest": { "type": "object", + "required": [ + "status", + "taskId" + ], "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { + "output": { "type": "string" }, - "creator": { + "status": { "type": "string" }, - "department": { + "taskId": { "type": "string" - }, + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest": { + "type": "object", + "properties": { "description": { "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest": { + "type": "object", + "properties": { + "groupName": { "type": "string" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "sizeX": { + "type": "integer" }, - "role": { - "$ref": "#/definitions/domain.Role" + "sizeY": { + "type": "integer" }, - "updatedAt": { - "type": "string" + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } } } }, - "domain.LoginRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest": { "type": "object", "required": [ - "accountId", - "organizationId", + "email", + "name", "password" ], "properties": { - "accountId": { - "type": "string" + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 }, - "organizationId": { + "email": { "type": "string" }, + "name": { + "type": "string", + "maxLength": 30, + "minLength": 1 + }, "password": { "type": "string" } } }, - "domain.LoginResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse": { "type": "object", "properties": { "user": { @@ -5849,243 +15665,326 @@ const docTemplate = `{ "department": { "type": "string" }, - "name": { + "email": { "type": "string" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "id": { + "type": "string" }, - "passwordExpired": { - "type": "boolean" + "name": { + "type": "string" }, - "role": { - "$ref": "#/definitions/domain.Role" + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" }, - "token": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } } } } } }, - "domain.LogoutResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest": { "type": "object", + "required": [ + "name" + ], "properties": { - "ssoUrls": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } + "adminAccountId": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string", + "maxLength": 30, + "minLength": 1 } } }, - "domain.Organization": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse": { "type": "object", "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "description": { - "type": "string" - }, "id": { "type": "string" - }, - "name": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" } } }, - "domain.PaginationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest": { "type": "object", + "required": [ + "newPassword", + "originPassword" + ], "properties": { - "filters": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.FilterResponse" - } - }, - "pageNumber": { - "type": "integer" - }, - "pageSize": { - "type": "integer" - }, - "sortColumn": { + "newPassword": { "type": "string" }, - "sortOrder": { + "originPassword": { "type": "string" - }, - "totalPages": { - "type": "integer" - }, - "totalRows": { - "type": "integer" } } }, - "domain.PingTokenRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest": { "type": "object", "required": [ - "organizationId", - "token" + "ID", + "isAllowed" ], "properties": { - "organizationId": { + "ID": { "type": "string" }, - "token": { - "type": "string" + "isAllowed": { + "type": "boolean", + "enum": [ + true, + false + ] } } }, - "domain.PodCount": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest": { "type": "object", "properties": { - "day": { - "type": "integer" - }, - "value": { - "type": "integer" + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest" + } } } }, - "domain.ResourceQuota": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { "type": "object", "properties": { - "quotas": { + "currentTargetClusterIds": { "type": "array", "items": { - "$ref": "#/definitions/domain.ResourceQuotaAttr" - } + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "newTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] } } }, - "domain.ResourceQuotaAttr": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { "type": "object", "properties": { - "quota": { - "type": "integer" + "description": { + "type": "string" }, - "required": { - "type": "integer" + "enforcementAction": { + "type": "string", + "enum": [ + "deny", + "dryrun", + "warn" + ], + "example": "deny" }, - "type": { + "mandatory": { + "type": "boolean" + }, + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" + }, + "matchYaml": { "type": "string" }, - "usage": { - "type": "integer" + "parameters": { + "type": "string", + "example": "{\"labels\":{\"key\":\"owner\",\"allowedRegex\":\"test*\"}" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" } } }, - "domain.Role": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", + "required": [ + "templateName" + ], "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, "description": { "type": "string" }, - "id": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "name": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "updatedAt": { - "type": "string" + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "domain.RollbackAppServeAppRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest": { "type": "object", "properties": { - "taskId": { + "primaryClusterId": { "type": "string" } } }, - "domain.SimpleCloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest": { "type": "object", "properties": { - "awsAccountId": { + "projectRoleId": { "type": "string" - }, - "cloudService": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectMemberId": { + "type": "string" + }, + "projectRoleId": { + "type": "string" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest": { + "type": "object", + "properties": { + "description": { "type": "string" - }, - "clusters": { - "type": "integer" - }, - "createdIAM": { - "type": "boolean" - }, + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { "description": { "type": "string" }, - "id": { - "type": "string" - }, "name": { "type": "string" }, - "organizationId": { + "projectLeaderId": { "type": "string" } } }, - "domain.SimpleClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" }, - "organizationId": { + "templateTargetVerson": { + "type": "string", + "example": "v1.0.3" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest": { + "type": "object", + "properties": { + "description": { "type": "string" } } }, - "domain.SimpleStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest": { + "type": "object", + "required": [ + "organizationIds" + ], + "properties": { + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest": { "type": "object", + "required": [ + "kubeType", + "kubeVersion", + "name", + "organizationIds", + "platform", + "serviceIds", + "template", + "version" + ], "properties": { "cloudService": { - "type": "string" + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] }, "description": { "type": "string" }, - "id": { - "type": "string" - }, "kubeType": { "type": "string" }, @@ -6095,736 +15994,783 @@ const docTemplate = `{ "name": { "type": "string" }, - "services": { + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "platform": { + "type": "string" + }, + "serviceIds": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackTemplateServiceResponse" + "type": "string" } }, "template": { "type": "string" + }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, + "version": { + "type": "string" } } }, - "domain.SimpleUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest": { "type": "object", "properties": { - "accountId": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { + "description": { "type": "string" } } }, - "domain.StackConfResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest": { "type": "object", "required": [ - "tksInfraNode", - "tksUserNode" + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" ], "properties": { - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" - }, - "tksCpNodeType": { + "description": { "type": "string" }, - "tksInfraNode": { - "type": "integer", - "maximum": 3, - "minimum": 1 + "messageActionProposal": { + "type": "string" }, - "tksInfraNodeMax": { - "type": "integer" + "messageContent": { + "type": "string" }, - "tksInfraNodeType": { + "messageTitle": { "type": "string" }, - "tksUserNode": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "name": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "systemNotificationCondition": { + "type": "object", + "properties": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + }, + "systemNotificationRuleId": { + "type": "string" + } + } }, - "tksUserNodeType": { + "systemNotificationTemplateId": { "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, - "domain.StackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest": { "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], "properties": { - "cloudAccount": { - "$ref": "#/definitions/domain.SimpleCloudAccountResponse" - }, - "conf": { - "$ref": "#/definitions/domain.StackConfResponse" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, "description": { "type": "string" }, - "favorited": { - "type": "boolean" - }, - "grafanaUrl": { - "type": "string" + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } }, - "id": { + "metricQuery": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" - }, - "primaryCluster": { - "type": "boolean" - }, - "resource": { - "$ref": "#/definitions/domain.DashboardStackResponse" + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 }, - "stackTemplate": { - "$ref": "#/definitions/domain.SimpleStackTemplateResponse" + "name": { + "type": "string", + "maxLength": 100, + "minLength": 0 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest": { + "type": "object", + "required": [ + "email", + "name", + "roles" + ], + "properties": { + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 }, - "status": { - "type": "string" + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 }, - "statusDesc": { + "email": { "type": "string" }, - "updatedAt": { + "name": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "userClusterEndpoint": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } } } }, - "domain.StackStepStatus": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse": { "type": "object", "properties": { - "maxStep": { - "type": "integer" - }, - "stage": { - "type": "string" - }, - "status": { - "type": "string" - }, - "step": { - "type": "integer" + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "type": "object", + "required": [ + "accountId", + "email", + "name", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + } } } }, - "domain.StackTemplate": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter": { "type": "object", "properties": { - "cloudService": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.User" - }, - "creatorId": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kubeType": { - "type": "string" - }, - "kubeVersion": { - "type": "string" - }, "name": { "type": "string" }, - "organizationId": { - "type": "string" - }, - "platform": { - "type": "string" - }, - "services": { - "type": "array", - "items": { - "type": "integer" - } - }, - "template": { - "type": "string" - }, - "templateType": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.User" - }, - "updatorId": { + "type": { "type": "string" }, - "version": { + "value": { "type": "string" } } }, - "domain.StackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UserCreationRole": { "type": "object", + "required": [ + "id" + ], "properties": { - "cloudService": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "description": { - "type": "string" - }, "id": { "type": "string" - }, - "kubeType": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest": { + "type": "object", + "required": [ + "email", + "organizationId", + "userName" + ], + "properties": { + "email": { "type": "string" }, - "kubeVersion": { + "organizationId": { "type": "string" }, - "name": { + "userName": { "type": "string" - }, - "platform": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse": { + "type": "object", + "properties": { + "validityPeriod": { "type": "string" - }, - "services": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.StackTemplateServiceResponse" - } - }, - "template": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "organizationId", + "userName" + ], + "properties": { + "accountId": { "type": "string" }, - "templateType": { + "email": { "type": "string" }, - "updatedAt": { + "organizationId": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "version": { + "userName": { "type": "string" } } }, - "domain.StackTemplateServiceApplicationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse": { "type": "object", "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "version": { + "validityPeriod": { "type": "string" } } }, - "domain.StackTemplateServiceResponse": { + "github_com_openinfradev_tks-api_pkg_domain.WidgetResponse": { "type": "object", "properties": { - "applications": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.StackTemplateServiceApplicationResponse" - } + "sizeX": { + "type": "integer" }, - "type": { + "sizeY": { + "type": "integer" + }, + "startX": { + "type": "integer" + }, + "startY": { + "type": "integer" + }, + "widgetKey": { "type": "string" } } }, - "domain.StageResponse": { + "github_com_openinfradev_tks-api_pkg_domain.WorkloadData": { "type": "object", "properties": { - "actions": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ActionResponse" - } - }, "name": { - "description": "BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백)", "type": "string" }, - "result": { - "type": "string" - }, - "status": { - "type": "string" + "value": { + "type": "integer" } } }, - "domain.Unit": { + "github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest": { "type": "object", "properties": { - "data": { + "policyTemplateIds": { "type": "array", "items": { "type": "string" } - }, - "name": { - "type": "string" } } }, - "domain.UpdateAlertRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse": { "type": "object", "properties": { - "description": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" } } }, - "domain.UpdateAppServeAppEndpointRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest": { "type": "object", "required": [ - "taskId" + "kind", + "rego", + "severity", + "templateName" ], "properties": { - "endpointUrl": { - "type": "string" - }, - "helmRevision": { - "type": "integer" - }, - "previewEndpointUrl": { - "type": "string" - }, - "taskId": { - "type": "string" - } - } - }, - "domain.UpdateAppServeAppRequest": { - "type": "object", - "properties": { - "abort": { - "type": "boolean" - }, - "appConfig": { - "type": "string" - }, - "appSecret": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "artifactUrl": { - "type": "string" - }, - "executablePath": { - "type": "string" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "extraEnv": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "imageUrl": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "port": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "profile": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "promote": { - "description": "Update Strategy", - "type": "boolean" + "rego": { + "type": "string", + "example": "rego 코드" }, - "resourceSpec": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "strategy": { - "description": "Task", - "type": "string" + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "domain.UpdateAppServeAppStatusRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest": { "type": "object", "required": [ - "status", - "taskId" + "currentVersion", + "expectedVersion", + "rego", + "versionUpType" ], "properties": { - "output": { - "type": "string" + "currentVersion": { + "type": "string", + "example": "v1.0.0" }, - "status": { - "type": "string" + "expectedVersion": { + "type": "string", + "example": "v1.1.0" }, - "taskId": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "versionUpType": { + "type": "string", + "enum": [ + "major", + "minor", + "patch" + ], + "example": "minor" } } }, - "domain.UpdateCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "version": { + "type": "string", + "example": "v1.1.1" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest": { + "type": "object", + "properties": { + "policyTemplateIds": { + "type": "array", + "items": { + "type": "string" + } } } }, - "domain.UpdateMyProfileRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse": { "type": "object", - "required": [ - "password" - ], "properties": { - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, - "email": { - "type": "string" - }, - "name": { - "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "password": { - "type": "string" + "existed": { + "type": "boolean" } } }, - "domain.UpdateMyProfileResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } + "existed": { + "type": "boolean" } } }, - "domain.UpdateOrganizationRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest": { "type": "object", "required": [ - "name" + "rego" ], "properties": { - "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "libs": { + "type": "array", + "items": { + "type": "string" + } }, - "name": { + "rego": { "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" + "example": "Rego 코드" } } }, - "domain.UpdateOrganizationResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse": { "type": "object", "properties": { - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } }, - "phone": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } } } }, - "domain.UpdatePasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse": { "type": "object", - "required": [ - "newPassword", - "originPassword" - ], "properties": { - "newPassword": { - "type": "string" - }, - "originPassword": { - "type": "string" + "deployVersion": { + "type": "object", + "additionalProperties": { + "type": "string" + } } } }, - "domain.UpdatePrimaryClusterRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse": { "type": "object", "properties": { - "primaryClusterId": { - "type": "string" + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" } } }, - "domain.UpdateStackRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" } } }, - "domain.UpdateStackTemplateRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policyTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" + } } } }, - "domain.UpdateUserRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse": { "type": "object", "properties": { - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, - "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 - }, - "email": { - "type": "string" - }, - "name": { - "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "role": { - "type": "string", - "enum": [ - "admin", - "user" - ] + "policyTemplateStatistics": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics" + } } } }, - "domain.UpdateUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "updatedAt": { - "type": "string" - } - } + "versions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "v1.1.0", + "v1.0.1", + "v1.0.0" + ] } } }, - "domain.User": { + "github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse": { "type": "object", "properties": { - "accountId": { - "type": "string" - }, "createdAt": { "type": "string" }, "creator": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "department": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, "description": { - "type": "string" - }, - "email": { - "type": "string" + "type": "string", + "example": "이 정책은 ..." }, "id": { - "type": "string" + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "name": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "password": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "passwordExpired": { - "type": "boolean" + "permittedOrganizations": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" + } }, - "passwordUpdatedAt": { - "type": "string" + "rego": { + "type": "string", + "example": "rego 코드" + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "role": { - "$ref": "#/definitions/domain.Role" + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, - "token": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "tks", + "organization" + ], + "example": "tks" }, "updatedAt": { "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.VerifyIdentityForLostIdRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics": { "type": "object", - "required": [ - "email", - "organizationId", - "userName" - ], "properties": { - "email": { - "type": "string" - }, "organizationId": { "type": "string" }, - "userName": { - "type": "string" - } - } - }, - "domain.VerifyIdentityForLostIdResponse": { - "type": "object", - "properties": { - "validityPeriod": { + "organizationName": { "type": "string" + }, + "usageCount": { + "type": "integer" } } }, - "domain.VerifyIdentityForLostPasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest": { "type": "object", "required": [ - "accountId", - "email", - "organizationId", - "userName" + "templateName" ], "properties": { - "accountId": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "email": { + "description": { "type": "string" }, - "organizationId": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "userName": { - "type": "string" - } - } - }, - "domain.VerifyIdentityForLostPasswordResponse": { - "type": "object", - "properties": { - "validityPeriod": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "httpErrors.RestError": { + "github_com_openinfradev_tks-api_pkg_httpErrors.RestError": { "type": "object", "properties": { "code": { @@ -6854,13 +16800,15 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ Version: "1.0", - Host: "tks-api-ft.taco-cat.xyz", + Host: "tks-api-dev.taco-cat.xyz", BasePath: "/api/1.0/", Schemes: []string{}, Title: "tks-api service", Description: "This is backend api service for tks platform", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", } func init() { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 9392836c..f2faa9d1 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -13,17 +13,17 @@ }, "version": "1.0" }, - "host": "tks-api-ft.taco-cat.xyz", + "host": "tks-api-dev.taco-cat.xyz", "basePath": "/api/1.0/", "paths": { - "/app-groups": { + "/admin/audits": { "get": { "security": [ { "JWT": [] } ], - "description": "Get appGroup list by giving params", + "description": "Get Audits", "consumes": [ "application/json" ], @@ -31,26 +31,20 @@ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Get appGroup list", + "summary": "Get Audits", "parameters": [ - { - "type": "string", - "description": "clusterId", - "name": "clusterId", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -70,8 +64,19 @@ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", - "name": "filters", + "name": "filter", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "or", "in": "query" } ], @@ -79,7 +84,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAppGroupsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse" } } } @@ -90,7 +95,7 @@ "JWT": [] } ], - "description": "Install appGroup", + "description": "Create Audit", "consumes": [ "application/json" ], @@ -98,17 +103,17 @@ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Install appGroup", + "summary": "Create Audit", "parameters": [ { - "description": "create appgroup request", + "description": "create audit request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateAppGroupRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest" } } ], @@ -116,18 +121,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateAppGroupResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse" } } } - }, - "delete": { + } + }, + "/admin/audits/{auditId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Uninstall appGroup", + "description": "Get Audit", "consumes": [ "application/json" ], @@ -135,35 +142,34 @@ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Uninstall appGroup", + "summary": "Get Audit", "parameters": [ { - "description": "body", - "name": "object", - "in": "body", - "required": true, - "schema": { - "type": "string" - } + "type": "string", + "description": "auditId", + "name": "auditId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse" + } } } - } - }, - "/app-groups/{appGroupId}": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get appGroup detail by appGroupId", + "description": "Delete Audit", "consumes": [ "application/json" ], @@ -171,36 +177,33 @@ "application/json" ], "tags": [ - "AppGroups" + "Audits" ], - "summary": "Get appGroup detail", + "summary": "Delete Audit 'NOT IMPLEMENTED'", "parameters": [ { "type": "string", - "description": "appGroupId", - "name": "appGroupId", + "description": "auditId", + "name": "auditId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetAppGroupResponse" - } + "description": "OK" } } } }, - "/app-groups/{appGroupId}/applications": { - "get": { + "/admin/organizations": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get applications", + "description": "Create organization in Admin portal", "consumes": [ "application/json" ], @@ -208,41 +211,38 @@ "application/json" ], "tags": [ - "AppGroups" + "Organizations" ], - "summary": "Get applications", + "summary": "Create organization in Admin portal", "parameters": [ { - "type": "string", - "description": "appGroupId", - "name": "appGroupId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "applicationType", - "name": "applicationType", - "in": "query", - "required": true + "description": "create organization request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetApplicationsResponse" + "type": "object" } } } - }, - "post": { + } + }, + "/admin/organizations/{organizationId}/policyTemplates": { + "put": { "security": [ { "JWT": [] } ], - "description": "Create application", + "description": "특정 조직에 대해 허용된 tks 템플릿의 허용 상태를 해제(제거)한다. tks 우형 템플릿에 대해서만 동작하고 organization 유형 템플릿에 대해서는 거부된다.", "consumes": [ "application/json" ], @@ -250,17 +250,24 @@ "application/json" ], "tags": [ - "AppGroups" + "PolicyTemplate" ], - "summary": "Create application", + "summary": "[Admin_DeletePermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 제거", "parameters": [ { - "description": "body", - "name": "object", + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "delete pemitted policy template request", + "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateApplicationRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest" } } ], @@ -269,11 +276,14 @@ "description": "OK" } } - } - }, - "/auth/find-id/code": { + }, "post": { - "description": "This API allows users to verify their identity for lost id by submitting required information", + "security": [ + { + "JWT": [] + } + ], + "description": "특정 조직에 대해 허용된 tks 템플릿 목록을 추가한다.", "consumes": [ "application/json" ], @@ -281,39 +291,42 @@ "application/json" ], "tags": [ - "Auth" + "PolicyTemplate" ], - "summary": "Request to verify identity for lost id", + "summary": "[Admin_AddPermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 추가", "parameters": [ { - "description": "Request body for verifying identity for lost id including {organization ID, email, username}", + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update pemitted policy template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostIdRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostIdResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" - } + "description": "OK" } } } }, - "/auth/find-id/verification": { - "post": { - "description": "This API allows users to find their account ID by submitting required information", + "/admin/organizations/{organizationId}/projects": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects as admin", "consumes": [ "application/json" ], @@ -321,116 +334,117 @@ "application/json" ], "tags": [ - "Auth" + "Projects" ], - "summary": "Request to find forgotten ID", + "summary": "Get projects as admin", "parameters": [ { - "description": "Request body for finding the account ID including {organization ID, email, username, 6 digit code}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FindIdRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Name", + "name": "projectName", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.FindIdResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse" } } } } }, - "/auth/find-password/code": { - "post": { - "description": "This API allows users to verify their identity for lost password by submitting required information", - "consumes": [ - "application/json" + "/admin/organizations/{organizationId}/roles": { + "get": { + "security": [ + { + "JWT": [] + } ], + "description": "Admin List Tks Roles", "produces": [ "application/json" ], "tags": [ - "Auth" + "Roles" ], - "summary": "Request to verify identity for lost password", + "summary": "Admin List Tks Roles", "parameters": [ { - "description": "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostPasswordRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.VerifyIdentityForLostPasswordResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse" } } } } }, - "/auth/find-password/verification": { - "post": { - "description": "This API allows users to reset their forgotten password by submitting required information", - "consumes": [ - "application/json" + "/admin/organizations/{organizationId}/roles/{roleId}": { + "get": { + "security": [ + { + "JWT": [] + } ], + "description": "Admin Get Tks Role", "produces": [ "application/json" ], "tags": [ - "Auth" + "Roles" ], - "summary": "Request to find forgotten password", + "summary": "Admin Get Tks Role", "parameters": [ { - "description": "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.FindPasswordRequest" - } + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request", + "description": "OK", "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" } } } } }, - "/auth/login": { + "/admin/organizations/{organizationId}/users": { "post": { - "description": "login", + "security": [ + { + "JWT": [] + } + ], + "description": "Create user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -438,38 +452,45 @@ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "login", + "summary": "Create user by admin in Admin Portal", "parameters": [ { - "description": "account info", + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create user request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.LoginRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest" } } ], "responses": { "200": { - "description": "user detail", + "description": "create user response", "schema": { - "$ref": "#/definitions/domain.LoginResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse" } } } } }, - "/auth/logout": { - "post": { + "/admin/organizations/{organizationId}/users/{accountId}": { + "put": { "security": [ { "JWT": [] } ], - "description": "logout", + "description": "Update user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -477,22 +498,50 @@ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "logout", - "responses": { - "200": { - "description": "OK", + "summary": "Update user by admin in Admin Portal", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/domain.LogoutResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse" } } } - } - }, - "/auth/ping": { - "post": { - "description": "ping with token", + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete user by admin in Admin Portal", "consumes": [ "application/json" ], @@ -500,18 +549,32 @@ "application/json" ], "tags": [ - "Auth" + "Users" ], - "summary": "ping with token", + "summary": "Delete user by admin in Admin Portal", "parameters": [ { - "description": "token info", + "description": "input", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.PingTokenRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest" } + }, + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true } ], "responses": { @@ -521,14 +584,14 @@ } } }, - "/clusters": { + "/admin/policy-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster list", + "description": "정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다.", "consumes": [ "application/json" ], @@ -536,32 +599,26 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get clusters", + "summary": "[Admin_ListPolicyTemplate] 정책 템플릿 목록 조회", "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { "type": "string", "description": "sortColumn", - "name": "soertColumn", + "name": "sortColumn", "in": "query" }, { @@ -575,6 +632,7 @@ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -584,7 +642,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetClustersResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse" } } } @@ -595,7 +653,7 @@ "JWT": [] } ], - "description": "Create cluster", + "description": "정책 템플릿을 신규 생성(v1.0.0을 생성)한다.", "consumes": [ "application/json" ], @@ -603,17 +661,17 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Create cluster", + "summary": "[Admin_CreatePolicyTemplate] 정책 템플릿 신규 생성", "parameters": [ { - "description": "create cluster request", + "description": "create policy template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateClusterRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest" } } ], @@ -621,20 +679,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateClusterResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse" } } } } }, - "/clusters/import": { - "post": { + "/admin/policy-templates/kind/{policyTemplateKind}/existence": { + "get": { "security": [ { "JWT": [] } ], - "description": "Import cluster", + "description": "해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다.", "consumes": [ "application/json" ], @@ -642,38 +700,36 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Import cluster", + "summary": "[Admin_ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인", "parameters": [ { - "description": "import cluster request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.ImportClusterRequest" - } + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateKind", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.ImportClusterResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse" } } } } }, - "/clusters/{clusterId}": { + "/admin/policy-templates/name/{policyTemplateName}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster detail", + "description": "해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다.", "consumes": [ "application/json" ], @@ -681,14 +737,14 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get cluster", + "summary": "[Admin_ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 이름", + "name": "policyTemplateName", "in": "path", "required": true } @@ -697,18 +753,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Cluster" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse" } } } - }, - "delete": { + } + }, + "/admin/policy-templates/{policyTemplateId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete cluster", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", "consumes": [ "application/json" ], @@ -716,14 +774,14 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Delete cluster", + "summary": "[Admin_GetPolicyTemplate] 정책 템플릿 조회(최신 버전)", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -732,20 +790,18 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Cluster" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse" } } } - } - }, - "/clusters/{clusterId}/bootstrap-kubeconfig": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get bootstrap kubeconfig for BYOH", + "description": "정책 템플릿을 삭제한다.", "consumes": [ "application/json" ], @@ -753,53 +809,31 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get bootstrap kubeconfig for BYOH", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetBootstrapKubeconfigResponse" - } - } - } - }, - "post": { - "security": [ + "summary": "[Admin_DeletePolicyTemplate] 정책 템플릿 삭제", + "parameters": [ { - "JWT": [] + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true } ], - "description": "Create bootstrap kubeconfig for BYOH", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Clusters" - ], - "summary": "Create bootstrap kubeconfig for BYOH", "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateBootstrapKubeconfigResponse" - } + "description": "OK" } } - } - }, - "/clusters/{clusterId}/install": { - "post": { + }, + "patch": { "security": [ { "JWT": [] } ], - "description": "Install cluster on tks cluster", + "description": "정책 템플릿의 업데이트 가능한 필드들을 업데이트한다.", "consumes": [ "application/json" ], @@ -807,16 +841,25 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Install cluster on tks cluster", + "summary": "[Admin_UpdatePolicyTemplate] 정책 템플릿 업데이트", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true + }, + { + "description": "update policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest" + } } ], "responses": { @@ -826,14 +869,14 @@ } } }, - "/clusters/{clusterId}/nodes": { + "/admin/policy-templates/{policyTemplateId}/deploy": { "get": { "security": [ { "JWT": [] } ], - "description": "Get nodes information for BYOH", + "description": "해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다.", "consumes": [ "application/json" ], @@ -841,14 +884,14 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get nodes information for BYOH", + "summary": "[Admin_GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -857,20 +900,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetClusterNodesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse" } } } } }, - "/clusters/{clusterId}/site-values": { + "/admin/policy-templates/{policyTemplateId}/statistics": { "get": { "security": [ { "JWT": [] } ], - "description": "Get cluster site values for creating", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다.", "consumes": [ "application/json" ], @@ -878,14 +921,14 @@ "application/json" ], "tags": [ - "Clusters" + "PolicyTemplate" ], - "summary": "Get cluster site values for creating", + "summary": "[Admin_ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회", "parameters": [ { "type": "string", - "description": "clusterId", - "name": "clusterId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true } @@ -894,20 +937,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.ClusterSiteValuesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse" } } } } }, - "/organizations": { + "/admin/policy-templates/{policyTemplateId}/versions": { "get": { "security": [ { "JWT": [] } ], - "description": "Get organization list", + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", "consumes": [ "application/json" ], @@ -915,52 +958,23 @@ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Get organization list", + "summary": "[Admin_ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회", "parameters": [ { "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", - "in": "query" - }, - { - "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" - }, - { - "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ListOrganizationBody" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse" } } } @@ -971,7 +985,7 @@ "JWT": [] } ], - "description": "Create organization", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다.", "consumes": [ "application/json" ], @@ -979,17 +993,24 @@ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Create organization", + "summary": "[Admin_CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장", "parameters": [ { - "description": "create organization request", + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "create policy template version request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateOrganizationRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest" } } ], @@ -997,20 +1018,20 @@ "200": { "description": "OK", "schema": { - "type": "object" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse" } } } } }, - "/organizations/{organizationId}": { + "/admin/policy-templates/{policyTemplateId}/versions/{version}": { "get": { "security": [ { "JWT": [] } ], - "description": "Get organization detail", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다.", "consumes": [ "application/json" ], @@ -1018,14 +1039,21 @@ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Get organization detail", + "summary": "[Admin_GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "조회할 버전(v0.0.0 형식)", + "name": "version", "in": "path", "required": true } @@ -1034,18 +1062,18 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetOrganizationResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse" } } } }, - "put": { + "delete": { "security": [ { "JWT": [] } ], - "description": "Update organization detail", + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다.", "consumes": [ "application/json" ], @@ -1053,43 +1081,40 @@ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Update organization detail", + "summary": "[Admin_DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", "in": "path", "required": true }, { - "description": "update organization request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateOrganizationRequest" - } + "type": "string", + "description": "삭제할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.UpdateOrganizationResponse" - } + "description": "OK" } } - }, - "delete": { + } + }, + "/admin/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters": { + "post": { "security": [ { "JWT": [] } ], - "description": "Delete organization", + "description": "정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다.", "consumes": [ "application/json" ], @@ -1097,36 +1122,52 @@ "application/json" ], "tags": [ - "Organizations" + "PolicyTemplate" ], - "summary": "Delete organization", + "summary": "[Admin_ExtractParameters] 정책 템플릿 파라미터 추출", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "버전(v0.0.0 형식)", + "name": "version", "in": "path", "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.Organization" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse" } } } } }, - "/organizations/{organizationId}/alerts": { + "/admin/stack-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Alerts", + "description": "Get StackTemplates", "consumes": [ "application/json" ], @@ -1134,27 +1175,20 @@ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Get Alerts", + "summary": "Get StackTemplates", "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -1174,6 +1208,7 @@ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -1183,20 +1218,57 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAlertsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Create StackTemplate", + "parameters": [ + { + "description": "create stack template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse" } } } } }, - "/organizations/{organizationId}/alerts/{alertId}": { + "/admin/stack-templates/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Alert", + "description": "Check name for stackTemplate", "consumes": [ "application/json" ], @@ -1204,21 +1276,107 @@ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Get Alert", + "summary": "Check name for stackTemplate", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true - }, + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse" + } + } + } + } + }, + "/admin/stack-templates/services": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get GetStackTemplateServices", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get GetStackTemplateServices", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse" + } + } + } + } + }, + "/admin/stack-templates/template-ids": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get GetStackTemplateTemplateIds", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get GetStackTemplateTemplateIds", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse" + } + } + } + } + }, + "/admin/stack-templates/{stackTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get StackTemplate", + "parameters": [ { "type": "string", - "description": "alertId", - "name": "alertId", + "description": "stackTemplateId", + "name": "stackTemplateId", "in": "path", "required": true } @@ -1227,7 +1385,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAlertResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse" } } } @@ -1238,7 +1396,7 @@ "JWT": [] } ], - "description": "Update Alert", + "description": "Update StackTemplate", "consumes": [ "application/json" ], @@ -1246,24 +1404,17 @@ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Update Alert", + "summary": "Update StackTemplate", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Update cloud setting request", + "description": "Update stack template request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAlertRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest" } } ], @@ -1279,7 +1430,7 @@ "JWT": [] } ], - "description": "Delete Alert", + "description": "Delete StackTemplate", "consumes": [ "application/json" ], @@ -1287,21 +1438,14 @@ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Delete Alert", + "summary": "Delete StackTemplate", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "alertId", - "name": "alertId", + "description": "stackTemplateId", + "name": "stackTemplateId", "in": "path", "required": true } @@ -1313,14 +1457,14 @@ } } }, - "/organizations/{organizationId}/alerts/{alertId}/actions": { - "post": { + "/admin/stack-templates/{stackTemplateId}/organizations": { + "put": { "security": [ { "JWT": [] } ], - "description": "Create alert action", + "description": "Update StackTemplate organizations", "consumes": [ "application/json" ], @@ -1328,16 +1472,18 @@ "application/json" ], "tags": [ - "Alerts" + "StackTemplates" ], - "summary": "Create alert action", + "summary": "Update StackTemplate organizations", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true + "description": "Update stack template organizations request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest" + } } ], "responses": { @@ -1347,14 +1493,14 @@ } } }, - "/organizations/{organizationId}/app-serve-apps": { + "/admin/system-notification-templates": { "get": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp list by giving params", + "description": "Get SystemNotificationTemplates", "consumes": [ "application/json" ], @@ -1362,33 +1508,20 @@ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get appServeApp list", + "summary": "Get SystemNotificationTemplates", "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "boolean", - "description": "Show all apps including deleted apps", - "name": "showAll", - "in": "query" - }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -1408,6 +1541,7 @@ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -1417,10 +1551,7 @@ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AppServeApp" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse" } } } @@ -1431,7 +1562,7 @@ "JWT": [] } ], - "description": "Install appServeApp", + "description": "Create alert template. ADMIN ONLY", "consumes": [ "application/json" ], @@ -1439,45 +1570,27 @@ "application/json" ], "tags": [ - "AppServeApps" - ], - "summary": "Install appServeApp", - "parameters": [ - { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Request body to create app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateAppServeAppRequest" - } - } + "SystemNotificationTemplates" ], + "summary": "Create alert template. ADMIN ONLY", "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest" } } } } }, - "/organizations/{organizationId}/app-serve-apps/count": { + "/admin/system-notification-templates/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get number of apps on given stack", + "description": "Check name for systemNotificationTemplate", "consumes": [ "application/json" ], @@ -1485,43 +1598,36 @@ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get number of apps on given stack", + "summary": "Check name for systemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true - }, - { - "type": "string", - "description": "Stack ID", - "name": "stackId", - "in": "query", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "integer" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse" } } } } }, - "/organizations/{organizationId}/app-serve-apps/name/{name}/existence": { + "/admin/system-notification-templates/{systemNotificationTemplateId}": { "get": { "security": [ { "JWT": [] } ], - "description": "Check duplicate appServeAppName by giving params", + "description": "Get SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1529,21 +1635,14 @@ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Check duplicate appServeAppName", + "summary": "Get SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true } @@ -1552,20 +1651,18 @@ "200": { "description": "OK", "schema": { - "type": "boolean" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}": { - "get": { + }, + "put": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp by giving params", + "description": "Update SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1573,41 +1670,40 @@ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Get appServeApp", + "summary": "Update SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true }, { - "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true + "description": "Update alert template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest" + } } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetAppServeAppResponse" - } + "description": "OK" } } }, - "put": { + "delete": { "security": [ { "JWT": [] } ], - "description": "Update appServeApp", + "description": "Delete SystemNotificationTemplate", "consumes": [ "application/json" ], @@ -1615,50 +1711,33 @@ "application/json" ], "tags": [ - "AppServeApps" + "SystemNotificationTemplates" ], - "summary": "Update appServeApp", + "summary": "Delete SystemNotificationTemplate", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", "in": "path", "required": true - }, - { - "description": "Request body to update app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppRequest" - } } ], "responses": { "200": { - "description": "OK", - "schema": { - "type": "string" - } + "description": "OK" } } - }, - "delete": { + } + }, + "/app-groups": { + "get": { "security": [ { "JWT": [] } ], - "description": "Uninstall appServeApp", + "description": "Get appGroup list by giving params", "consumes": [ "application/json" ], @@ -1666,43 +1745,67 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Uninstall appServeApp", + "summary": "Get appGroup list", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true + "description": "clusterId", + "name": "clusterId", + "in": "query" }, { "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/endpoint": { - "patch": { + }, + "post": { "security": [ { "JWT": [] } ], - "description": "Update app endpoint", + "description": "Install appGroup", "consumes": [ "application/json" ], @@ -1710,31 +1813,17 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Update app endpoint", + "summary": "Install appGroup", "parameters": [ { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "appId", - "name": "appId", - "in": "path", - "required": true - }, - { - "description": "Request body to update app endpoint", + "description": "create appgroup request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppEndpointRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest" } } ], @@ -1742,20 +1831,18 @@ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/exist": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get appServeApp by giving params", + "description": "Uninstall appGroup", "consumes": [ "application/json" ], @@ -1763,27 +1850,35 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Get appServeApp", - "responses": { - "200": { - "description": "OK", + "summary": "Uninstall appGroup", + "parameters": [ + { + "description": "body", + "name": "object", + "in": "body", + "required": true, "schema": { - "type": "boolean" + "type": "string" } } + ], + "responses": { + "200": { + "description": "OK" + } } } }, - "/organizations/{organizationId}/app-serve-apps/{appId}/latest-task": { + "/app-groups/{appGroupId}": { "get": { "security": [ { "JWT": [] } ], - "description": "Get latest task from appServeApp", + "description": "Get appGroup detail by appGroupId", "consumes": [ "application/json" ], @@ -1791,21 +1886,14 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Get latest task from appServeApp", + "summary": "Get appGroup detail", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", + "description": "appGroupId", + "name": "appGroupId", "in": "path", "required": true } @@ -1814,20 +1902,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetAppServeAppTaskResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse" } } } } }, - "/organizations/{organizationId}/app-serve-apps/{appId}/rollback": { - "post": { + "/app-groups/{appGroupId}/applications": { + "get": { "security": [ { "JWT": [] } ], - "description": "Rollback appServeApp", + "description": "Get applications", "consumes": [ "application/json" ], @@ -1835,52 +1923,41 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Rollback appServeApp", + "summary": "Get applications", "parameters": [ { "type": "string", - "description": "Organization ID", - "name": "organizationId", + "description": "appGroupId", + "name": "appGroupId", "in": "path", "required": true }, { "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", + "description": "applicationType", + "name": "applicationType", + "in": "query", "required": true - }, - { - "description": "Request body to rollback app", - "name": "object", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.RollbackAppServeAppRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse" } } } - } - }, - "/organizations/{organizationId}/app-serve-apps/{appId}/status": { - "patch": { + }, + "post": { "security": [ { "JWT": [] } ], - "description": "Update app status", + "description": "Create application", "consumes": [ "application/json" ], @@ -1888,52 +1965,30 @@ "application/json" ], "tags": [ - "AppServeApps" + "AppGroups" ], - "summary": "Update app status", + "summary": "Create application", "parameters": [ { - "type": "string", - "description": "Organization ID", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "App ID", - "name": "appId", - "in": "path", - "required": true - }, - { - "description": "Request body to update app status", - "name": "body", + "description": "body", + "name": "object", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateAppServeAppStatusRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest" } } ], "responses": { "200": { - "description": "OK", - "schema": { - "type": "string" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/cloud-accounts": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Get CloudAccounts", + "/auth/find-id/code": { + "post": { + "description": "This API allows users to verify their identity for lost id by submitting required information", "consumes": [ "application/json" ], @@ -1941,67 +1996,39 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Get CloudAccounts", + "summary": "Request to verify identity for lost id", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", - "in": "query" - }, - { - "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" - }, - { - "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "Request body for verifying identity for lost id including {organization ID, email, username}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetCloudAccountsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } - }, + } + }, + "/auth/find-id/verification": { "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Create CloudAccount", + "description": "This API allows users to find their account ID by submitting required information", "consumes": [ "application/json" ], @@ -2009,24 +2036,17 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Create CloudAccount", + "summary": "Request to find forgotten ID", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "create cloud setting request", + "description": "Request body for finding the account ID including {organization ID, email, username, 6 digit code}", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdRequest" } } ], @@ -2034,20 +2054,21 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateCloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } } }, - "/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Check awsAccountId for cloudAccount", + "/auth/find-password/code": { + "post": { + "description": "This API allows users to verify their identity for lost password by submitting required information", "consumes": [ "application/json" ], @@ -2055,43 +2076,76 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Check awsAccountId for cloudAccount", + "summary": "Request to verify identity for lost password", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "awsAccountId", - "name": "awsAccountId", - "in": "path", - "required": true + "description": "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckCloudAccountAwsAccountIdResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" } } } } }, - "/organizations/{organizationId}/cloud-accounts/name/{name}/existence": { - "get": { - "security": [ + "/auth/find-password/verification": { + "post": { + "description": "This API allows users to reset their forgotten password by submitting required information", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "Request to find forgotten password", + "parameters": [ { - "JWT": [] + "description": "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest" + } } ], - "description": "Check name for cloudAccount", + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/auth/login": { + "post": { + "description": "login", "consumes": [ "application/json" ], @@ -2099,43 +2153,59 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Auth" ], - "summary": "Check name for cloudAccount", + "summary": "login", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", - "in": "path", - "required": true + "description": "account info", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginRequest" + } } ], "responses": { "200": { - "description": "OK", + "description": "user detail", "schema": { - "$ref": "#/definitions/domain.CheckCloudAccountNameResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginResponse" } } } } }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}": { + "/auth/logout": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "logout", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Auth" + ], + "summary": "logout", + "responses": {} + } + }, + "/clusters": { "get": { "security": [ { "JWT": [] } ], - "description": "Get CloudAccount", + "description": "Get cluster list", "consumes": [ "application/json" ], @@ -2143,41 +2213,67 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Get CloudAccount", + "summary": "Get clusters", "parameters": [ { "type": "string", "description": "organizationId", "name": "organizationId", - "in": "path", - "required": true + "in": "query" }, { "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetCloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse" } } } }, - "put": { + "post": { "security": [ { "JWT": [] } ], - "description": "Update CloudAccount", + "description": "Create cluster", "consumes": [ "application/json" ], @@ -2185,40 +2281,38 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Update CloudAccount", + "summary": "Create cluster", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Update cloud setting request", + "description": "create cluster request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse" + } } } - }, - "delete": { + } + }, + "/clusters/import": { + "post": { "security": [ { "JWT": [] } ], - "description": "Delete CloudAccount", + "description": "Import cluster", "consumes": [ "application/json" ], @@ -2226,49 +2320,38 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Delete CloudAccount", + "summary": "Import cluster", "parameters": [ { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Delete cloud setting request", + "description": "import cluster request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.DeleteCloudAccountRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest" } - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", - "in": "path", - "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse" + } } } } }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error": { - "delete": { + "/clusters/{clusterId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete Force CloudAccount", + "description": "Get cluster detail", "consumes": [ "application/json" ], @@ -2276,40 +2359,34 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Delete Force CloudAccount", + "summary": "Get cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse" + } } } - } - }, - "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { - "get": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Get resource quota by cloudAccount", + "description": "Delete cluster", "consumes": [ "application/json" ], @@ -2317,43 +2394,33 @@ "application/json" ], "tags": [ - "CloudAccounts" + "Clusters" ], - "summary": "Get resource quota by cloudAccount", + "summary": "Delete cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "cloudAccountId", - "name": "cloudAccountId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetCloudAccountResourceQuotaResponse" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/dashboard/charts": { + "/clusters/{clusterId}/bootstrap-kubeconfig": { "get": { "security": [ { "JWT": [] } ], - "description": "Get charts data", + "description": "Get bootstrap kubeconfig for BYOH", "consumes": [ "application/json" ], @@ -2361,56 +2428,53 @@ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get charts data", - "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "chartType", - "name": "chartType", - "in": "query" - }, - { - "type": "string", - "description": "duration", - "name": "duration", - "in": "query", - "required": true - }, + "summary": "Get bootstrap kubeconfig for BYOH", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse" + } + } + } + }, + "post": { + "security": [ { - "type": "string", - "description": "interval", - "name": "interval", - "in": "query", - "required": true + "JWT": [] } ], + "description": "Create bootstrap kubeconfig for BYOH", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Clusters" + ], + "summary": "Create bootstrap kubeconfig for BYOH", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardChartsResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse" } } } } }, - "/organizations/{organizationId}/dashboard/charts/{chartType}": { - "get": { + "/clusters/{clusterId}/install": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get chart data", + "description": "Install cluster on tks cluster", "consumes": [ "application/json" ], @@ -2418,57 +2482,33 @@ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get chart data", + "summary": "Install cluster on tks cluster", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "chartType", - "name": "chartType", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true - }, - { - "type": "string", - "description": "duration", - "name": "duration", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "interval", - "name": "interval", - "in": "query", - "required": true } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.GetDashboardChartResponse" - } + "description": "OK" } } } }, - "/organizations/{organizationId}/dashboard/resources": { + "/clusters/{clusterId}/nodes": { "get": { "security": [ { "JWT": [] } ], - "description": "Get resources", + "description": "Get nodes information for BYOH", "consumes": [ "application/json" ], @@ -2476,14 +2516,14 @@ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get resources", + "summary": "Get nodes information for BYOH", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } @@ -2492,20 +2532,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardResourcesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse" } } } } }, - "/organizations/{organizationId}/dashboard/stacks": { + "/clusters/{clusterId}/site-values": { "get": { "security": [ { "JWT": [] } ], - "description": "Get stacks", + "description": "Get cluster site values for creating", "consumes": [ "application/json" ], @@ -2513,14 +2553,14 @@ "application/json" ], "tags": [ - "Dashboards" + "Clusters" ], - "summary": "Get stacks", + "summary": "Get cluster site values for creating", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "clusterId", + "name": "clusterId", "in": "path", "required": true } @@ -2529,20 +2569,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetDashboardStacksResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse" } } } } }, - "/organizations/{organizationId}/my-profile": { + "/organizations": { "get": { "security": [ { "JWT": [] } ], - "description": "Get my profile detail", + "description": "Get organization list", "consumes": [ "application/json" ], @@ -2550,34 +2590,66 @@ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Get my profile detail", + "summary": "Get organization list", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetMyProfileResponse" + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse" + } } } } - }, - "put": { + } + }, + "/organizations/name/{name}/existence": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update my profile detail", + "description": "Check name for organization", "consumes": [ "application/json" ], @@ -2585,58 +2657,14 @@ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update my profile detail", + "summary": "Check name for organization", "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", - "in": "path", - "required": true - }, - { - "description": "Required fields: password due to double-check", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateMyProfileRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.UpdateMyProfileResponse" - } - } - } - }, - "delete": { - "security": [ - { - "JWT": [] - } - ], - "description": "Delete myProfile", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "My-profile" - ], - "summary": "Delete myProfile", - "parameters": [ - { - "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "name", + "name": "name", "in": "path", "required": true } @@ -2644,21 +2672,18 @@ "responses": { "200": { "description": "OK" - }, - "400": { - "description": "Bad Request" } } } }, - "/organizations/{organizationId}/my-profile/next-password-change": { - "put": { + "/organizations/{organizationId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update user's password expired date to current date", + "description": "Get organization detail", "consumes": [ "application/json" ], @@ -2666,9 +2691,9 @@ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update user's password expired date to current date", + "summary": "Get organization detail", "parameters": [ { "type": "string", @@ -2680,25 +2705,20 @@ ], "responses": { "200": { - "description": "OK" - }, - "400": { - "description": "Bad Request", + "description": "OK", "schema": { - "$ref": "#/definitions/httpErrors.RestError" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse" } } } - } - }, - "/organizations/{organizationId}/my-profile/password": { + }, "put": { "security": [ { "JWT": [] } ], - "description": "Update user password detail", + "description": "Update organization detail", "consumes": [ "application/json" ], @@ -2706,9 +2726,9 @@ "application/json" ], "tags": [ - "My-profile" + "Organizations" ], - "summary": "Update user password detail", + "summary": "Update organization detail", "parameters": [ { "type": "string", @@ -2718,30 +2738,31 @@ "required": true }, { - "description": "update user password request", + "description": "update organization request", "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdatePasswordRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse" + } } } - } - }, - "/organizations/{organizationId}/primary-cluster": { - "patch": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Update primary cluster", + "description": "Delete organization", "consumes": [ "application/json" ], @@ -2751,7 +2772,7 @@ "tags": [ "Organizations" ], - "summary": "Update primary cluster", + "summary": "Delete organization", "parameters": [ { "type": "string", @@ -2759,32 +2780,26 @@ "name": "organizationId", "in": "path", "required": true - }, - { - "description": "update primary cluster request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdatePrimaryClusterRequest" - } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse" + } } } } }, - "/organizations/{organizationId}/stacks": { + "/organizations/{organizationId}/cloud-accounts": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Stacks", + "description": "Get CloudAccounts", "consumes": [ "application/json" ], @@ -2792,9 +2807,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get Stacks", + "summary": "Get CloudAccounts", "parameters": [ { "type": "string", @@ -2806,13 +2821,13 @@ { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -2828,9 +2843,13 @@ "in": "query" }, { - "type": "string", - "description": "combinedFilter", - "name": "combinedFilter", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", "in": "query" } ], @@ -2838,7 +2857,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStacksResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse" } } } @@ -2849,7 +2868,7 @@ "JWT": [] } ], - "description": "Create Stack", + "description": "Create CloudAccount", "consumes": [ "application/json" ], @@ -2857,9 +2876,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Create Stack", + "summary": "Create CloudAccount", "parameters": [ { "type": "string", @@ -2874,7 +2893,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.CreateStackRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest" } } ], @@ -2882,20 +2901,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CreateStackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse" } } } } }, - "/organizations/{organizationId}/stacks/name/{name}/existence": { + "/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Check name for stack", + "description": "Check awsAccountId for cloudAccount", "consumes": [ "application/json" ], @@ -2903,9 +2922,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Check name for stack", + "summary": "Check awsAccountId for cloudAccount", "parameters": [ { "type": "string", @@ -2916,34 +2935,30 @@ }, { "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "name", - "name": "name", + "description": "awsAccountId", + "name": "awsAccountId", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse" + } } } } }, - "/organizations/{organizationId}/stacks/{stackId}": { + "/organizations/{organizationId}/cloud-accounts/name/{name}/existence": { "get": { "security": [ { "JWT": [] } ], - "description": "Get Stack", + "description": "Check name for cloudAccount", "consumes": [ "application/json" ], @@ -2951,9 +2966,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get Stack", + "summary": "Check name for cloudAccount", "parameters": [ { "type": "string", @@ -2964,8 +2979,8 @@ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "name", + "name": "name", "in": "path", "required": true } @@ -2974,18 +2989,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse" } } } - }, - "put": { + } + }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update Stack", + "description": "Get CloudAccount", "consumes": [ "application/json" ], @@ -2993,9 +3010,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Update Stack", + "summary": "Get CloudAccount", "parameters": [ { "type": "string", @@ -3006,34 +3023,28 @@ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true - }, - { - "description": "Update cloud setting request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateStackRequest" - } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse" + } } } }, - "delete": { + "put": { "security": [ { "JWT": [] } ], - "description": "Delete Stack", + "description": "Update CloudAccount", "consumes": [ "application/json" ], @@ -3041,9 +3052,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Delete Stack", + "summary": "Update CloudAccount", "parameters": [ { "type": "string", @@ -3053,11 +3064,13 @@ "required": true }, { - "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest" + } } ], "responses": { @@ -3065,16 +3078,14 @@ "description": "OK" } } - } - }, - "/organizations/{organizationId}/stacks/{stackId}/favorite": { - "post": { + }, + "delete": { "security": [ { "JWT": [] } ], - "description": "Set favorite stack", + "description": "Delete CloudAccount", "consumes": [ "application/json" ], @@ -3082,9 +3093,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Set favorite stack", + "summary": "Delete CloudAccount", "parameters": [ { "type": "string", @@ -3093,10 +3104,19 @@ "in": "path", "required": true }, + { + "description": "Delete cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest" + } + }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3106,14 +3126,16 @@ "description": "OK" } } - }, + } + }, + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error": { "delete": { "security": [ { "JWT": [] } ], - "description": "Delete favorite stack", + "description": "Delete Force CloudAccount", "consumes": [ "application/json" ], @@ -3121,9 +3143,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Delete favorite stack", + "summary": "Delete Force CloudAccount", "parameters": [ { "type": "string", @@ -3134,8 +3156,8 @@ }, { "type": "string", - "description": "stackId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3147,14 +3169,14 @@ } } }, - "/organizations/{organizationId}/stacks/{stackId}/kube-config": { + "/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota": { "get": { "security": [ { "JWT": [] } ], - "description": "Get KubeConfig by stack", + "description": "Get resource quota by cloudAccount", "consumes": [ "application/json" ], @@ -3162,9 +3184,9 @@ "application/json" ], "tags": [ - "Stacks" + "CloudAccounts" ], - "summary": "Get KubeConfig by stack", + "summary": "Get resource quota by cloudAccount", "parameters": [ { "type": "string", @@ -3175,8 +3197,8 @@ }, { "type": "string", - "description": "organizationId", - "name": "stackId", + "description": "cloudAccountId", + "name": "cloudAccountId", "in": "path", "required": true } @@ -3185,20 +3207,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackKubeConfigResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse" } } } } }, - "/organizations/{organizationId}/stacks/{stackId}/status": { - "get": { + "/organizations/{organizationId}/dashboards": { + "post": { "security": [ { "JWT": [] } ], - "description": "Get Stack Status", + "description": "Create new dashboard", "consumes": [ "application/json" ], @@ -3206,43 +3228,45 @@ "application/json" ], "tags": [ - "Stacks" + "Dashboards" ], - "summary": "Get Stack Status", + "summary": "Create new dashboard", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true }, { - "type": "string", - "description": "stackId", - "name": "stackId", - "in": "path", - "required": true + "description": "Request body to create dashboard", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackStatusResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse" } } } } }, - "/organizations/{organizationId}/users": { + "/organizations/{organizationId}/dashboards/widgets/charts": { "get": { "security": [ { "JWT": [] } ], - "description": "Get user list", + "description": "Get charts data", "consumes": [ "application/json" ], @@ -3250,9 +3274,9 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user list", + "summary": "Get charts data", "parameters": [ { "type": "string", @@ -3263,57 +3287,43 @@ }, { "type": "string", - "description": "pageSize", - "name": "limit", - "in": "query" - }, - { - "type": "string", - "description": "pageNumber", - "name": "page", + "description": "chartType", + "name": "chartType", "in": "query" }, { "type": "string", - "description": "sortColumn", - "name": "soertColumn", - "in": "query" + "description": "duration", + "name": "duration", + "in": "query", + "required": true }, { "type": "string", - "description": "sortOrder", - "name": "sortOrder", - "in": "query" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "description": "filters", - "name": "filters", - "in": "query" + "description": "interval", + "name": "interval", + "in": "query", + "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ListUserBody" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse" } } } - }, - "post": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/charts/{chartType}": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create user", + "description": "Get chart data", "consumes": [ "application/json" ], @@ -3321,9 +3331,9 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Create user", + "summary": "Get chart data", "parameters": [ { "type": "string", @@ -3332,54 +3342,25 @@ "in": "path", "required": true }, - { - "description": "create user request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateUserRequest" - } - } - ], - "responses": { - "200": { - "description": "create user response", - "schema": { - "$ref": "#/definitions/domain.CreateUserResponse" - } - } - } - } - }, - "/organizations/{organizationId}/users/account-id/{accountId}/existence": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "return true when accountId exists", - "produces": [ - "application/json" - ], - "tags": [ - "Users" - ], - "summary": "Get user id existence", - "parameters": [ { "type": "string", - "description": "organizationId", - "name": "organizationId", + "description": "chartType", + "name": "chartType", "in": "path", "required": true }, { "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", "required": true } ], @@ -3387,61 +3368,57 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckExistedResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse" } } } } }, - "/organizations/{organizationId}/users/email/{email}/existence": { + "/organizations/{organizationId}/dashboards/widgets/policy-enforcement": { "get": { "security": [ { "JWT": [] } ], - "description": "return true when email exists", + "description": "Get the number of policy enforcement", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user email existence", + "summary": "Get the number of policy enforcement", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "email", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.CheckExistedResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse" } } } } }, - "/organizations/{organizationId}/users/{accountId}": { + "/organizations/{organizationId}/dashboards/widgets/policy-statistics": { "get": { "security": [ { "JWT": [] } ], - "description": "Get user detail", + "description": "Get policy violation log", "consumes": [ "application/json" ], @@ -3449,41 +3426,36 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Get user detail", + "summary": "Get policy violation log", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse" } } } - }, - "put": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-status": { + "get": { "security": [ { "JWT": [] } ], - "description": "Update user", + "description": "Get policy status", "consumes": [ "application/json" ], @@ -3491,50 +3463,36 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Update user", + "summary": "Get policy status", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true - }, - { - "description": "input", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateUserRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.UpdateUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse" } } } - }, - "delete": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-update": { + "get": { "security": [ { "JWT": [] } ], - "description": "Delete user", + "description": "Get the number of policytemplates that need to be updated", "consumes": [ "application/json" ], @@ -3542,43 +3500,36 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Delete user", + "summary": "Get the number of policytemplates that need to be updated", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true - }, - { - "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", - "required": true } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.User" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse" } } } } }, - "/organizations/{organizationId}/users/{accountId}/reset-password": { - "put": { + "/organizations/{organizationId}/dashboards/widgets/policy-violation": { + "get": { "security": [ { "JWT": [] } ], - "description": "Reset user's password as temporary password by admin and send email to user", + "description": "Get the number of policy violation", "consumes": [ "application/json" ], @@ -3586,40 +3537,50 @@ "application/json" ], "tags": [ - "Users" + "Dashboard Widgets" ], - "summary": "Reset user's password as temporary password by admin", + "summary": "Get the number of policy violation", "parameters": [ { "type": "string", - "description": "organizationId", + "description": "Organization ID", "name": "organizationId", "in": "path", "required": true }, { "type": "string", - "description": "accountId", - "name": "accountId", - "in": "path", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse" + } } } } }, - "/stack-templates": { + "/organizations/{organizationId}/dashboards/widgets/policy-violation-log": { "get": { "security": [ { "JWT": [] } ], - "description": "Get StackTemplates", + "description": "Get policy violation log", "consumes": [ "application/json" ], @@ -3627,20 +3588,27 @@ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Get StackTemplates", + "summary": "Get policy violation log", "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, { "type": "string", "description": "pageSize", - "name": "limit", + "name": "pageSize", "in": "query" }, { "type": "string", "description": "pageNumber", - "name": "page", + "name": "pageNumber", "in": "query" }, { @@ -3660,6 +3628,7 @@ "items": { "type": "string" }, + "collectionFormat": "csv", "description": "filters", "name": "filters", "in": "query" @@ -3669,18 +3638,20 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackTemplatesResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" } } } - }, - "post": { + } + }, + "/organizations/{organizationId}/dashboards/widgets/policy-violation-top5": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create StackTemplate", + "description": "Get policy violation top5", "consumes": [ "application/json" ], @@ -3688,38 +3659,50 @@ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Create StackTemplate 'NOT IMPLEMENTED'", + "summary": "Get policy violation top5", "parameters": [ { - "description": "create stack template request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.CreateStackTemplateRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.CreateStackTemplateResponse" - } - } - } - } - }, - "/stack-templates/{stackTemplateId}": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "Get StackTemplate", + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "duration", + "name": "duration", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "interval", + "name": "interval", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/resources": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resources", "consumes": [ "application/json" ], @@ -3727,14 +3710,132 @@ "application/json" ], "tags": [ - "StackTemplates" + "Dashboard Widgets" ], - "summary": "Get StackTemplate", + "summary": "Get resources", "parameters": [ { "type": "string", - "description": "stackTemplateId", - "name": "stackTemplateId", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/stacks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get stacks", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboard Widgets" + ], + "summary": "Get stacks", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/widgets/workload": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get workloads", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboard Widgets" + ], + "summary": "Get workloads", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse" + } + } + } + } + }, + "/organizations/{organizationId}/dashboards/{dashboardKey}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get dashboard", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Dashboards" + ], + "summary": "Get dashboard", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Dashboard Key", + "name": "dashboardKey", "in": "path", "required": true } @@ -3743,7 +3844,10 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/domain.GetStackTemplateResponse" + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse" + } } } } @@ -3754,7 +3858,7 @@ "JWT": [] } ], - "description": "Update StackTemplate", + "description": "Update dashboard", "consumes": [ "application/json" ], @@ -3762,33 +3866,80 @@ "application/json" ], "tags": [ - "StackTemplates" + "Dashboards" ], - "summary": "Update StackTemplate 'NOT IMPLEMENTED'", + "summary": "Update dashboard", "parameters": [ { - "description": "Update stack template request", - "name": "body", + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Request body to update dashboard", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.UpdateStackTemplateRequest" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest" } } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse" + } + } + } + } + }, + "/organizations/{organizationId}/mandatory-policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse" + } } } }, - "delete": { + "patch": { "security": [ { "JWT": [] } ], - "description": "Delete StackTemplate", + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", "consumes": [ "application/json" ], @@ -3796,16 +3947,25 @@ "application/json" ], "tags": [ - "StackTemplates" + "Policy" ], - "summary": "Delete StackTemplate 'NOT IMPLEMENTED'", + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", "parameters": [ { "type": "string", - "description": "stackTemplateId", - "name": "stackTemplateId", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", "in": "path", "required": true + }, + { + "description": "update mandatory policy/policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest" + } } ], "responses": { @@ -3815,14 +3975,14 @@ } } }, - "/system-api/organizations/{organizationId}/alerts": { - "post": { + "/organizations/{organizationId}/my-profile": { + "get": { "security": [ { "JWT": [] } ], - "description": "Create alert. ADMIN ONLY", + "description": "Get my profile detail", "consumes": [ "application/json" ], @@ -3830,9 +3990,9 @@ "application/json" ], "tags": [ - "Alerts" + "My-profile" ], - "summary": "Create alert. ADMIN ONLY", + "summary": "Get my profile detail", "parameters": [ { "type": "string", @@ -3844,236 +4004,9367 @@ ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse" + } } } - } - } - }, - "definitions": { - "domain.ActionResponse": { - "type": "object", - "properties": { - "body": { - "type": "object", - "additionalProperties": { - "type": "string" + }, + "put": { + "security": [ + { + "JWT": [] } - }, - "method": { - "type": "string" - }, - "name": { - "description": "ENDPOINT (화면보기), PREVIEW (미리보기), PROMOTE (배포), ABORT (중단)", - "type": "string" - }, - "type": { - "description": "LINK, API", - "type": "string" - }, - "uri": { - "type": "string" - } - } - }, - "domain.AlertActionResponse": { - "type": "object", - "properties": { - "alertId": { + ], + "description": "Update my profile detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update my profile detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Required fields: password due to double-check", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete myProfile", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Delete myProfile", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request" + } + } + } + }, + "/organizations/{organizationId}/my-profile/next-password-change": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user's password expired date to current date", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user's password expired date to current date", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError" + } + } + } + } + }, + "/organizations/{organizationId}/my-profile/password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user password detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "My-profile" + ], + "summary": "Update user password detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update user password request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ListPolicy] 정책 목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "filledParameter", + "name": "filledParameter", + "in": "query" + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[CreatePolicy] 정책 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/name/{policyName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyName] 정책 이름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 이름", + "name": "policyName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/resource-name/{policyResourceName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 자원 이름을 가진 정책이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[ExistsPolicyResourceName] 정책 자원 이름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 자원 이름(쿠버네티스 배포 시 자원 이름)", + "name": "policyResourceName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePolicy] 정책 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[UpdatePolicy] 정책을 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}/clusters": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + }, + { + "description": "update policy set request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policies/{policyId}/edit": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 정보를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicy] 정책 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 식별자(uuid)", + "name": "policyId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-notifications/{policyNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get PolicyNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyNotifications" + ], + "summary": "Get PolicyNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "policyNotificationId", + "name": "policyNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책의 통계를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[GetPolicyStatistics] 정책 템플릿, 정책 통계 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplate] 정책 템플릿 목록 조회", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + }, + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿을 신규 생성(v1.0.0을 생성)한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CreatePolicyTemplate] 정책 템플릿 신규 생성", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/kind/{policyTemplateKind}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateKind", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/name/{policyTemplateName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 이름", + "name": "policyTemplateName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplate] 정책 템플릿 조회(최신 버전)", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿을 삭제한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[DeletePolicyTemplate] 정책 템플릿 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿의 업데이트 가능한 필드들을 업데이트한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[UpdatePolicyTemplate] 정책 템플릿 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "update policy template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/deploy": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "create policy template version request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse" + } + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "조회할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "삭제할 버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[ExtractParameters] 정책 템플릿 파라미터 추출", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "버전(v0.0.0 형식)", + "name": "version", + "in": "path", + "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse" + } + } + } + } + }, + "/organizations/{organizationId}/primary-cluster": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update primary cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Organizations" + ], + "summary": "Update primary cluster", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "update primary cluster request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/projects": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get projects", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "(all | only)", + "name": "query", + "in": "query" + }, + { + "type": "string", + "description": "Project Name", + "name": "projectName", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create new project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Create new project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Request body to create project", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check project name exist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Check project name exist", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "type (name)", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "value (project name)", + "name": "value", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/project-roles": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project roles by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project roles", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "project role search by query (query=all), (query=leader), (query=member), (query=viewer)", + "name": "query", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/project-roles/{projectRoleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project role by id", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Role ID", + "name": "projectRoleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get projects", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get projects", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeApp list by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeApp list", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "Show all apps including deleted apps", + "name": "showAll", + "in": "query" + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp" + } + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Install appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Install appServeApp", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to create app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/count": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get number of apps on given stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get number of apps on given stack", + "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": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "integer" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check duplicate appServeAppName by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Check duplicate appServeAppName", + "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": "name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Uninstall appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Uninstall appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/endpoint": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update app endpoint", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update app endpoint", + "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": "appId", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app endpoint", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/exist": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeApp by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeApp", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "boolean" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/latest-task": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get latest task from appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get latest task from appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/rollback": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Rollback appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Rollback appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to rollback app", + "name": "object", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/status": { + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update app status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Update app status", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "description": "Request body to update app status", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get appServeAppTask list by giving params", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get appServeAppTask list", + "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": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp" + } + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks/{taskId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get task detail from appServeApp", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "AppServeApps" + ], + "summary": "Get task detail from appServeApp", + "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": "App ID", + "name": "appId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Task ID", + "name": "taskId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project kubeconfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project kubeconfig", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project members", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project members", + "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": "project member search by query (query=all), (query=leader), (query=member), (query=viewer)", + "name": "query", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project member Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project member Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project member role", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add project member to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Add project member to project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to add project member", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove project members to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Remove project members to project", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + }, + { + "description": "Request body to remove project member", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/count": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project member count group by project role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project member count group by project role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project member", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project member", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove project members to project", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Remove project members to project", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project member Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update project member Role", + "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": "Project Member ID", + "name": "projectMemberId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project member role", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespaces", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project namespaces", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project ID", + "name": "projectId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Create 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 + }, + { + "description": "Request body to create project namespace", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get 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": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Stack ID", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Update 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": "Project Namespace", + "name": "projectNamespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Project Stack ID", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "Request body to update project namespace", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Delete 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/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check project namespace exist", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Check project namespace exist", + "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": "Project 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/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get project namespace kubeconfig", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get project namespace kubeconfig", + "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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/resources-usage": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get resources usage for project namespace", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Projects" + ], + "summary": "Get resources usage 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/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse" + } + } + } + } + }, + "/organizations/{organizationId}/roles": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "List Tks Roles", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "List Tks Roles", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create Tks Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Create Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Create Tks Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Tks Role", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Tks Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Update Tks Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete Tks Role", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Delete Tks Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Role ID", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Permissions By Role ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Update Permissions By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Update Permissions By Role ID Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleId}/users": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Users By Role ID", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Get Users By Role ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Append Users To Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Append Users To Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Append Users To Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove Users From Role", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Remove Users From Role", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role ID", + "name": "roleId", + "in": "path", + "required": true + }, + { + "description": "Remove Users From Role Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/roles/{roleName}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check whether the role name exists", + "produces": [ + "application/json" + ], + "tags": [ + "Roles" + ], + "summary": "Check whether the role name exists", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Role Name", + "name": "roleName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stack-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization StackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get Organization StackTemplates", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove organization stackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Remove organization stackTemplates", + "parameters": [ + { + "description": "Remove organization stack templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add organization stackTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Add organization stackTemplates", + "parameters": [ + { + "description": "Add organization stack templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stack-templates/{stackTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization StackTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackTemplates" + ], + "summary": "Get Organization StackTemplate", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stacks", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stacks", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Create Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check name for stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Check name for stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Update Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete Stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Delete Stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/favorite": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Set favorite stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Set favorite stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete favorite stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Delete favorite stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/kube-config": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get KubeConfig by stack", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get KubeConfig by stack", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "organizationId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policies": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "특정 스택에서 정책 식별자로 지정된 정책을 제거한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[DeletePoliciesForStack] 특정 스택의 정책 제거", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "delete policies for stack request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest" + } + } + ], + "responses": {} + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "특정 스택의 정책 목록을 정책 식별자 리스트로 지정해서 추가한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[AddPoliciesForStack] 특정 스택의 정책 목록 추가", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "description": "add policies for stack request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policy-status": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿의 클러스터 버전 등 상태를 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[GetStackPolicyTemplateStatus] 클러스터 템플릿 상태 상세 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "해당 템플릿의 버전 업데이트 및 연관된 정책의 새 기본값을 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[UpdateStackPolicyTemplateStatus] 템플릿 버전 업데이트", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "정책 템플릿 식별자(uuid)", + "name": "policyTemplateId", + "in": "path", + "required": true + }, + { + "description": "update stack policy template status request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/statistics": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "StackPolicyStatus" + ], + "summary": "[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회", + "parameters": [ + { + "type": "string", + "description": "조직 식별자(o로 시작)", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "스택 식별자", + "name": "stackId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "sortColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics" + } + } + } + } + }, + "/organizations/{organizationId}/stacks/{stackId}/status": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Stack Status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Stacks" + ], + "summary": "Get Stack Status", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "stackId", + "name": "stackId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotificationRules", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Get SystemNotificationRules", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse" + } + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Create SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create stack template request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/default-system-rules": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "MakeDefaultSystemNotificationRules", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "MakeDefaultSystemNotificationRules", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/name/{name}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Check name for systemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Check name for systemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Get SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Update SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + }, + { + "description": "Update systemNotificationRule request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete SystemNotificationRule", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationRules" + ], + "summary": "Delete SystemNotificationRule", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationRuleId", + "name": "systemNotificationRuleId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notification-templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization SystemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Get Organization SystemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Remove organization systemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Remove organization systemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Remove organization systemNotification templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Add organization systemNotificationTemplates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Add organization systemNotificationTemplates", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "Add organization systemNotification templates request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notification-templates/{systemNotificationTemplateId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Organization SystemNotificationTemplate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotificationTemplates" + ], + "summary": "Get Organization SystemNotificationTemplate", + "parameters": [ + { + "type": "string", + "description": "systemNotificationTemplateId", + "name": "systemNotificationTemplateId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notifications": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotifications", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Get SystemNotifications", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/system-notifications/{systemNotificationId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Get SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Update SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + }, + { + "description": "Update cloud setting request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete SystemNotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Delete SystemNotification", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/system-notifications/{systemNotificationId}/actions": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create systemNotification action", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Create systemNotification action", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "systemNotificationId", + "name": "systemNotificationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/organizations/{organizationId}/users": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get user list", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user list", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "pageNumber", + "name": "pageNumber", + "in": "query" + }, + { + "type": "string", + "description": "sortColumn", + "name": "soertColumn", + "in": "query" + }, + { + "type": "string", + "description": "sortOrder", + "name": "sortOrder", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "description": "filters", + "name": "filters", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListUserBody" + } + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update multiple users", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update multiple users", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest" + } + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Create user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "description": "create user request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "create user response", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/account-id/{accountId}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "return true when accountId exists", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user id existence", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/email/{email}/existence": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "return true when email exists", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user email existence", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "email", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get user detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user detail", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUserResponse" + } + } + } + }, + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Update user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + }, + { + "description": "input", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse" + } + } + } + }, + "delete": { + "security": [ + { + "JWT": [] + } + ], + "description": "Delete user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete user", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}/permissions": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permissions By Account ID", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get Permissions By Account ID", + "parameters": [ + { + "type": "string", + "description": "Organization ID", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Account ID", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse" + } + } + } + } + }, + "/organizations/{organizationId}/users/{accountId}/reset-password": { + "put": { + "security": [ + { + "JWT": [] + } + ], + "description": "Reset user's password as temporary password by admin and send email to user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Reset user's password as temporary password by admin", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "accountId", + "name": "accountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/permissions/templates": { + "get": { + "security": [ + { + "JWT": [] + } + ], + "description": "Get Permission Templates", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Permission" + ], + "summary": "Get Permission Templates", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse" + } + } + } + } + }, + "/policy-templates/rego-compile": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Rego 코드 컴파일 및 파라미터 파싱을 수행한다. 파라미터 파싱을 위해서는 먼저 컴파일이 성공해야 하며, parseParameter를 false로 하면 컴파일만 수행할 수 있다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PolicyTemplate" + ], + "summary": "[CompileRego] Rego 코드 컴파일 및 파라미터 파싱", + "parameters": [ + { + "type": "boolean", + "description": "파라미터 파싱 여부", + "name": "parseParameter", + "in": "query", + "required": true + }, + { + "description": "Rego 코드", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse" + } + } + } + } + }, + "/system-api/organizations/{organizationId}/system-notifications": { + "post": { + "security": [ + { + "JWT": [] + } + ], + "description": "Create systemNotification. ADMIN ONLY", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "SystemNotifications" + ], + "summary": "Create systemNotification. ADMIN ONLY", + "parameters": [ + { + "type": "string", + "description": "organizationId", + "name": "organizationId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "definitions": { + "github_com_openinfradev_tks-api_internal_model.AppServeApp": { + "type": "object", + "properties": { + "appType": { + "description": "appType (spring/springboot)", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "description": "description for application", + "type": "string" + }, + "endpointUrl": { + "description": "endpoint URL of deployed app", + "type": "string" + }, + "grafanaUrl": { + "description": "grafana dashboard URL for deployed app", + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "description": "application name", + "type": "string" + }, + "namespace": { + "description": "application namespace", + "type": "string" + }, + "organizationId": { + "description": "contractId is a contract ID which this app belongs to", + "type": "string" + }, + "previewEndpointUrl": { + "description": "preview svc endpoint URL in B/G deployment", + "type": "string" + }, + "projectId": { + "description": "project ID which this app belongs to", + "type": "string" + }, + "status": { + "description": "status is status of deployed app", + "type": "string" + }, + "targetClusterId": { + "description": "target cluster to which the app is deployed", + "type": "string" + }, + "targetClusterName": { + "description": "target cluster name", + "type": "string" + }, + "type": { + "description": "type (build/deploy/all)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ActionResponse": { + "type": "object", + "properties": { + "body": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "method": { + "type": "string" + }, + "name": { + "description": "ENDPOINT (화면보기), PREVIEW (미리보기), PROMOTE (배포), ABORT (중단)", + "type": "string" + }, + "type": { + "description": "LINK, API", + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest": { + "type": "object", + "required": [ + "stackTemplateIds" + ], + "properties": { + "stackTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest": { + "type": "object", + "required": [ + "systemNotificationTemplateIds" + ], + "properties": { + "systemNotificationTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest": { + "type": "object", + "properties": { + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "adminPassword": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest": { + "type": "object", + "required": [ + "email", + "roles" + ], + "properties": { + "adminPassword": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse": { + "type": "object", + "properties": { + "appGroupType": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupType" + }, + "clusterId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "workflowId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "x-enum-varnames": [ + "AppGroupStatus_PENDING", + "AppGroupStatus_INSTALLING", + "AppGroupStatus_RUNNING", + "AppGroupStatus_DELETING", + "AppGroupStatus_DELETED", + "AppGroupStatus_INSTALL_ERROR", + "AppGroupStatus_DELETE_ERROR" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AppGroupType": { + "type": "integer", + "enum": [ + 0, + 1, + 2 + ], + "x-enum-varnames": [ + "AppGroupType_UNSPECIFIED", + "AppGroupType_LMA", + "AppGroupType_SERVICE_MESH" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse": { + "type": "object", + "properties": { + "appType": { + "description": "appType (spring/springboot)", + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "description": "description for application", + "type": "string" + }, + "endpointUrl": { + "description": "endpoint URL of deployed app", + "type": "string" + }, + "grafanaUrl": { + "description": "grafana dashboard URL for deployed app", + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "description": "application name", + "type": "string" + }, + "namespace": { + "description": "application namespace", + "type": "string" + }, + "organizationId": { + "description": "contractId is a contract ID which this app belongs to", + "type": "string" + }, + "previewEndpointUrl": { + "description": "preview svc endpoint URL in B/G deployment", + "type": "string" + }, + "projectId": { + "description": "project ID which this app belongs to", + "type": "string" + }, + "status": { + "description": "status is status of deployed app", + "type": "string" + }, + "targetClusterId": { + "description": "target cluster to which the app is deployed", + "type": "string" + }, + "targetClusterName": { + "description": "target cluster name", + "type": "string" + }, + "type": { + "description": "type (build/deploy/all)", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse": { + "type": "object", + "properties": { + "appConfig": { + "description": "java app config", + "type": "string" + }, + "appSecret": { + "description": "java app secret", + "type": "string" + }, + "appServeAppId": { + "description": "ID for appServeApp that this task belongs to", + "type": "string" + }, + "artifactUrl": { + "description": "URL of java app artifact (Eg, Jar)", + "type": "string" + }, + "availableRollback": { + "type": "boolean" + }, + "createdAt": { + "description": "createdAt is a creation timestamp for the application", + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "executablePath": { + "description": "Executable path of app image", + "type": "string" + }, + "extraEnv": { + "description": "env variable list for java app", + "type": "string" + }, + "helmRevision": { + "description": "revision of deployed helm release", + "type": "integer" + }, + "id": { + "type": "string" + }, + "imageUrl": { + "description": "URL of built image for app", + "type": "string" + }, + "output": { + "description": "output for task result", + "type": "string" + }, + "port": { + "description": "java app port", + "type": "string" + }, + "profile": { + "description": "java app profile", + "type": "string" + }, + "pvAccessMode": { + "type": "string" + }, + "pvEnabled": { + "type": "boolean" + }, + "pvMountPath": { + "type": "string" + }, + "pvSize": { + "type": "string" + }, + "pvStorageClass": { + "type": "string" + }, + "resourceSpec": { + "description": "resource spec of app pod", + "type": "string" + }, + "rollbackVersion": { + "description": "rollback target version", + "type": "string" + }, + "status": { + "description": "status is app status", + "type": "string" + }, + "strategy": { + "description": "deployment strategy (eg, rolling-update)", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "version": { + "description": "application version", + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest": { + "type": "object", + "required": [ + "users" + ], + "properties": { + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse": { + "type": "object", + "properties": { + "appGroupId": { + "type": "string" + }, + "applicationType": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationType" + }, + "createdAt": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "id": { + "type": "string" + }, + "metadata": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ApplicationType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ], + "x-enum-varnames": [ + "ApplicationType_UNSPECIFIED", + "ApplicationType_THANOS", + "ApplicationType_PROMETHEUS", + "ApplicationType_GRAFANA", + "ApplicationType_KIALI", + "ApplicationType_KIBANA", + "ApplicationType_ELASTICSERCH", + "ApplicationType_CLOUD_CONSOLE", + "ApplicationType_HORIZON", + "ApplicationType_JAEGER", + "ApplicationType_KUBERNETES_DASHBOARD" + ] + }, + "github_com_openinfradev_tks-api_pkg_domain.AuditResponse": { + "type": "object", + "properties": { + "clientIP": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "group": { + "type": "string" + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "userAccountId": { + "type": "string" + }, + "userId": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "userRoles": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Axis": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.BarChartData": { + "type": "object", + "properties": { + "series": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UnitNumber" + } + }, + "xAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig": { + "type": "object", + "properties": { + "expiration": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ChartData": { + "type": "object", + "properties": { + "podCounts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PodCount" + } + }, + "series": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Unit" + } + }, + "xAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + }, + "yAxis": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse": { + "type": "object", + "properties": { + "isExist": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse": { + "type": "object", + "properties": { + "awsAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string" + }, + "clusters": { + "type": "integer" + }, + "createdAt": { + "type": "string" + }, + "createdIAM": { + "type": "boolean" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "status": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse": { + "type": "object", + "properties": { + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterHost": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterNode": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "hosts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterHost" + } + }, + "registered": { + "type": "integer" + }, + "registering": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "targeted": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "validity": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterResponse": { + "type": "object", + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse" + }, + "cloudService": { + "type": "string" + }, + "clusterType": { + "type": "string" + }, + "conf": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "favorited": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "isStack": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse": { + "type": "object", + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "clusterRegion": { + "type": "string" + }, + "clusterType": { + "type": "string" + }, + "sshKeyName": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse": { + "type": "object", + "properties": { + "result": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse": { + "type": "object", + "properties": { + "result": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest": { + "type": "object", + "required": [ + "clusterId", + "name" + ], + "properties": { + "appGroupType": { + "type": "string", + "enum": [ + "LMA", + "SERVICE_MESH" + ] + }, + "clusterId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest": { + "type": "object", + "required": [ + "name", + "targetClusterId" + ], + "properties": { + "appConfig": { + "type": "string" + }, + "appSecret": { + "type": "string" + }, + "appType": { + "description": "springboot spring", + "type": "string" + }, + "artifactUrl": { + "type": "string" + }, + "executablePath": { + "type": "string" + }, + "extraEnv": { + "type": "string" + }, + "imageUrl": { + "type": "string" + }, + "name": { + "description": "App", + "type": "string" + }, + "namespace": { + "type": "string" + }, + "port": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "pvAccessMode": { + "type": "string" + }, + "pvEnabled": { + "type": "boolean" + }, + "pvMountPath": { + "type": "string" + }, + "pvSize": { + "type": "string" + }, + "pvStorageClass": { + "type": "string" + }, + "resourceSpec": { + "description": "tiny medium large", + "type": "string" + }, + "strategy": { + "description": "rolling-update blue-green canary", + "type": "string" + }, + "targetClusterId": { + "type": "string" + }, + "type": { + "description": "build deploy all", + "type": "string" + }, + "version": { + "description": "Task", + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest": { + "type": "object", + "properties": { + "applicationType": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "metadata": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest": { + "type": "object" + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse": { + "type": "object" + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest": { + "type": "object", + "required": [ + "accessKeyId", + "awsAccountId", + "name", + "secretAccessKey" + ], + "properties": { + "accessKeyId": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "awsAccountId": { + "type": "string", + "maxLength": 12, + "minLength": 12 + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "AZZURE", + "GCP" + ] + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "secretAccessKey": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "sessionToken": { + "type": "string", + "maxLength": 2000 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest": { + "type": "object", + "required": [ + "cloudService", + "name", + "organizationId", + "stackTemplateId" + ], + "properties": { + "byoClusterEndpointHost": { + "type": "string" + }, + "byoClusterEndpointPort": { + "type": "integer" + }, + "cloudAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "clusterType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "isStack": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "stackTemplateId": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest": { + "type": "object", + "properties": { + "contents": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardContents" + } + }, + "dashboardKey": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse": { + "type": "object", + "properties": { + "dashboardId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest": { + "type": "object", + "required": [ + "adminAccountId", + "adminEmail", + "name" + ], + "properties": { + "adminAccountId": { + "type": "string" + }, + "adminEmail": { + "type": "string" + }, + "adminName": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest": { + "type": "object", + "required": [ + "enforcementAction", + "policyName" + ], + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string", + "enum": [ + "deny", + "dryrun", + "warn" + ], + "example": "deny" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" + }, + "matchYaml": { + "type": "string", + "example": "namespaces:\r\n- testns1" + }, + "parameters": { + "type": "string", + "example": "{\"key\":\"value\"}" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "policyResourceName": { + "type": "string", + "example": "labelpolicy" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest": { + "type": "object", + "required": [ + "kind", + "rego", + "severity", + "templateName" + ], + "properties": { + "deprecated": { + "type": "boolean", + "example": false + }, + "description": { + "type": "string", + "example": "이 정책은 ..." + }, + "kind": { + "type": "string", + "example": "K8sRequiredLabels" + }, + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest": { + "type": "object", + "required": [ + "currentVersion", + "expectedVersion", + "rego", + "versionUpType" + ], + "properties": { + "currentVersion": { + "type": "string", + "example": "v1.0.0" + }, + "expectedVersion": { + "type": "string", + "example": "v1.1.0" + }, + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "versionUpType": { + "type": "string", + "enum": [ + "major", + "minor", + "patch" + ], + "example": "minor" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse": { + "type": "object", + "properties": { + "version": { + "type": "string", + "example": "v1.1.1" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "stackId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectLeaderId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse": { + "type": "object", + "properties": { + "projectId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest": { + "type": "object", + "required": [ + "cloudService", + "name", + "stackTemplateId" + ], + "properties": { + "cloudAccountId": { + "type": "string" + }, + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "clusterId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "stackTemplateId": { + "type": "string" + }, + "tksCpNode": { + "type": "integer" + }, + "tksCpNodeMax": { + "type": "integer" + }, + "tksCpNodeType": { + "type": "string" + }, + "tksInfraNode": { + "type": "integer" + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { + "type": "string" + }, + "tksUserNode": { + "type": "integer" + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { + "type": "string" + }, + "userClusterEndpoint": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest": { + "type": "object", + "required": [ + "kubeType", + "kubeVersion", + "name", + "organizationIds", + "platform", + "serviceIds", + "template", + "version" + ], + "properties": { + "cloudService": { + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] + }, + "description": { + "type": "string" + }, + "kubeType": { + "type": "string" + }, + "kubeVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "platform": { + "type": "string" + }, + "serviceIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "string" + }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, + "version": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest": { + "type": "object", + "required": [ + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" + ], + "properties": { + "description": { + "type": "string" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "systemNotificationCondition": { + "type": "object", + "properties": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + } + } + }, + "systemNotificationTemplateId": { + "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest": { + "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], + "properties": { + "description": { + "type": "string" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "password", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "month": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "year": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardContents": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "sizeX": { + "type": "integer" + }, + "sizeY": { + "type": "integer" + }, + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus": { + "type": "object", + "properties": { + "error": { + "type": "integer" + }, + "normal": { + "type": "integer" + }, + "warning": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate": { + "type": "object", + "properties": { + "policy": { + "type": "integer" + }, + "policyTemplate": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardResource": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "stack": { + "type": "object", + "properties": { + "abnormal": { + "type": "string" + }, + "normal": { + "type": "string" + } + } + }, + "storage": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "storage": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest": { + "type": "object", + "required": [ + "accessKeyId", + "secretAccessKey" + ], + "properties": { + "accessKeyId": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "secretAccessKey": { + "type": "string", + "maxLength": 128, + "minLength": 16 + }, + "sessionToken": { + "type": "string", + "maxLength": 2000 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest": { + "type": "object", + "properties": { + "policyIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest": { + "type": "object", + "properties": { + "adminPassword": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.EndpointResponse": { + "type": "object", + "properties": { + "group": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse": { + "type": "object", + "properties": { + "existed": { + "type": "boolean" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest": { + "type": "object", + "required": [ + "rego" + ], + "properties": { + "libs": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { + "type": "string", + "example": "Rego 코드" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { + "type": "object", + "properties": { + "column": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "releation": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindIdRequest": { + "type": "object", + "required": [ + "code", + "email", + "organizationId", + "userName" + ], + "properties": { + "code": { + "type": "string" + }, + "email": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "userName": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindIdResponse": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest": { + "type": "object", + "required": [ + "accountId", + "code", + "email", + "organizationId", + "userName" + ], + "properties": { + "accountId": { + "type": "string" + }, + "code": { + "type": "string" + }, + "email": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "userName": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse": { + "type": "object", + "properties": { + "appGroup": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse": { + "type": "object", + "properties": { + "appGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse": { + "type": "object", + "properties": { + "appServeApp": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse" + }, + "appServeAppTask": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse" + }, + "stages": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StageResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse": { + "type": "object", + "properties": { + "applications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse": { + "type": "object", + "properties": { + "audit": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse": { + "type": "object", + "properties": { + "audits": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "resourceQuota": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuota" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse": { + "type": "object", + "properties": { + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse": { + "type": "object", + "properties": { + "cloudAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse": { + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterNode" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse": { + "type": "object", + "properties": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse": { + "type": "object", + "properties": { + "clusters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse": { + "type": "object", + "properties": { + "chart": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse": { + "type": "object", + "properties": { + "charts": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse": { + "type": "object", + "properties": { + "policyCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount" + }, + "templateCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse": { + "type": "object", + "properties": { + "statuses": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse": { + "type": "object", + "properties": { + "updatedResources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response": { + "type": "object", + "properties": { + "chartData": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData" + }, + "chartType": { + "type": "string" + }, + "description": { + "type": "string" + }, + "duration": { + "type": "string" + }, + "interval": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse": { + "type": "object", + "properties": { + "resources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardResource" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "sizeX": { + "type": "integer" + }, + "sizeY": { + "type": "integer" + }, + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse": { + "type": "object", + "properties": { + "stacks": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WorkloadData" + } + }, + "title": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse": { + "type": "object", + "properties": { + "templates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse": { + "type": "object", + "properties": { + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *PermissionTemplateResponse `json:\"permissions\"`", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *PermissionSetResponse `json:\"permissions\"`", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse": { + "type": "object", + "properties": { + "policyNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "content": { + "policyNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse": { + "type": "object", + "properties": { + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse": { + "type": "object", + "properties": { + "deployVersion": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse": { + "type": "object", + "properties": { + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse": { + "type": "object", + "properties": { + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse": { + "type": "object", + "properties": { + "kubeconfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse": { + "type": "object", + "properties": { + "projectLeaderCount": { + "type": "integer" }, - "createdAt": { - "type": "string" + "projectMemberAllCount": { + "type": "integer" }, - "id": { + "projectMemberCount": { + "type": "integer" + }, + "projectViewerCount": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse": { + "type": "object", + "properties": { + "projectMember": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "projectMembers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse": { + "type": "object", + "properties": { + "k8sResources": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse": { + "type": "object", + "properties": { + "kubeConfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse": { + "type": "object", + "properties": { + "resourcesUsage": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse": { + "type": "object", + "properties": { + "projectNamespace": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse": { + "type": "object", + "properties": { + "projectNamespaces": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse": { + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse": { + "type": "object", + "properties": { + "projectRole": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse": { + "type": "object", + "properties": { + "projectRoles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "status": { + "projects": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse": { + "type": "object", + "properties": { + "kubeConfig": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse": { + "type": "object", + "properties": { + "affectedPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatus" + } }, - "taker": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" }, - "updatedAt": { + "templateDescription": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateLatestVersion": { + "type": "string", + "example": "v1.0.3" + }, + "templateLatestVersionReleaseDate": { + "type": "string", + "format": "date-time" + }, + "templateMandatory": { + "type": "boolean" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + }, + "updatedPolicyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackResponse": { + "type": "object", + "properties": { + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse": { + "type": "object", + "properties": { + "stackStatus": { "type": "string" + }, + "stepStatus": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackStepStatus" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse": { + "type": "object", + "properties": { + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse": { + "type": "object", + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse": { + "type": "object", + "properties": { + "templateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "stackTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "stacks": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse": { + "type": "object", + "properties": { + "systemNotification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse": { + "type": "object", + "properties": { + "systemNotificationRule": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse" } } }, - "domain.AlertResponse": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse": { "type": "object", "properties": { - "alertActions": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "systemNotificationRules": { "type": "array", "items": { - "$ref": "#/definitions/domain.AlertActionResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse" } - }, - "closedAt": { - "type": "string" - }, - "cluster": { - "$ref": "#/definitions/domain.SimpleClusterResponse" - }, - "code": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "description": { - "type": "string" - }, - "firedAt": { - "type": "string" - }, - "grade": { - "type": "string" - }, - "grafanaUrl": { - "type": "string" - }, - "id": { - "type": "string" - }, - "lastTaker": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "message": { - "type": "string" - }, - "name": { - "type": "string" - }, - "node": { - "type": "string" - }, - "organizationId": { - "type": "string" - }, - "processingSec": { - "type": "integer" - }, - "rawData": { - "type": "string" - }, - "status": { - "type": "string" - }, - "takedAt": { - "type": "string" - }, - "takedSec": { - "type": "integer" - }, - "updatedAt": { - "type": "string" } } }, - "domain.AppGroupResponse": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse": { "type": "object", "properties": { - "appGroupType": { - "type": "integer" - }, - "clusterId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "workflowId": { - "type": "string" + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse" } } }, - "domain.AppServeApp": { + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse": { "type": "object", "properties": { - "appServeAppTasks": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "systemNotificationTemplates": { "type": "array", "items": { - "$ref": "#/definitions/domain.AppServeAppTask" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse" } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "appType": { - "description": "appType (spring/springboot)", - "type": "string" - }, + "systemNotifications": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse": { + "type": "object", + "properties": { "createdAt": { "type": "string" }, - "deletedAt": { - "type": "string" - }, - "endpointUrl": { - "description": "endpoint URL of deployed app", + "creator": { "type": "string" }, - "grafanaUrl": { - "description": "grafana dashboard URL for deployed app", + "description": { "type": "string" }, "id": { "type": "string" }, "name": { - "description": "application name", - "type": "string" - }, - "namespace": { - "description": "application namespace", "type": "string" }, "organizationId": { - "description": "contractId is a contract ID which this app belongs to", - "type": "string" - }, - "previewEndpointUrl": { - "description": "preview svc endpoint URL in B/G deployment", - "type": "string" - }, - "status": { - "description": "status is status of deployed app", - "type": "string" - }, - "targetClusterId": { - "description": "target cluster to which the app is deployed", - "type": "string" - }, - "targetClusterName": { - "description": "target cluster name", - "type": "string" - }, - "type": { - "description": "type (build/deploy/all)", "type": "string" }, "updatedAt": { @@ -4081,136 +13372,130 @@ } } }, - "domain.AppServeAppTask": { + "github_com_openinfradev_tks-api_pkg_domain.GetUserResponse": { "type": "object", "properties": { - "appConfig": { - "description": "java app config", - "type": "string" - }, - "appSecret": { - "description": "java app secret", - "type": "string" - }, - "appServeAppId": { - "description": "ID for appServeApp that this task belongs to", - "type": "string" - }, - "artifactUrl": { - "description": "URL of java app artifact (Eg, Jar)", - "type": "string" - }, - "availableRollback": { - "type": "boolean" - }, - "createdAt": { - "description": "createdAt is a creation timestamp for the application", - "type": "string" - }, - "deletedAt": { - "type": "string" - }, - "executablePath": { - "description": "Executable path of app image", - "type": "string" - }, - "extraEnv": { - "description": "env variable list for java app", - "type": "string" - }, - "helmRevision": { - "description": "revision of deployed helm release", - "type": "integer" - }, - "id": { - "type": "string" - }, - "imageUrl": { - "description": "URL of built image for app", - "type": "string" - }, - "output": { - "description": "output for task result", - "type": "string" - }, - "port": { - "description": "java app port", - "type": "string" - }, - "profile": { - "description": "java app profile", - "type": "string" - }, - "pvAccessMode": { - "type": "string" - }, - "pvEnabled": { - "type": "boolean" - }, - "pvMountPath": { - "type": "string" - }, - "pvSize": { - "type": "string" + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "creator": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "pvStorageClass": { + "users": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse": { + "type": "object", + "properties": { + "permissions": { + "description": "Permissions *MergedPermissionSetResponse `json:\"permissions\"`", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest": { + "type": "object", + "required": [ + "name", + "organizationId", + "stackTemplateId" + ], + "properties": { + "cloudService": { "type": "string" }, - "resourceSpec": { - "description": "resource spec of app pod", + "clusterType": { "type": "string" }, - "rollbackVersion": { - "description": "rollback target version", + "description": { "type": "string" }, - "status": { - "description": "status is app status", - "type": "string" + "kubeconfig": { + "type": "array", + "items": { + "type": "integer" + } }, - "strategy": { - "description": "deployment strategy (eg, rolling-update)", + "name": { "type": "string" }, - "updatedAt": { + "organizationId": { "type": "string" }, - "version": { - "description": "application version", + "stackTemplateId": { "type": "string" } } }, - "domain.ApplicationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse": { "type": "object", "properties": { - "appGroupId": { - "type": "string" - }, - "applicationType": { - "type": "integer" - }, - "createdAt": { - "type": "string" - }, - "endpoint": { - "type": "string" - }, "id": { "type": "string" - }, - "metadata": { - "type": "string" - }, - "updatedAt": { - "type": "string" } } }, - "domain.Axis": { + "github_com_openinfradev_tks-api_pkg_domain.Kinds": { "type": "object", "properties": { - "data": { + "apiGroups": { + "type": "array", + "items": { + "type": "string" + } + }, + "kinds": { "type": "array", "items": { "type": "string" @@ -4218,147 +13503,301 @@ } } }, - "domain.BootstrapKubeconfig": { + "github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse": { "type": "object", "properties": { - "expiration": { - "type": "integer" + "organizations": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + } + }, + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" } } }, - "domain.ChartData": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { "type": "object", "properties": { - "podCounts": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { "type": "array", "items": { - "$ref": "#/definitions/domain.PodCount" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "series": { + "policyTemplates": { "type": "array", "items": { - "$ref": "#/definitions/domain.Unit" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse" } - }, - "xAxis": { - "$ref": "#/definitions/domain.Axis" - }, - "yAxis": { - "$ref": "#/definitions/domain.Axis" } } }, - "domain.CheckCloudAccountAwsAccountIdResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "policyTemplateStatistics": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics" + } } } }, - "domain.CheckCloudAccountNameResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "versions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "v1.1.0", + "v1.0.1", + "v1.0.0" + ] } } }, - "domain.CheckExistedResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse": { "type": "object", "properties": { - "existed": { - "type": "boolean" + "polices": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse" + } } } }, - "domain.CloudAccount": { + "github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse": { "type": "object", "properties": { - "accessKeyId": { - "type": "string" - }, - "awsAccountId": { - "type": "string" + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" }, - "cloudService": { + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ListUserBody": { + "type": "object", + "properties": { + "accountId": { "type": "string" }, - "clusters": { - "type": "integer" - }, "createdAt": { "type": "string" }, - "createdIAM": { - "type": "boolean" - }, "creator": { - "$ref": "#/definitions/domain.User" + "type": "string" }, - "creatorId": { + "department": { "type": "string" }, "description": { "type": "string" }, + "email": { + "type": "string" + }, "id": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" }, - "resource": { + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.LoginRequest": { + "type": "object", + "required": [ + "accountId", + "organizationId", + "password" + ], + "properties": { + "accountId": { "type": "string" }, - "secretAccessKey": { + "organizationId": { "type": "string" }, - "sessionToken": { + "password": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.LoginResponse": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "passwordExpired": { + "type": "boolean" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "token": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" }, - "status": { - "type": "integer" + "mandatory": { + "type": "boolean" }, - "statusDesc": { - "type": "string" + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" }, - "updatedAt": { - "type": "string" + "policyName": { + "type": "string", + "example": "org 레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo": { + "type": "object", + "properties": { + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo" + } + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.Match": { + "type": "object", + "properties": { + "excludedNamespaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "kinds": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Kinds" + } + }, + "namespaces": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } }, - "updator": { - "$ref": "#/definitions/domain.User" + "isAllowed": { + "type": "boolean" }, - "updatorId": { + "key": { "type": "string" } } }, - "domain.CloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse": { "type": "object", "properties": { - "awsAccountId": { - "type": "string" - }, - "cloudService": { - "type": "string" - }, - "clusters": { - "type": "integer" + "admin": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, "createdAt": { "type": "string" }, - "createdIAM": { - "type": "boolean" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, "description": { "type": "string" }, @@ -4368,1358 +13807,1701 @@ "name": { "type": "string" }, - "organizationId": { - "type": "string" + "policyTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse" + } }, - "resource": { + "primaryClusterId": { "type": "string" }, + "stackCount": { + "type": "integer" + }, + "stackTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + } + }, "status": { "type": "string" }, - "updatedAt": { + "statusDesc": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "systemNotificationTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + } + }, + "updatedAt": { + "type": "string" } } }, - "domain.Cluster": { + "github_com_openinfradev_tks-api_pkg_domain.PaginationResponse": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" + "filters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.FilterResponse" + } }, - "byoClusterEndpointPort": { + "pageNumber": { "type": "integer" }, - "cloudAccount": { - "$ref": "#/definitions/domain.CloudAccount" + "pageSize": { + "type": "integer" }, - "cloudAccountId": { + "sortColumn": { "type": "string" }, - "cloudService": { + "sortOrder": { "type": "string" }, - "clusterType": { + "totalPages": { "type": "integer" }, - "conf": { - "$ref": "#/definitions/domain.ClusterConf" + "totalRows": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ParameterDef": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "createdAt": { + "defaultValue": { "type": "string" }, - "creator": { - "$ref": "#/definitions/domain.User" + "isArray": { + "type": "boolean" }, - "creatorId": { - "type": "string" + "isNew": { + "type": "boolean" }, - "description": { + "key": { "type": "string" }, - "favorited": { - "type": "boolean" - }, - "id": { + "type": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PermissionResponse": { + "type": "object", + "properties": { + "ID": { "type": "string" }, - "isStack": { - "type": "boolean" + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse" + } }, - "kubeconfig": { + "endpoints": { "type": "array", "items": { - "type": "integer" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.EndpointResponse" } }, - "name": { - "type": "string" + "isAllowed": { + "type": "boolean" }, - "organizationId": { + "key": { "type": "string" }, - "stackTemplate": { - "$ref": "#/definitions/domain.StackTemplate" - }, - "stackTemplateId": { + "name": { "type": "string" - }, - "status": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PodCount": { + "type": "object", + "properties": { + "day": { "type": "integer" }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.User" - }, - "updatorId": { - "type": "string" + "value": { + "type": "integer" } } }, - "domain.ClusterConf": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyCount": { "type": "object", "properties": { - "tksCpNode": { + "deny": { "type": "integer" }, - "tksCpNodeMax": { + "dryrun": { "type": "integer" }, - "tksCpNodeType": { - "type": "string" - }, - "tksInfraNode": { + "fromOrgTemplate": { "type": "integer" }, - "tksInfraNodeMax": { + "fromTksTemplate": { "type": "integer" }, - "tksInfraNodeType": { - "type": "string" - }, - "tksUserNode": { + "total": { "type": "integer" }, - "tksUserNodeMax": { + "warn": { "type": "integer" - }, - "tksUserNodeType": { - "type": "string" } } }, - "domain.ClusterConfResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse": { "type": "object", "properties": { - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" }, - "tksCpNodeType": { + "createdAt": { "type": "string" }, - "tksInfraNode": { - "type": "integer" - }, - "tksInfraNodeMax": { - "type": "integer" + "grafanaUrl": { + "type": "string" }, - "tksInfraNodeType": { + "id": { "type": "string" }, - "tksUserNode": { - "type": "integer" + "messageActionProposal": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "messageContent": { + "type": "string" }, - "tksUserNodeType": { + "messageTitle": { "type": "string" - } - } - }, - "domain.ClusterHost": { - "type": "object", - "properties": { - "name": { + }, + "notificationType": { "type": "string" }, - "status": { + "organizationId": { "type": "string" - } - } - }, - "domain.ClusterNode": { - "type": "object", - "properties": { - "command": { + }, + "policyName": { "type": "string" }, - "hosts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterHost" - } + "rawData": { + "type": "string" }, - "registered": { - "type": "integer" + "read": { + "type": "boolean" }, - "registering": { - "type": "integer" + "severity": { + "type": "string" }, "status": { "type": "string" }, - "targeted": { - "type": "integer" - }, - "type": { + "updatedAt": { "type": "string" - }, - "validity": { - "type": "integer" } } }, - "domain.ClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyParameter": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" - }, - "byoClusterEndpointPort": { - "type": "integer" - }, - "cloudAccount": { - "$ref": "#/definitions/domain.SimpleCloudAccountResponse" - }, - "cloudService": { + "name": { "type": "string" }, - "clusterType": { + "type": { "type": "string" }, - "conf": { - "$ref": "#/definitions/domain.ClusterConfResponse" + "updatable": { + "type": "boolean" }, - "createdAt": { + "value": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" }, "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, "description": { "type": "string" }, + "enforcementAction": { + "type": "string", + "example": "deny" + }, + "filledParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, "id": { - "type": "string" + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "isStack": { + "mandatory": { "type": "boolean" }, - "name": { - "type": "string" + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" }, - "organizationId": { - "type": "string" + "matchYaml": { + "type": "string", + "example": "namespaces:\r\n- testns1" }, - "stackTemplate": { - "$ref": "#/definitions/domain.SimpleStackTemplateResponse" + "parameters": { + "type": "string", + "example": "{\"key\":\"value\"}" }, - "status": { - "type": "string" + "policyName": { + "type": "string", + "example": "label 정책" }, - "statusDesc": { - "type": "string" + "policyResourceName": { + "type": "string", + "example": "labelpolicy" + }, + "targetClusters": { + "description": "TargetClusterIds []string `json:\"targetClusterIds\" example:\"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90\"`", + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + } + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, "updatedAt": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse": { + "type": "object", + "properties": { + "policyCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" + "templateCount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount" } } }, - "domain.ClusterSiteValuesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyStatus": { "type": "object", "properties": { - "byoClusterEndpointHost": { - "type": "string" - }, - "byoClusterEndpointPort": { - "type": "integer" + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" }, - "clusterRegion": { + "policyName": { "type": "string" }, - "clusterType": { + "policyPolicyParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyParameter" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { + "type": "object", + "properties": { + "createdAt": { "type": "string" }, - "sshKeyName": { - "type": "string" + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "tksCpNode": { - "type": "integer" + "deprecated": { + "type": "boolean", + "example": false }, - "tksCpNodeMax": { - "type": "integer" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "tksCpNodeType": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "tksInfraNode": { - "type": "integer" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "tksInfraNodeMax": { - "type": "integer" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "tksInfraNodeType": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "tksUserNode": { - "type": "integer" + "rego": { + "type": "string", + "example": "rego 코드" }, - "tksUserNodeMax": { - "type": "integer" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "tksUserNodeType": { - "type": "string" - } - } - }, - "domain.CreateAppGroupRequest": { - "type": "object", - "required": [ - "clusterId", - "name" - ], - "properties": { - "appGroupType": { + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "type": { "type": "string", "enum": [ - "LMA", - "SERVICE_MESH" - ] + "tks", + "organization" + ], + "example": "tks" }, - "clusterId": { + "updatedAt": { "type": "string" }, - "description": { - "type": "string" + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "name": { - "type": "string" + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.CreateAppGroupResponse": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics": { "type": "object", "properties": { - "id": { + "organizationId": { + "type": "string" + }, + "organizationName": { "type": "string" + }, + "usageCount": { + "type": "integer" } } }, - "domain.CreateAppServeAppRequest": { + "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse": { "type": "object", - "required": [ - "name", - "targetClusterId" - ], "properties": { - "appConfig": { + "createdAt": { "type": "string" }, - "appSecret": { - "type": "string" + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "appType": { - "description": "springboot spring", - "type": "string" + "currentVersion": { + "type": "string", + "example": "v1.0.1" }, - "artifactUrl": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "executablePath": { - "type": "string" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "extraEnv": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "imageUrl": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "name": { - "description": "App", - "type": "string" + "latestVersion": { + "type": "string", + "example": "v1.0.1" }, - "namespace": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "port": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "profile": { - "type": "string" + "rego": { + "type": "string", + "example": "rego 코드" }, - "pvAccessMode": { + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "type": { + "type": "string", + "enum": [ + "tks", + "organization" + ], + "example": "tks" + }, + "updatedAt": { "type": "string" }, - "pvEnabled": { - "type": "boolean" + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse": { + "type": "object", + "properties": { + "createdAt": { + "description": "AppCount int `json:\"appCount\"`\nNamespaceCount int `json:\"namespaceCount\"`\nMemberCount int `json:\"memberCount\"`", + "type": "string" }, - "pvMountPath": { + "description": { "type": "string" }, - "pvSize": { + "id": { "type": "string" }, - "pvStorageClass": { + "name": { "type": "string" }, - "resourceSpec": { - "description": "tiny medium large", + "organizationId": { "type": "string" }, - "strategy": { - "description": "rolling-update blue-green canary", + "projectLeaderAccountId": { "type": "string" }, - "targetClusterId": { + "projectLeaderDepartment": { "type": "string" }, - "type": { - "description": "build deploy all", + "projectLeaderId": { "type": "string" }, - "version": { - "description": "Task", + "projectLeaderName": { "type": "string" - } - } - }, - "domain.CreateApplicationRequest": { - "type": "object", - "properties": { - "applicationType": { + }, + "projectRoleId": { "type": "string" }, - "endpoint": { + "projectRoleName": { "type": "string" }, - "metadata": { + "updatedAt": { "type": "string" } } }, - "domain.CreateBootstrapKubeconfigResponse": { - "type": "object", - "properties": { - "kubeconfig": { - "$ref": "#/definitions/domain.BootstrapKubeconfig" - } - } - }, - "domain.CreateCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest": { "type": "object", "required": [ - "accessKeyId", - "awsAccountId", - "name", - "secretAccessKey" + "projectRoleId", + "projectUserId" ], "properties": { - "accessKeyId": { - "type": "string", - "maxLength": 128, - "minLength": 16 - }, - "awsAccountId": { - "type": "string", - "maxLength": 12, - "minLength": 12 - }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "AZZURE", - "GCP" - ] - }, - "description": { + "projectRoleId": { "type": "string" }, - "name": { + "projectUserId": { "type": "string" - }, - "secretAccessKey": { - "type": "string", - "maxLength": 128, - "minLength": 16 - }, - "sessionToken": { - "type": "string", - "maxLength": 2000 } } }, - "domain.CreateCloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse": { "type": "object", "properties": { - "id": { + "createdAt": { "type": "string" - } - } - }, - "domain.CreateClusterRequest": { - "type": "object", - "required": [ - "cloudService", - "name", - "organizationId", - "stackTemplateId" - ], - "properties": { - "byoClusterEndpointHost": { + }, + "id": { "type": "string" }, - "byoClusterEndpointPort": { - "type": "integer" + "projectId": { + "type": "string" }, - "cloudAccountId": { + "projectRoleId": { "type": "string" }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "BYOH" - ] + "projectRoleName": { + "type": "string" }, - "clusterType": { + "projectUserAccountId": { "type": "string" }, - "description": { + "projectUserDepartment": { "type": "string" }, - "isStack": { - "type": "boolean" + "projectUserEmail": { + "type": "string" }, - "name": { + "projectUserId": { "type": "string" }, - "organizationId": { + "projectUserName": { "type": "string" }, - "stackTemplateId": { + "updatedAt": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources": { + "type": "object", + "properties": { + "cronjobs": { + "type": "integer" }, - "tksCpNode": { + "daemonsets": { "type": "integer" }, - "tksCpNodeMax": { + "deployments": { "type": "integer" }, - "tksCpNodeType": { - "type": "string" + "ingresses": { + "type": "integer" }, - "tksInfraNode": { + "jobs": { "type": "integer" }, - "tksInfraNodeMax": { + "pods": { "type": "integer" }, - "tksInfraNodeType": { - "type": "string" + "pvcs": { + "type": "integer" }, - "tksUserNode": { + "services": { "type": "integer" }, - "tksUserNodeMax": { + "statefulsets": { "type": "integer" }, - "tksUserNodeType": { + "updatedAt": { "type": "string" } } }, - "domain.CreateClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage": { "type": "object", "properties": { - "id": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + }, + "storage": { "type": "string" } } }, - "domain.CreateOrganizationRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse": { "type": "object", - "required": [ - "Email", - "name" - ], "properties": { - "Email": { + "appCount": { + "type": "integer" + }, + "createdAt": { "type": "string" }, "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "type": "string" }, - "name": { + "grafanaUrl": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "stackId": { + "type": "string" + }, + "stackName": { + "type": "string" + }, + "status": { "type": "string" }, - "phone": { + "updatedAt": { "type": "string" } } }, - "domain.CreateStackRequest": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectResponse": { "type": "object", - "required": [ - "cloudService", - "name", - "stackTemplateId" - ], "properties": { - "cloudAccountId": { - "type": "string" - }, - "cloudService": { - "type": "string", - "enum": [ - "AWS", - "BYOH" - ] + "appCount": { + "type": "integer" }, - "clusterId": { + "createdAt": { "type": "string" }, "description": { "type": "string" }, - "name": { + "id": { "type": "string" }, - "stackTemplateId": { + "isMyProject": { "type": "string" }, - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { + "memberCount": { "type": "integer" }, - "tksCpNodeType": { + "name": { "type": "string" }, - "tksInfraNode": { + "namespaceCount": { "type": "integer" }, - "tksInfraNodeMax": { - "type": "integer" + "organizationId": { + "type": "string" }, - "tksInfraNodeType": { + "projectLeaderId": { "type": "string" }, - "tksUserNode": { - "type": "integer" + "projectLeaderName": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "projectRoleId": { + "type": "string" }, - "tksUserNodeType": { + "projectRoleName": { "type": "string" }, - "userClusterEndpoint": { + "updatedAt": { "type": "string" } } }, - "domain.CreateStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse": { "type": "object", "properties": { + "createdAt": { + "type": "string" + }, + "deletedAt": { + "type": "string" + }, + "description": { + "type": "string" + }, "id": { "type": "string" + }, + "name": { + "description": "project-leader, project-member, project-viewer", + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RegoCompieError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "example": "P_INVALID_REGO_SYNTAX" + }, + "message": { + "type": "string", + "example": "Invalid rego syntax" + }, + "status": { + "type": "integer", + "example": 400 + }, + "text": { + "type": "string", + "example": "Rego 문법 에러입니다. 라인:2 컬럼:1 에러메시지: var testnum is not safe" } } }, - "domain.CreateStackTemplateRequest": { + "github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest": { "type": "object", "required": [ - "name", - "platform", - "template", - "version" + "rego" ], "properties": { - "cloudService": { + "libs": { + "type": "array", + "items": { + "type": "string" + } + }, + "rego": { "type": "string", - "enum": [ - "AWS", - "AZZURE", - "GCP" - ] + "example": "Rego 코드" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } }, - "description": { + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest": { + "type": "object", + "required": [ + "stackTemplateIds" + ], + "properties": { + "stackTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest": { + "type": "object", + "required": [ + "systemNotificationTemplateIds" + ], + "properties": { + "systemNotificationTemplateIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectMemberId": { + "type": "string" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest": { + "type": "object", + "required": [ + "users" + ], + "properties": { + "users": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ResourceQuota": { + "type": "object", + "properties": { + "quotas": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr": { + "type": "object", + "properties": { + "quota": { + "type": "integer" + }, + "required": { + "type": "integer" + }, + "type": { "type": "string" }, - "name": { + "usage": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest": { + "type": "object", + "properties": { + "taskId": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse": { + "type": "object", + "properties": { + "awsAccountId": { "type": "string" }, - "platform": { + "cloudService": { "type": "string" }, - "template": { + "clusters": { + "type": "integer" + }, + "createdIAM": { + "type": "boolean" + }, + "description": { "type": "string" }, - "templateType": { - "type": "string", - "enum": [ - "STANDARD", - "MSA" - ] + "id": { + "type": "string" }, - "version": { + "name": { + "type": "string" + }, + "organizationId": { "type": "string" } } }, - "domain.CreateStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse": { "type": "object", "properties": { "id": { "type": "string" + }, + "name": { + "type": "string" + }, + "organizationId": { + "type": "string" } } }, - "domain.CreateUserRequest": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse": { "type": "object", - "required": [ - "accountId", - "email", - "password", - "role" - ], "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "type": "string" }, - "email": { + "id": { "type": "string" }, "name": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "password": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, - "role": { + "type": { "type": "string", "enum": [ - "admin", - "user" - ] + "tks", + "organization" + ], + "example": "tks" + }, + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.CreateUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } + "id": { + "type": "string" + }, + "name": { + "type": "string" } } }, - "domain.DashboardChartResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse": { "type": "object", "properties": { - "chartData": { - "$ref": "#/definitions/domain.ChartData" - }, - "chartType": { + "cloudService": { "type": "string" }, "description": { "type": "string" }, - "duration": { + "id": { "type": "string" }, - "interval": { + "kubeType": { "type": "string" }, - "month": { + "kubeVersion": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse" + } }, - "updatedAt": { + "template": { "type": "string" - }, - "year": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse": { + "type": "object", + "properties": { + "type": { "type": "string" } } }, - "domain.DashboardResource": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse": { "type": "object", "properties": { - "cpu": { + "createdAt": { "type": "string" }, - "memory": { + "description": { "type": "string" }, - "stack": { + "id": { "type": "string" }, - "storage": { + "name": { + "type": "string" + }, + "updatedAt": { "type": "string" } } }, - "domain.DashboardStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse": { "type": "object", "properties": { - "cpu": { + "accountId": { "type": "string" }, - "createdAt": { + "department": { "type": "string" }, - "description": { + "email": { "type": "string" }, "id": { "type": "string" }, - "memory": { - "type": "string" - }, "name": { "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.StackConfResponse": { + "type": "object", + "required": [ + "tksInfraNode", + "tksUserNode" + ], + "properties": { + "tksCpNode": { + "type": "integer" }, - "status": { - "type": "string" + "tksCpNodeMax": { + "type": "integer" }, - "statusDesc": { + "tksCpNodeType": { "type": "string" }, - "storage": { + "tksInfraNode": { + "type": "integer", + "maximum": 3, + "minimum": 1 + }, + "tksInfraNodeMax": { + "type": "integer" + }, + "tksInfraNodeType": { "type": "string" }, - "updatedAt": { + "tksUserNode": { + "type": "integer", + "maximum": 100, + "minimum": 0 + }, + "tksUserNodeMax": { + "type": "integer" + }, + "tksUserNodeType": { "type": "string" } } }, - "domain.DeleteCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics": { "type": "object", - "required": [ - "accessKeyId", - "secretAccessKey" - ], "properties": { - "accessKeyId": { - "type": "string", - "maxLength": 128, - "minLength": 16 + "outofdatePolicyCount": { + "type": "integer" }, - "secretAccessKey": { - "type": "string", - "maxLength": 128, - "minLength": 16 + "outofdateTemplateCount": { + "type": "integer" }, - "sessionToken": { - "type": "string", - "maxLength": 2000 + "totalPolicyCount": { + "type": "integer" + }, + "totalTemplateCount": { + "type": "integer" + }, + "uptodatePolicyCount": { + "type": "integer" + }, + "uptodateTemplateCount": { + "type": "integer" } } }, - "domain.FilterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse": { "type": "object", "properties": { - "column": { - "type": "string" + "policyDescription": { + "type": "string", + "example": "org 레이블 설정 여부 검사" }, - "values": { - "type": "array", - "items": { - "type": "string" - } + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "policyMandatory": { + "type": "boolean" + }, + "policyName": { + "type": "string", + "example": "org 레이블 요구" + }, + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" + }, + "templateDescription": { + "type": "string", + "example": "파라미터로 설정된 레이블 검사" + }, + "templateId": { + "type": "string", + "example": "708d1e5b-4e6f-40e9-87a3-329e2fd051a5" + }, + "templateLatestVersion": { + "type": "string", + "example": "v1.0.3" + }, + "templateName": { + "type": "string", + "example": "레이블 요구" } } }, - "domain.FindIdRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackResponse": { "type": "object", - "required": [ - "code", - "email", - "organizationId", - "userName" - ], "properties": { - "code": { + "appServeAppCnt": { + "type": "integer" + }, + "cloudAccount": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse" + }, + "conf": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackConfResponse" + }, + "createdAt": { "type": "string" }, - "email": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "favorited": { + "type": "boolean" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { "type": "string" }, "organizationId": { "type": "string" }, - "userName": { + "primaryCluster": { + "type": "boolean" + }, + "resource": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse" + }, + "stackTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse" + }, + "status": { + "type": "string" + }, + "statusDesc": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "userClusterEndpoint": { "type": "string" } } }, - "domain.FindIdResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackStepStatus": { "type": "object", "properties": { - "accountId": { + "maxStep": { + "type": "integer" + }, + "stage": { + "type": "string" + }, + "status": { "type": "string" + }, + "step": { + "type": "integer" } } }, - "domain.FindPasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse": { "type": "object", - "required": [ - "accountId", - "code", - "email", - "organizationId", - "userName" - ], "properties": { - "accountId": { + "cloudService": { "type": "string" }, - "code": { + "createdAt": { "type": "string" }, - "email": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { "type": "string" }, - "organizationId": { + "id": { "type": "string" }, - "userName": { + "kubeType": { "type": "string" - } - } - }, - "domain.GetAlertResponse": { - "type": "object", - "properties": { - "alert": { - "$ref": "#/definitions/domain.AlertResponse" - } - } - }, - "domain.GetAlertsResponse": { - "type": "object", - "properties": { - "alerts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AlertResponse" - } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetAppGroupResponse": { - "type": "object", - "properties": { - "appGroup": { - "$ref": "#/definitions/domain.AppGroupResponse" - } - } - }, - "domain.GetAppGroupsResponse": { - "type": "object", - "properties": { - "appGroups": { + "kubeVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organizations": { "type": "array", "items": { - "$ref": "#/definitions/domain.AppGroupResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetAppServeAppResponse": { - "type": "object", - "properties": { - "appServeApp": { - "$ref": "#/definitions/domain.AppServeApp" + "platform": { + "type": "string" }, - "stages": { + "services": { "type": "array", "items": { - "$ref": "#/definitions/domain.StageResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse" } + }, + "template": { + "type": "string" + }, + "templateType": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "version": { + "type": "string" } } }, - "domain.GetAppServeAppTaskResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse": { "type": "object", "properties": { - "appServeAppTask": { - "$ref": "#/definitions/domain.AppServeAppTask" + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" } } }, - "domain.GetApplicationsResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse": { "type": "object", "properties": { "applications": { "type": "array", "items": { - "$ref": "#/definitions/domain.ApplicationResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse" } - } - } - }, - "domain.GetBootstrapKubeconfigResponse": { - "type": "object", - "properties": { - "kubeconfig": { - "$ref": "#/definitions/domain.BootstrapKubeconfig" - } - } - }, - "domain.GetCloudAccountResourceQuotaResponse": { - "type": "object", - "properties": { - "available": { - "type": "boolean" }, - "resourceQuota": { - "$ref": "#/definitions/domain.ResourceQuota" - } - } - }, - "domain.GetCloudAccountResponse": { - "type": "object", - "properties": { - "cloudAccount": { - "$ref": "#/definitions/domain.CloudAccountResponse" + "type": { + "type": "string" } } }, - "domain.GetCloudAccountsResponse": { + "github_com_openinfradev_tks-api_pkg_domain.StageResponse": { "type": "object", "properties": { - "cloudAccounts": { + "actions": { "type": "array", "items": { - "$ref": "#/definitions/domain.CloudAccountResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ActionResponse" } }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetClusterNodesResponse": { - "type": "object", - "properties": { - "nodes": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterNode" - } - } - } - }, - "domain.GetClustersResponse": { - "type": "object", - "properties": { - "clusters": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ClusterResponse" - } + "name": { + "description": "BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백)", + "type": "string" }, - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" - } - } - }, - "domain.GetDashboardChartResponse": { - "type": "object", - "properties": { - "chart": { - "$ref": "#/definitions/domain.DashboardChartResponse" - } - } - }, - "domain.GetDashboardChartsResponse": { - "type": "object", - "properties": { - "charts": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.DashboardChartResponse" - } + "result": { + "type": "string" + }, + "status": { + "type": "string" } } }, - "domain.GetDashboardResourcesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse": { "type": "object", "properties": { - "resources": { - "$ref": "#/definitions/domain.DashboardResource" + "content": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationId": { + "type": "string" + }, + "taker": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "updatedAt": { + "type": "string" } } }, - "domain.GetDashboardStacksResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse": { "type": "object", "properties": { - "stacks": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { "type": "array", "items": { - "$ref": "#/definitions/domain.DashboardStackResponse" - } - } - } - }, - "domain.GetMyProfileResponse": { - "type": "object", - "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } - } - } - }, - "domain.GetOrganizationResponse": { - "type": "object", - "properties": { - "organization": { - "type": "object", - "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" - }, - "status": { - "type": "string" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" - } + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" } + }, + "severity": { + "type": "string" + }, + "systemNotificationRuleId": { + "type": "string" } } }, - "domain.GetStackKubeConfigResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse": { "type": "object", + "required": [ + "key", + "order", + "value" + ], "properties": { - "kubeConfig": { + "key": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { "type": "string" } } }, - "domain.GetStackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter": { "type": "object", "properties": { - "stack": { - "$ref": "#/definitions/domain.StackResponse" + "operator": { + "type": "string" + }, + "order": { + "type": "integer" + }, + "value": { + "type": "string" } } }, - "domain.GetStackStatusResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse": { "type": "object", "properties": { - "stackStatus": { + "closedAt": { "type": "string" }, - "stepStatus": { + "cluster": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse" + }, + "createdAt": { + "type": "string" + }, + "firedAt": { + "type": "string" + }, + "grafanaUrl": { + "type": "string" + }, + "id": { + "type": "string" + }, + "lastTaker": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "node": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "policyName": { + "type": "string" + }, + "processingSec": { + "type": "integer" + }, + "rawData": { + "type": "string" + }, + "read": { + "type": "boolean" + }, + "severity": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationActions": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackStepStatus" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse" } + }, + "takedAt": { + "type": "string" + }, + "takedSec": { + "type": "integer" + }, + "updatedAt": { + "type": "string" } } }, - "domain.GetStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse": { "type": "object", "properties": { - "stackTemplate": { - "$ref": "#/definitions/domain.StackTemplateResponse" + "createdAt": { + "type": "string" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isSystem": { + "type": "boolean" + }, + "messageActionProposal": { + "type": "string" + }, + "messageContent": { + "type": "string" + }, + "messageTitle": { + "type": "string" + }, + "name": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "status": { + "type": "string" + }, + "systemNotificationCondition": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse" + }, + "systemNotificationTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse" + }, + "targetUsers": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" } } }, - "domain.GetStackTemplatesResponse": { + "github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse": { "type": "object", + "required": [ + "metricQuery" + ], "properties": { - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" + "createdAt": { + "type": "string" }, - "stackTemplates": { + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isSystem": { + "type": "boolean" + }, + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } + }, + "metricQuery": { + "type": "string" + }, + "name": { + "type": "string" + }, + "notificationType": { + "type": "string" + }, + "organizations": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackTemplateResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" } + }, + "updatedAt": { + "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" } } }, - "domain.GetStacksResponse": { + "github_com_openinfradev_tks-api_pkg_domain.TemplateCount": { "type": "object", "properties": { - "pagination": { - "$ref": "#/definitions/domain.PaginationResponse" + "organizationTemplate": { + "type": "integer" }, - "stacks": { + "tksTemplate": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.TemplateResponse": { + "type": "object", + "properties": { + "children": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse" } + }, + "isAllowed": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" } } }, - "domain.GetUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.Unit": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "updatedAt": { - "type": "string" - } + "data": { + "type": "array", + "items": { + "type": "string" } + }, + "name": { + "type": "string" } } }, - "domain.ImportClusterRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UnitNumber": { "type": "object", - "required": [ - "name", - "organizationId", - "stackTemplateId" - ], "properties": { - "cloudService": { - "type": "string" - }, - "clusterType": { - "type": "string" - }, - "description": { - "type": "string" - }, - "kubeconfig": { + "data": { "type": "array", "items": { "type": "integer" @@ -5727,110 +15509,145 @@ }, "name": { "type": "string" - }, - "organizationId": { - "type": "string" - }, - "stackTemplateId": { - "type": "string" } } }, - "domain.ImportClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest": { "type": "object", + "required": [ + "taskId" + ], "properties": { - "id": { + "endpointUrl": { + "type": "string" + }, + "helmRevision": { + "type": "integer" + }, + "previewEndpointUrl": { + "type": "string" + }, + "taskId": { "type": "string" } } }, - "domain.ListOrganizationBody": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest": { "type": "object", "properties": { - "createdAt": { + "abort": { + "type": "boolean" + }, + "appConfig": { "type": "string" }, - "description": { + "appSecret": { "type": "string" }, - "id": { + "artifactUrl": { + "type": "string" + }, + "executablePath": { + "type": "string" + }, + "extraEnv": { "type": "string" }, - "name": { + "imageUrl": { "type": "string" }, - "phone": { + "port": { "type": "string" }, - "primaryClusterId": { + "profile": { "type": "string" }, - "status": { + "promote": { + "description": "Update Strategy", + "type": "boolean" + }, + "resourceSpec": { "type": "string" }, - "updatedAt": { + "strategy": { + "description": "Task", "type": "string" } } }, - "domain.ListUserBody": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest": { "type": "object", + "required": [ + "status", + "taskId" + ], "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { + "output": { "type": "string" }, - "creator": { + "status": { "type": "string" }, - "department": { + "taskId": { "type": "string" - }, + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest": { + "type": "object", + "properties": { "description": { "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest": { + "type": "object", + "properties": { + "groupName": { "type": "string" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "sizeX": { + "type": "integer" }, - "role": { - "$ref": "#/definitions/domain.Role" + "sizeY": { + "type": "integer" }, - "updatedAt": { - "type": "string" + "widgets": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse" + } } } }, - "domain.LoginRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest": { "type": "object", "required": [ - "accountId", - "organizationId", + "email", + "name", "password" ], "properties": { - "accountId": { - "type": "string" + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 }, - "organizationId": { + "email": { "type": "string" }, + "name": { + "type": "string", + "maxLength": 30, + "minLength": 1 + }, "password": { "type": "string" } } }, - "domain.LoginResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse": { "type": "object", "properties": { "user": { @@ -5842,243 +15659,326 @@ "department": { "type": "string" }, - "name": { + "email": { "type": "string" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "id": { + "type": "string" }, - "passwordExpired": { - "type": "boolean" + "name": { + "type": "string" }, - "role": { - "$ref": "#/definitions/domain.Role" + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" }, - "token": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } } } } } }, - "domain.LogoutResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest": { "type": "object", + "required": [ + "name" + ], "properties": { - "ssoUrls": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } + "adminAccountId": { + "type": "string" + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "name": { + "type": "string", + "maxLength": 30, + "minLength": 1 } } }, - "domain.Organization": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse": { "type": "object", "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" - }, - "description": { - "type": "string" - }, "id": { "type": "string" - }, - "name": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" - }, - "status": { - "type": "integer" - }, - "statusDesc": { - "type": "string" - }, - "updatedAt": { - "type": "string" } } }, - "domain.PaginationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest": { "type": "object", + "required": [ + "newPassword", + "originPassword" + ], "properties": { - "filters": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.FilterResponse" - } - }, - "pageNumber": { - "type": "integer" - }, - "pageSize": { - "type": "integer" - }, - "sortColumn": { + "newPassword": { "type": "string" }, - "sortOrder": { + "originPassword": { "type": "string" - }, - "totalPages": { - "type": "integer" - }, - "totalRows": { - "type": "integer" } } }, - "domain.PingTokenRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest": { "type": "object", "required": [ - "organizationId", - "token" + "ID", + "isAllowed" ], "properties": { - "organizationId": { + "ID": { "type": "string" }, - "token": { - "type": "string" + "isAllowed": { + "type": "boolean", + "enum": [ + true, + false + ] } } }, - "domain.PodCount": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest": { "type": "object", "properties": { - "day": { - "type": "integer" - }, - "value": { - "type": "integer" + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest" + } } } }, - "domain.ResourceQuota": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { "type": "object", "properties": { - "quotas": { + "currentTargetClusterIds": { "type": "array", "items": { - "$ref": "#/definitions/domain.ResourceQuotaAttr" - } + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "newTargetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] } } }, - "domain.ResourceQuotaAttr": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { "type": "object", "properties": { - "quota": { - "type": "integer" + "description": { + "type": "string" }, - "required": { - "type": "integer" + "enforcementAction": { + "type": "string", + "enum": [ + "deny", + "dryrun", + "warn" + ], + "example": "deny" }, - "type": { + "mandatory": { + "type": "boolean" + }, + "match": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match" + }, + "matchYaml": { "type": "string" }, - "usage": { - "type": "integer" + "parameters": { + "type": "string", + "example": "{\"labels\":{\"key\":\"owner\",\"allowedRegex\":\"test*\"}" + }, + "policyName": { + "type": "string", + "example": "label 정책" + }, + "targetClusterIds": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "83bf8081-f0c5-4b31-826d-23f6f366ec90", + "83bf8081-f0c5-4b31-826d-23f6f366ec90" + ] + }, + "templateId": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" } } }, - "domain.Role": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", + "required": [ + "templateName" + ], "properties": { - "createdAt": { - "type": "string" - }, - "creator": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, "description": { "type": "string" }, - "id": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "name": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "updatedAt": { - "type": "string" + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "domain.RollbackAppServeAppRequest": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest": { "type": "object", "properties": { - "taskId": { + "primaryClusterId": { "type": "string" } } }, - "domain.SimpleCloudAccountResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest": { "type": "object", "properties": { - "awsAccountId": { + "projectRoleId": { "type": "string" - }, - "cloudService": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest": { + "type": "object", + "properties": { + "projectMembers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "projectMemberId": { + "type": "string" + }, + "projectRoleId": { + "type": "string" + } + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest": { + "type": "object", + "properties": { + "description": { "type": "string" - }, - "clusters": { - "type": "integer" - }, - "createdIAM": { - "type": "boolean" - }, + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { "description": { "type": "string" }, - "id": { - "type": "string" - }, "name": { "type": "string" }, - "organizationId": { + "projectLeaderId": { "type": "string" } } }, - "domain.SimpleClusterResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" + "templateCurrentVersion": { + "type": "string", + "example": "v1.0.1" }, - "organizationId": { + "templateTargetVerson": { + "type": "string", + "example": "v1.0.3" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest": { + "type": "object", + "properties": { + "description": { "type": "string" } } }, - "domain.SimpleStackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest": { + "type": "object", + "required": [ + "organizationIds" + ], + "properties": { + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest": { "type": "object", + "required": [ + "kubeType", + "kubeVersion", + "name", + "organizationIds", + "platform", + "serviceIds", + "template", + "version" + ], "properties": { "cloudService": { - "type": "string" + "type": "string", + "enum": [ + "AWS", + "BYOH" + ] }, "description": { "type": "string" }, - "id": { - "type": "string" - }, "kubeType": { "type": "string" }, @@ -6088,736 +15988,783 @@ "name": { "type": "string" }, - "services": { + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "platform": { + "type": "string" + }, + "serviceIds": { "type": "array", "items": { - "$ref": "#/definitions/domain.StackTemplateServiceResponse" + "type": "string" } }, "template": { "type": "string" + }, + "templateType": { + "type": "string", + "enum": [ + "STANDARD", + "MSA" + ] + }, + "version": { + "type": "string" } } }, - "domain.SimpleUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest": { "type": "object", "properties": { - "accountId": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { + "description": { "type": "string" } } }, - "domain.StackConfResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest": { "type": "object", "required": [ - "tksInfraNode", - "tksUserNode" + "messageContent", + "messageTitle", + "name", + "systemNotificationTemplateId" ], "properties": { - "tksCpNode": { - "type": "integer" - }, - "tksCpNodeMax": { - "type": "integer" - }, - "tksCpNodeType": { + "description": { "type": "string" }, - "tksInfraNode": { - "type": "integer", - "maximum": 3, - "minimum": 1 + "messageActionProposal": { + "type": "string" }, - "tksInfraNodeMax": { - "type": "integer" + "messageContent": { + "type": "string" }, - "tksInfraNodeType": { + "messageTitle": { "type": "string" }, - "tksUserNode": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "name": { + "type": "string" }, - "tksUserNodeMax": { - "type": "integer" + "systemNotificationCondition": { + "type": "object", + "properties": { + "duration": { + "type": "string" + }, + "enableEmail": { + "type": "boolean" + }, + "enablePortal": { + "type": "boolean" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter" + } + }, + "severity": { + "type": "string" + }, + "systemNotificationRuleId": { + "type": "string" + } + } }, - "tksUserNodeType": { + "systemNotificationTemplateId": { "type": "string" + }, + "targetUserIds": { + "type": "array", + "items": { + "type": "string" + } } } }, - "domain.StackResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest": { "type": "object", + "required": [ + "metricQuery", + "name", + "organizationIds" + ], "properties": { - "cloudAccount": { - "$ref": "#/definitions/domain.SimpleCloudAccountResponse" - }, - "conf": { - "$ref": "#/definitions/domain.StackConfResponse" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, "description": { "type": "string" }, - "favorited": { - "type": "boolean" - }, - "grafanaUrl": { - "type": "string" + "metricParameters": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse" + } }, - "id": { + "metricQuery": { "type": "string" }, "name": { "type": "string" }, - "organizationId": { - "type": "string" - }, - "primaryCluster": { - "type": "boolean" - }, - "resource": { - "$ref": "#/definitions/domain.DashboardStackResponse" + "organizationIds": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 }, - "stackTemplate": { - "$ref": "#/definitions/domain.SimpleStackTemplateResponse" + "name": { + "type": "string", + "maxLength": 100, + "minLength": 0 + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest": { + "type": "object", + "required": [ + "email", + "name", + "roles" + ], + "properties": { + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 }, - "status": { - "type": "string" + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 }, - "statusDesc": { + "email": { "type": "string" }, - "updatedAt": { + "name": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "userClusterEndpoint": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } } } }, - "domain.StackStepStatus": { + "github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse": { "type": "object", "properties": { - "maxStep": { - "type": "integer" - }, - "stage": { - "type": "string" - }, - "status": { - "type": "string" - }, - "step": { - "type": "integer" + "user": { + "type": "object", + "properties": { + "accountId": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "department": { + "type": "string" + }, + "description": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "organization": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse" + } + }, + "updatedAt": { + "type": "string" + } + } + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "type": "object", + "required": [ + "accountId", + "email", + "name", + "roles" + ], + "properties": { + "accountId": { + "type": "string" + }, + "department": { + "type": "string", + "maxLength": 50, + "minLength": 0 + }, + "description": { + "type": "string", + "maxLength": 100, + "minLength": 0 + }, + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole" + } + } + } + } } } }, - "domain.StackTemplate": { + "github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter": { "type": "object", "properties": { - "cloudService": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.User" - }, - "creatorId": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "kubeType": { - "type": "string" - }, - "kubeVersion": { - "type": "string" - }, "name": { "type": "string" }, - "organizationId": { - "type": "string" - }, - "platform": { - "type": "string" - }, - "services": { - "type": "array", - "items": { - "type": "integer" - } - }, - "template": { - "type": "string" - }, - "templateType": { - "type": "string" - }, - "updatedAt": { - "type": "string" - }, - "updator": { - "$ref": "#/definitions/domain.User" - }, - "updatorId": { + "type": { "type": "string" }, - "version": { + "value": { "type": "string" } } }, - "domain.StackTemplateResponse": { + "github_com_openinfradev_tks-api_pkg_domain.UserCreationRole": { "type": "object", + "required": [ + "id" + ], "properties": { - "cloudService": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "creator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "description": { - "type": "string" - }, "id": { "type": "string" - }, - "kubeType": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest": { + "type": "object", + "required": [ + "email", + "organizationId", + "userName" + ], + "properties": { + "email": { "type": "string" }, - "kubeVersion": { + "organizationId": { "type": "string" }, - "name": { + "userName": { "type": "string" - }, - "platform": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse": { + "type": "object", + "properties": { + "validityPeriod": { "type": "string" - }, - "services": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.StackTemplateServiceResponse" - } - }, - "template": { + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest": { + "type": "object", + "required": [ + "accountId", + "email", + "organizationId", + "userName" + ], + "properties": { + "accountId": { "type": "string" }, - "templateType": { + "email": { "type": "string" }, - "updatedAt": { + "organizationId": { "type": "string" }, - "updator": { - "$ref": "#/definitions/domain.SimpleUserResponse" - }, - "version": { + "userName": { "type": "string" } } }, - "domain.StackTemplateServiceApplicationResponse": { + "github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse": { "type": "object", "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "version": { + "validityPeriod": { "type": "string" } } }, - "domain.StackTemplateServiceResponse": { + "github_com_openinfradev_tks-api_pkg_domain.WidgetResponse": { "type": "object", "properties": { - "applications": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.StackTemplateServiceApplicationResponse" - } + "sizeX": { + "type": "integer" }, - "type": { + "sizeY": { + "type": "integer" + }, + "startX": { + "type": "integer" + }, + "startY": { + "type": "integer" + }, + "widgetKey": { "type": "string" } } }, - "domain.StageResponse": { + "github_com_openinfradev_tks-api_pkg_domain.WorkloadData": { "type": "object", "properties": { - "actions": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.ActionResponse" - } - }, "name": { - "description": "BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백)", "type": "string" }, - "result": { - "type": "string" - }, - "status": { - "type": "string" + "value": { + "type": "integer" } } }, - "domain.Unit": { + "github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest": { "type": "object", "properties": { - "data": { + "policyTemplateIds": { "type": "array", "items": { "type": "string" } - }, - "name": { - "type": "string" } } }, - "domain.UpdateAlertRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse": { "type": "object", "properties": { - "description": { - "type": "string" + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" } } }, - "domain.UpdateAppServeAppEndpointRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest": { "type": "object", "required": [ - "taskId" + "kind", + "rego", + "severity", + "templateName" ], "properties": { - "endpointUrl": { - "type": "string" - }, - "helmRevision": { - "type": "integer" - }, - "previewEndpointUrl": { - "type": "string" - }, - "taskId": { - "type": "string" - } - } - }, - "domain.UpdateAppServeAppRequest": { - "type": "object", - "properties": { - "abort": { - "type": "boolean" - }, - "appConfig": { - "type": "string" - }, - "appSecret": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "artifactUrl": { - "type": "string" - }, - "executablePath": { - "type": "string" + "description": { + "type": "string", + "example": "이 정책은 ..." }, - "extraEnv": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "imageUrl": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "port": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "profile": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "promote": { - "description": "Update Strategy", - "type": "boolean" + "rego": { + "type": "string", + "example": "rego 코드" }, - "resourceSpec": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "strategy": { - "description": "Task", - "type": "string" + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "domain.UpdateAppServeAppStatusRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest": { "type": "object", "required": [ - "status", - "taskId" + "currentVersion", + "expectedVersion", + "rego", + "versionUpType" ], "properties": { - "output": { - "type": "string" + "currentVersion": { + "type": "string", + "example": "v1.0.0" }, - "status": { - "type": "string" + "expectedVersion": { + "type": "string", + "example": "v1.1.0" }, - "taskId": { - "type": "string" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] + }, + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } + }, + "rego": { + "type": "string", + "example": "rego 코드" + }, + "versionUpType": { + "type": "string", + "enum": [ + "major", + "minor", + "patch" + ], + "example": "minor" } } }, - "domain.UpdateCloudAccountRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "version": { + "type": "string", + "example": "v1.1.1" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest": { + "type": "object", + "properties": { + "policyTemplateIds": { + "type": "array", + "items": { + "type": "string" + } } } }, - "domain.UpdateMyProfileRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse": { "type": "object", - "required": [ - "password" - ], "properties": { - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, - "email": { - "type": "string" - }, - "name": { - "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "password": { - "type": "string" + "existed": { + "type": "boolean" } } }, - "domain.UpdateMyProfileResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "department": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - } - } + "existed": { + "type": "boolean" } } }, - "domain.UpdateOrganizationRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest": { "type": "object", "required": [ - "name" + "rego" ], "properties": { - "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 + "libs": { + "type": "array", + "items": { + "type": "string" + } }, - "name": { + "rego": { "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "phone": { - "type": "string" - }, - "primaryClusterId": { - "type": "string" + "example": "Rego 코드" } } }, - "domain.UpdateOrganizationResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse": { "type": "object", "properties": { - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError" + } }, - "phone": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } } } }, - "domain.UpdatePasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse": { "type": "object", - "required": [ - "newPassword", - "originPassword" - ], "properties": { - "newPassword": { - "type": "string" - }, - "originPassword": { - "type": "string" + "deployVersion": { + "type": "object", + "additionalProperties": { + "type": "string" + } } } }, - "domain.UpdatePrimaryClusterRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse": { "type": "object", "properties": { - "primaryClusterId": { - "type": "string" + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" } } }, - "domain.UpdateStackRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "policyTemplate": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" } } }, - "domain.UpdateStackTemplateRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse": { "type": "object", "properties": { - "description": { - "type": "string" + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policyTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse" + } } } }, - "domain.UpdateUserRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse": { "type": "object", "properties": { - "department": { - "type": "string", - "maxLength": 50, - "minLength": 0 - }, - "description": { - "type": "string", - "maxLength": 100, - "minLength": 0 - }, - "email": { - "type": "string" - }, - "name": { - "type": "string", - "maxLength": 30, - "minLength": 1 - }, - "role": { - "type": "string", - "enum": [ - "admin", - "user" - ] + "policyTemplateStatistics": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics" + } } } }, - "domain.UpdateUserResponse": { + "github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse": { "type": "object", "properties": { - "user": { - "type": "object", - "properties": { - "accountId": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "department": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "$ref": "#/definitions/domain.Organization" - }, - "role": { - "$ref": "#/definitions/domain.Role" - }, - "updatedAt": { - "type": "string" - } - } + "versions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "v1.1.0", + "v1.0.1", + "v1.0.0" + ] } } }, - "domain.User": { + "github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse": { "type": "object", "properties": { - "accountId": { - "type": "string" - }, "createdAt": { "type": "string" }, "creator": { - "type": "string" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" }, - "department": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, "description": { - "type": "string" - }, - "email": { - "type": "string" + "type": "string", + "example": "이 정책은 ..." }, "id": { - "type": "string" + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" }, - "name": { - "type": "string" + "kind": { + "type": "string", + "example": "K8sRequiredLabels" }, - "organization": { - "$ref": "#/definitions/domain.Organization" + "libs": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "rego 코드" + ] }, - "password": { - "type": "string" + "parametersSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef" + } }, - "passwordExpired": { - "type": "boolean" + "permittedOrganizations": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse" + } }, - "passwordUpdatedAt": { - "type": "string" + "rego": { + "type": "string", + "example": "rego 코드" + }, + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" }, - "role": { - "$ref": "#/definitions/domain.Role" + "templateName": { + "type": "string", + "example": "필수 Label 검사" }, - "token": { - "type": "string" + "type": { + "type": "string", + "enum": [ + "tks", + "organization" + ], + "example": "tks" }, "updatedAt": { "type": "string" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "version": { + "type": "string", + "example": "v1.0.1" } } }, - "domain.VerifyIdentityForLostIdRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics": { "type": "object", - "required": [ - "email", - "organizationId", - "userName" - ], "properties": { - "email": { - "type": "string" - }, "organizationId": { "type": "string" }, - "userName": { - "type": "string" - } - } - }, - "domain.VerifyIdentityForLostIdResponse": { - "type": "object", - "properties": { - "validityPeriod": { + "organizationName": { "type": "string" + }, + "usageCount": { + "type": "integer" } } }, - "domain.VerifyIdentityForLostPasswordRequest": { + "github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest": { "type": "object", "required": [ - "accountId", - "email", - "organizationId", - "userName" + "templateName" ], "properties": { - "accountId": { - "type": "string" + "deprecated": { + "type": "boolean", + "example": false }, - "email": { + "description": { "type": "string" }, - "organizationId": { - "type": "string" + "permittedOrganizationIds": { + "type": "array", + "items": { + "type": "string" + } }, - "userName": { - "type": "string" - } - } - }, - "domain.VerifyIdentityForLostPasswordResponse": { - "type": "object", - "properties": { - "validityPeriod": { - "type": "string" + "severity": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "example": "medium" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" } } }, - "httpErrors.RestError": { + "github_com_openinfradev_tks-api_pkg_httpErrors.RestError": { "type": "object", "properties": { "code": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index e5915d23..fe6f2961 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -1,6 +1,56 @@ basePath: /api/1.0/ definitions: - domain.ActionResponse: + github_com_openinfradev_tks-api_internal_model.AppServeApp: + properties: + appType: + description: appType (spring/springboot) + type: string + createdAt: + type: string + deletedAt: + type: string + description: + description: description for application + type: string + endpointUrl: + description: endpoint URL of deployed app + type: string + grafanaUrl: + description: grafana dashboard URL for deployed app + type: string + id: + type: string + name: + description: application name + type: string + namespace: + description: application namespace + type: string + organizationId: + description: contractId is a contract ID which this app belongs to + type: string + previewEndpointUrl: + description: preview svc endpoint URL in B/G deployment + type: string + projectId: + description: project ID which this app belongs to + type: string + status: + description: status is status of deployed app + type: string + targetClusterId: + description: target cluster to which the app is deployed + type: string + targetClusterName: + description: target cluster name + type: string + type: + description: type (build/deploy/all) + type: string + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.ActionResponse: properties: body: additionalProperties: @@ -17,80 +67,132 @@ definitions: uri: type: string type: object - domain.AlertActionResponse: + github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest: properties: - alertId: - type: string - content: - type: string - createdAt: - type: string - id: - type: string - status: - type: string - taker: - $ref: '#/definitions/domain.SimpleUserResponse' - updatedAt: - type: string + stackTemplateIds: + items: + type: string + type: array + required: + - stackTemplateIds type: object - domain.AlertResponse: + github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest: properties: - alertActions: + systemNotificationTemplateIds: items: - $ref: '#/definitions/domain.AlertActionResponse' + type: string type: array - closedAt: + required: + - systemNotificationTemplateIds + type: object + github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest: + properties: + policyIds: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest: + properties: + projectMembers: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest: + properties: + accountId: type: string - cluster: - $ref: '#/definitions/domain.SimpleClusterResponse' - code: + adminPassword: type: string - createdAt: + department: + maxLength: 50 + minLength: 0 type: string description: + maxLength: 100 + minLength: 0 type: string - firedAt: - type: string - grade: - type: string - grafanaUrl: - type: string - id: - type: string - lastTaker: - $ref: '#/definitions/domain.SimpleUserResponse' - message: + email: type: string name: type: string - node: + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole' + type: array + required: + - accountId + - email + - roles + type: object + github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse: + properties: + id: type: string - organizationId: + type: object + github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest: + properties: + adminPassword: type: string - processingSec: - type: integer - rawData: + department: + maxLength: 50 + minLength: 0 type: string - status: + description: + maxLength: 100 + minLength: 0 type: string - takedAt: + email: type: string - takedSec: - type: integer - updatedAt: + name: type: string + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole' + type: array + required: + - email + - roles + type: object + github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse: + properties: + user: + properties: + accountId: + type: string + createdAt: + type: string + department: + type: string + description: + type: string + email: + type: string + id: + type: string + name: + type: string + organization: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array + updatedAt: + type: string + type: object type: object - domain.AppGroupResponse: + github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse: properties: appGroupType: - type: integer + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupType' clusterId: type: string createdAt: type: string creator: - $ref: '#/definitions/domain.SimpleUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' description: type: string id: @@ -98,22 +200,46 @@ definitions: name: type: string status: - type: integer + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus' statusDesc: type: string updatedAt: type: string updator: - $ref: '#/definitions/domain.SimpleUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' workflowId: type: string type: object - domain.AppServeApp: + github_com_openinfradev_tks-api_pkg_domain.AppGroupStatus: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + type: integer + x-enum-varnames: + - AppGroupStatus_PENDING + - AppGroupStatus_INSTALLING + - AppGroupStatus_RUNNING + - AppGroupStatus_DELETING + - AppGroupStatus_DELETED + - AppGroupStatus_INSTALL_ERROR + - AppGroupStatus_DELETE_ERROR + github_com_openinfradev_tks-api_pkg_domain.AppGroupType: + enum: + - 0 + - 1 + - 2 + type: integer + x-enum-varnames: + - AppGroupType_UNSPECIFIED + - AppGroupType_LMA + - AppGroupType_SERVICE_MESH + github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse: properties: - appServeAppTasks: - items: - $ref: '#/definitions/domain.AppServeAppTask' - type: array appType: description: appType (spring/springboot) type: string @@ -121,6 +247,9 @@ definitions: type: string deletedAt: type: string + description: + description: description for application + type: string endpointUrl: description: endpoint URL of deployed app type: string @@ -141,6 +270,9 @@ definitions: previewEndpointUrl: description: preview svc endpoint URL in B/G deployment type: string + projectId: + description: project ID which this app belongs to + type: string status: description: status is status of deployed app type: string @@ -156,7 +288,7 @@ definitions: updatedAt: type: string type: object - domain.AppServeAppTask: + github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse: properties: appConfig: description: java app config @@ -228,12 +360,21 @@ definitions: description: application version type: string type: object - domain.ApplicationResponse: + github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest: + properties: + users: + items: + type: string + type: array + required: + - users + type: object + github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse: properties: appGroupId: type: string applicationType: - type: integer + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationType' createdAt: type: string endpoint: @@ -245,52 +386,134 @@ definitions: updatedAt: type: string type: object - domain.Axis: + github_com_openinfradev_tks-api_pkg_domain.ApplicationType: + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + type: integer + x-enum-varnames: + - ApplicationType_UNSPECIFIED + - ApplicationType_THANOS + - ApplicationType_PROMETHEUS + - ApplicationType_GRAFANA + - ApplicationType_KIALI + - ApplicationType_KIBANA + - ApplicationType_ELASTICSERCH + - ApplicationType_CLOUD_CONSOLE + - ApplicationType_HORIZON + - ApplicationType_JAEGER + - ApplicationType_KUBERNETES_DASHBOARD + github_com_openinfradev_tks-api_pkg_domain.AuditResponse: + properties: + clientIP: + type: string + createdAt: + type: string + description: + type: string + group: + type: string + id: + type: string + message: + type: string + organizationId: + type: string + organizationName: + type: string + updatedAt: + type: string + userAccountId: + type: string + userId: + type: string + userName: + type: string + userRoles: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.Axis: properties: data: items: type: string type: array type: object - domain.BootstrapKubeconfig: + github_com_openinfradev_tks-api_pkg_domain.BarChartData: + properties: + series: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UnitNumber' + type: array + xAxis: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis' + type: object + github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig: properties: expiration: type: integer type: object - domain.ChartData: + github_com_openinfradev_tks-api_pkg_domain.ChartData: properties: podCounts: items: - $ref: '#/definitions/domain.PodCount' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PodCount' type: array series: items: - $ref: '#/definitions/domain.Unit' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Unit' type: array xAxis: - $ref: '#/definitions/domain.Axis' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis' yAxis: - $ref: '#/definitions/domain.Axis' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Axis' type: object - domain.CheckCloudAccountAwsAccountIdResponse: + github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse: properties: existed: type: boolean type: object - domain.CheckCloudAccountNameResponse: + github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse: properties: existed: type: boolean type: object - domain.CheckExistedResponse: + github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse: properties: existed: type: boolean type: object - domain.CloudAccount: + github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse: + properties: + isExist: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse: properties: - accessKeyId: - type: string awsAccountId: type: string cloudService: @@ -302,9 +525,7 @@ definitions: createdIAM: type: boolean creator: - $ref: '#/definitions/domain.User' - creatorId: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' description: type: string id: @@ -315,161 +536,48 @@ definitions: type: string resource: type: string - secretAccessKey: - type: string - sessionToken: - type: string status: - type: integer - statusDesc: type: string updatedAt: type: string updator: - $ref: '#/definitions/domain.User' - updatorId: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' type: object - domain.CloudAccountResponse: + github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse: properties: - awsAccountId: - type: string - cloudService: - type: string - clusters: + tksCpNode: type: integer - createdAt: + tksCpNodeMax: + type: integer + tksCpNodeType: type: string - createdIAM: - type: boolean - creator: - $ref: '#/definitions/domain.SimpleUserResponse' - description: + tksInfraNode: + type: integer + tksInfraNodeMax: + type: integer + tksInfraNodeType: type: string - id: + tksUserNode: + type: integer + tksUserNodeMax: + type: integer + tksUserNodeType: type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.ClusterHost: + properties: name: type: string - organizationId: - type: string - resource: - type: string status: type: string - updatedAt: - type: string - updator: - $ref: '#/definitions/domain.SimpleUserResponse' type: object - domain.Cluster: + github_com_openinfradev_tks-api_pkg_domain.ClusterNode: properties: - byoClusterEndpointHost: - type: string - byoClusterEndpointPort: - type: integer - cloudAccount: - $ref: '#/definitions/domain.CloudAccount' - cloudAccountId: - type: string - cloudService: - type: string - clusterType: - type: integer - conf: - $ref: '#/definitions/domain.ClusterConf' - createdAt: - type: string - creator: - $ref: '#/definitions/domain.User' - creatorId: - type: string - description: - type: string - favorited: - type: boolean - id: - type: string - isStack: - type: boolean - kubeconfig: - items: - type: integer - type: array - name: - type: string - organizationId: - type: string - stackTemplate: - $ref: '#/definitions/domain.StackTemplate' - stackTemplateId: - type: string - status: - type: integer - statusDesc: - type: string - updatedAt: - type: string - updator: - $ref: '#/definitions/domain.User' - updatorId: - type: string - type: object - domain.ClusterConf: - properties: - tksCpNode: - type: integer - tksCpNodeMax: - type: integer - tksCpNodeType: - type: string - tksInfraNode: - type: integer - tksInfraNodeMax: - type: integer - tksInfraNodeType: - type: string - tksUserNode: - type: integer - tksUserNodeMax: - type: integer - tksUserNodeType: - type: string - type: object - domain.ClusterConfResponse: - properties: - tksCpNode: - type: integer - tksCpNodeMax: - type: integer - tksCpNodeType: - type: string - tksInfraNode: - type: integer - tksInfraNodeMax: - type: integer - tksInfraNodeType: - type: string - tksUserNode: - type: integer - tksUserNodeMax: - type: integer - tksUserNodeType: - type: string - type: object - domain.ClusterHost: - properties: - name: - type: string - status: - type: string - type: object - domain.ClusterNode: - properties: - command: + command: type: string hosts: items: - $ref: '#/definitions/domain.ClusterHost' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterHost' type: array registered: type: integer @@ -484,26 +592,28 @@ definitions: validity: type: integer type: object - domain.ClusterResponse: + github_com_openinfradev_tks-api_pkg_domain.ClusterResponse: properties: byoClusterEndpointHost: type: string byoClusterEndpointPort: type: integer cloudAccount: - $ref: '#/definitions/domain.SimpleCloudAccountResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse' cloudService: type: string clusterType: type: string conf: - $ref: '#/definitions/domain.ClusterConfResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterConfResponse' createdAt: type: string creator: - $ref: '#/definitions/domain.SimpleUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' description: type: string + favorited: + type: boolean id: type: string isStack: @@ -513,7 +623,7 @@ definitions: organizationId: type: string stackTemplate: - $ref: '#/definitions/domain.SimpleStackTemplateResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse' status: type: string statusDesc: @@ -521,9 +631,9 @@ definitions: updatedAt: type: string updator: - $ref: '#/definitions/domain.SimpleUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' type: object - domain.ClusterSiteValuesResponse: + github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse: properties: byoClusterEndpointHost: type: string @@ -554,7 +664,17 @@ definitions: tksUserNodeType: type: string type: object - domain.CreateAppGroupRequest: + github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse: + properties: + result: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse: + properties: + result: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest: properties: appGroupType: enum: @@ -571,12 +691,12 @@ definitions: - clusterId - name type: object - domain.CreateAppGroupResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse: properties: id: type: string type: object - domain.CreateAppServeAppRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest: properties: appConfig: type: string @@ -630,7 +750,7 @@ definitions: - name - targetClusterId type: object - domain.CreateApplicationRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest: properties: applicationType: type: string @@ -639,12 +759,16 @@ definitions: metadata: type: string type: object - domain.CreateBootstrapKubeconfigResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest: + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse: + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse: properties: kubeconfig: - $ref: '#/definitions/domain.BootstrapKubeconfig' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig' type: object - domain.CreateCloudAccountRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest: properties: accessKeyId: maxLength: 128 @@ -677,12 +801,12 @@ definitions: - name - secretAccessKey type: object - domain.CreateCloudAccountResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse: properties: id: type: string type: object - domain.CreateClusterRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest: properties: byoClusterEndpointHost: type: string @@ -705,6 +829,10 @@ definitions: type: string organizationId: type: string + policyIds: + items: + type: string + type: array stackTemplateId: type: string tksCpNode: @@ -731,14 +859,32 @@ definitions: - organizationId - stackTemplateId type: object - domain.CreateClusterResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse: properties: id: type: string type: object - domain.CreateOrganizationRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest: + properties: + contents: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardContents' + type: array + dashboardKey: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse: + properties: + dashboardId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest: properties: - Email: + adminAccountId: + type: string + adminEmail: + type: string + adminName: type: string description: maxLength: 100 @@ -746,13 +892,173 @@ definitions: type: string name: type: string - phone: + required: + - adminAccountId + - adminEmail + - name + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest: + properties: + description: + type: string + enforcementAction: + enum: + - deny + - dryrun + - warn + example: deny + type: string + mandatory: + type: boolean + match: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match' + matchYaml: + example: "namespaces:\r\n- testns1" + type: string + parameters: + example: '{"key":"value"}' + type: string + policyName: + example: label 정책 + type: string + policyResourceName: + example: labelpolicy + type: string + targetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + required: + - enforcementAction + - policyName + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse: + properties: + id: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse: + properties: + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest: + properties: + deprecated: + example: false + type: boolean + description: + example: 이 정책은 ... + type: string + kind: + example: K8sRequiredLabels + type: string + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + permittedOrganizationIds: + items: + type: string + type: array + rego: + example: rego 코드 + type: string + severity: + enum: + - low + - medium + - high + example: medium + type: string + templateName: + example: 필수 Label 검사 + type: string + required: + - kind + - rego + - severity + - templateName + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest: + properties: + currentVersion: + example: v1.0.0 + type: string + expectedVersion: + example: v1.1.0 + type: string + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + rego: + example: rego 코드 + type: string + versionUpType: + enum: + - major + - minor + - patch + example: minor + type: string + required: + - currentVersion + - expectedVersion + - rego + - versionUpType + type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse: + properties: + version: + example: v1.1.1 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest: + properties: + description: + type: string + namespace: + type: string + stackId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest: + properties: + description: + type: string + name: + type: string + projectLeaderId: type: string required: - - Email - name type: object - domain.CreateStackRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse: + properties: + projectId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest: properties: cloudAccountId: type: string @@ -767,6 +1073,10 @@ definitions: type: string name: type: string + policyIds: + items: + type: string + type: array stackTemplateId: type: string tksCpNode: @@ -794,25 +1104,36 @@ definitions: - name - stackTemplateId type: object - domain.CreateStackResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse: properties: id: type: string type: object - domain.CreateStackTemplateRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest: properties: cloudService: enum: - AWS - - AZZURE - - GCP + - BYOH type: string description: type: string + kubeType: + type: string + kubeVersion: + type: string name: type: string + organizationIds: + items: + type: string + type: array platform: type: string + serviceIds: + items: + type: string + type: array template: type: string templateType: @@ -823,76 +1144,162 @@ definitions: version: type: string required: + - kubeType + - kubeVersion - name + - organizationIds - platform + - serviceIds - template - version type: object - domain.CreateStackTemplateResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse: properties: id: type: string type: object - domain.CreateUserRequest: + github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest: properties: - accountId: - type: string - department: - maxLength: 50 - minLength: 0 - type: string description: - maxLength: 100 - minLength: 0 type: string - email: + messageActionProposal: type: string - name: + messageContent: type: string - password: + messageTitle: type: string - role: - enum: - - admin - - user + name: type: string - required: - - accountId - - email - - password - - role - type: object - domain.CreateUserResponse: - properties: - user: + systemNotificationCondition: properties: - accountId: - type: string - department: + duration: type: string - description: - type: string - email: - type: string - id: - type: string - name: + enableEmail: + type: boolean + enablePortal: + type: boolean + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: type: string - organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' type: object + systemNotificationTemplateId: + type: string + targetUserIds: + items: + type: string + type: array + required: + - messageContent + - messageTitle + - name + - systemNotificationTemplateId type: object - domain.DashboardChartResponse: + github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse: properties: - chartData: - $ref: '#/definitions/domain.ChartData' - chartType: + id: type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest: + properties: description: type: string - duration: + metricParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse' + type: array + metricQuery: + type: string + name: + type: string + organizationIds: + items: + type: string + type: array + required: + - metricQuery + - name + - organizationIds + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest: + properties: + description: + maxLength: 100 + minLength: 0 + type: string + name: + type: string + required: + - name + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse: + properties: + id: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest: + properties: + accountId: + type: string + department: + maxLength: 50 + minLength: 0 + type: string + description: + maxLength: 100 + minLength: 0 + type: string + email: + type: string + name: + type: string + password: + type: string + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole' + type: array + required: + - accountId + - email + - password + - roles + type: object + github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse: + properties: + user: + properties: + accountId: + type: string + department: + type: string + description: + type: string + email: + type: string + id: + type: string + name: + type: string + organization: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array + type: object + type: object + github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse: + properties: + chartData: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ChartData' + chartType: + type: string + description: + type: string + duration: type: string interval: type: string @@ -907,18 +1314,52 @@ definitions: year: type: string type: object - domain.DashboardResource: + github_com_openinfradev_tks-api_pkg_domain.DashboardContents: + properties: + groupName: + type: string + sizeX: + type: integer + sizeY: + type: integer + widgets: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus: + properties: + error: + type: integer + normal: + type: integer + warning: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate: + properties: + policy: + type: integer + policyTemplate: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.DashboardResource: properties: cpu: type: string memory: type: string stack: - type: string + properties: + abnormal: + type: string + normal: + type: string + type: object storage: type: string type: object - domain.DashboardStackResponse: + github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse: properties: cpu: type: string @@ -941,7 +1382,7 @@ definitions: updatedAt: type: string type: object - domain.DeleteCloudAccountRequest: + github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest: properties: accessKeyId: maxLength: 128 @@ -958,16 +1399,89 @@ definitions: - accessKeyId - secretAccessKey type: object - domain.FilterResponse: + github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse: + properties: + id: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest: + properties: + policyIds: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse: + properties: + id: + type: string + name: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest: + properties: + adminPassword: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse: + properties: + accountId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.EndpointResponse: + properties: + group: + type: string + name: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest: + properties: + libs: + items: + type: string + type: array + rego: + example: Rego 코드 + type: string + required: + - rego + type: object + github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse: + properties: + errors: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError' + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.FilterResponse: properties: column: type: string + operator: + type: string + releation: + type: string values: items: type: string type: array type: object - domain.FindIdRequest: + github_com_openinfradev_tks-api_pkg_domain.FindIdRequest: properties: code: type: string @@ -983,12 +1497,12 @@ definitions: - organizationId - userName type: object - domain.FindIdResponse: + github_com_openinfradev_tks-api_pkg_domain.FindIdResponse: properties: accountId: type: string type: object - domain.FindPasswordRequest: + github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest: properties: accountId: type: string @@ -1007,122 +1521,227 @@ definitions: - organizationId - userName type: object - domain.GetAlertResponse: - properties: - alert: - $ref: '#/definitions/domain.AlertResponse' - type: object - domain.GetAlertsResponse: - properties: - alerts: - items: - $ref: '#/definitions/domain.AlertResponse' - type: array - pagination: - $ref: '#/definitions/domain.PaginationResponse' - type: object - domain.GetAppGroupResponse: + github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse: properties: appGroup: - $ref: '#/definitions/domain.AppGroupResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse' type: object - domain.GetAppGroupsResponse: + github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse: properties: appGroups: items: - $ref: '#/definitions/domain.AppGroupResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppGroupResponse' type: array pagination: - $ref: '#/definitions/domain.PaginationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' type: object - domain.GetAppServeAppResponse: + github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse: properties: appServeApp: - $ref: '#/definitions/domain.AppServeApp' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppResponse' + appServeAppTask: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppServeAppTaskResponse' stages: items: - $ref: '#/definitions/domain.StageResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StageResponse' type: array type: object - domain.GetAppServeAppTaskResponse: + github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse: properties: - appServeAppTask: - $ref: '#/definitions/domain.AppServeAppTask' + applications: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ApplicationResponse' + type: array type: object - domain.GetApplicationsResponse: + github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse: properties: - applications: + audit: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse: + properties: + audits: items: - $ref: '#/definitions/domain.ApplicationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AuditResponse' type: array + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' type: object - domain.GetBootstrapKubeconfigResponse: + github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse: properties: kubeconfig: - $ref: '#/definitions/domain.BootstrapKubeconfig' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.BootstrapKubeconfig' type: object - domain.GetCloudAccountResourceQuotaResponse: + github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse: properties: available: type: boolean resourceQuota: - $ref: '#/definitions/domain.ResourceQuota' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuota' type: object - domain.GetCloudAccountResponse: + github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse: properties: cloudAccount: - $ref: '#/definitions/domain.CloudAccountResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse' type: object - domain.GetCloudAccountsResponse: + github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse: properties: cloudAccounts: items: - $ref: '#/definitions/domain.CloudAccountResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CloudAccountResponse' type: array pagination: - $ref: '#/definitions/domain.PaginationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' type: object - domain.GetClusterNodesResponse: + github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse: properties: nodes: items: - $ref: '#/definitions/domain.ClusterNode' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterNode' type: array type: object - domain.GetClustersResponse: + github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse: + properties: + cluster: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse: properties: clusters: items: - $ref: '#/definitions/domain.ClusterResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterResponse' type: array pagination: - $ref: '#/definitions/domain.PaginationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' type: object - domain.GetDashboardChartResponse: + github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse: properties: chart: - $ref: '#/definitions/domain.DashboardChartResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse' type: object - domain.GetDashboardChartsResponse: + github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse: properties: charts: items: - $ref: '#/definitions/domain.DashboardChartResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardChartResponse' type: array type: object - domain.GetDashboardResourcesResponse: + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse: + properties: + chartData: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData' + chartType: + type: string + description: + type: string + duration: + type: string + interval: + type: string + name: + type: string + organizationId: + type: string + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse: + properties: + policyCount: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount' + templateCount: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse: + properties: + statuses: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyStatus' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse: + properties: + updatedResources: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardPolicyUpdate' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse: + properties: + chartData: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData' + chartType: + type: string + description: + type: string + duration: + type: string + interval: + type: string + name: + type: string + organizationId: + type: string + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response: + properties: + chartData: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.BarChartData' + chartType: + type: string + description: + type: string + duration: + type: string + interval: + type: string + name: + type: string + organizationId: + type: string + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse: properties: resources: - $ref: '#/definitions/domain.DashboardResource' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardResource' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse: + properties: + groupName: + type: string + sizeX: + type: integer + sizeY: + type: integer + widgets: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse' + type: array type: object - domain.GetDashboardStacksResponse: + github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse: properties: stacks: items: - $ref: '#/definitions/domain.DashboardStackResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse: + properties: + data: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.WorkloadData' + type: array + title: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse: + properties: + templates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo' type: array type: object - domain.GetMyProfileResponse: + github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse: properties: user: properties: @@ -1137,80 +1756,300 @@ definitions: name: type: string organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array type: object type: object - domain.GetOrganizationResponse: + github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse: properties: organization: - properties: - createdAt: - type: string - creator: - type: string - description: - type: string - id: - type: string - name: - type: string - phone: - type: string - primaryClusterId: - type: string - status: - type: string - statusDesc: - type: string - updatedAt: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse: + properties: + permissions: + description: Permissions *PermissionTemplateResponse `json:"permissions"` + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse: + properties: + permissions: + description: Permissions *PermissionSetResponse `json:"permissions"` + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse: + properties: + policyNotification: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policyNotifications: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse: + properties: + policy: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse: + properties: + deployVersion: + additionalProperties: + type: string type: object type: object - domain.GetStackKubeConfigResponse: + github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse: + properties: + policyTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse: + properties: + policyTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse: + properties: + kubeconfig: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse: + properties: + projectLeaderCount: + type: integer + projectMemberAllCount: + type: integer + projectMemberCount: + type: integer + projectViewerCount: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse: + properties: + projectMember: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + projectMembers: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse: + properties: + k8sResources: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse: + properties: + kubeConfig: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse: + properties: + resourcesUsage: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse: + properties: + projectNamespace: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse: + properties: + projectNamespaces: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse: + properties: + project: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse: + properties: + projectRole: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse: + properties: + projectRoles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + projects: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ProjectResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse: properties: kubeConfig: type: string type: object - domain.GetStackResponse: + github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse: + properties: + affectedPolicies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatus' + type: array + templateCurrentVersion: + example: v1.0.1 + type: string + templateDescription: + example: 파라미터로 설정된 레이블 검사 + type: string + templateId: + example: 708d1e5b-4e6f-40e9-87a3-329e2fd051a5 + type: string + templateLatestVersion: + example: v1.0.3 + type: string + templateLatestVersionReleaseDate: + format: date-time + type: string + templateMandatory: + type: boolean + templateName: + example: 레이블 요구 + type: string + updatedPolicyParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetStackResponse: properties: stack: - $ref: '#/definitions/domain.StackResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse' type: object - domain.GetStackStatusResponse: + github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse: properties: stackStatus: type: string stepStatus: items: - $ref: '#/definitions/domain.StackStepStatus' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackStepStatus' type: array type: object - domain.GetStackTemplateResponse: + github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse: properties: stackTemplate: - $ref: '#/definitions/domain.StackTemplateResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse: + properties: + services: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse: + properties: + templateIds: + items: + type: string + type: array type: object - domain.GetStackTemplatesResponse: + github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse: properties: pagination: - $ref: '#/definitions/domain.PaginationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' stackTemplates: items: - $ref: '#/definitions/domain.StackTemplateResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse' type: array type: object - domain.GetStacksResponse: + github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse: properties: pagination: - $ref: '#/definitions/domain.PaginationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' stacks: items: - $ref: '#/definitions/domain.StackResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse: + properties: + systemNotification: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse: + properties: + systemNotificationRule: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + systemNotificationRules: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse: + properties: + systemNotificationTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + systemNotificationTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + systemNotifications: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse' type: array type: object - domain.GetUserResponse: + github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse: + properties: + createdAt: + type: string + creator: + type: string + description: + type: string + id: + type: string + name: + type: string + organizationId: + type: string + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.GetUserResponse: properties: user: properties: @@ -1231,14 +2070,33 @@ definitions: name: type: string organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array updatedAt: type: string type: object type: object - domain.ImportClusterRequest: + github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + users: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse: + properties: + permissions: + description: Permissions *MergedPermissionSetResponse `json:"permissions"` + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest: properties: cloudService: type: string @@ -1261,31 +2119,84 @@ definitions: - organizationId - stackTemplateId type: object - domain.ImportClusterResponse: + github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse: properties: id: type: string type: object - domain.ListOrganizationBody: + github_com_openinfradev_tks-api_pkg_domain.Kinds: properties: - createdAt: - type: string - description: - type: string - id: - type: string - name: - type: string - phone: - type: string - primaryClusterId: - type: string - status: - type: string - updatedAt: - type: string + apiGroups: + items: + type: string + type: array + kinds: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse: + properties: + organizations: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + type: array + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policyTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse: + properties: + policyTemplateStatistics: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse: + properties: + versions: + example: + - v1.1.0 + - v1.0.1 + - v1.0.0 + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse: + properties: + polices: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse' + type: array type: object - domain.ListUserBody: + github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ListUserBody: properties: accountId: type: string @@ -1304,13 +2215,15 @@ definitions: name: type: string organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array updatedAt: type: string type: object - domain.LoginRequest: + github_com_openinfradev_tks-api_pkg_domain.LoginRequest: properties: accountId: type: string @@ -1323,7 +2236,7 @@ definitions: - organizationId - password type: object - domain.LoginResponse: + github_com_openinfradev_tks-api_pkg_domain.LoginResponse: properties: user: properties: @@ -1334,29 +2247,88 @@ definitions: name: type: string organization: - $ref: '#/definitions/domain.Organization' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' passwordExpired: type: boolean - role: - $ref: '#/definitions/domain.Role' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array token: type: string type: object type: object - domain.LogoutResponse: + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo: properties: - ssoUrls: - additionalProperties: - items: - type: string - type: array - type: object + description: + example: org 레이블 설정 여부 검사 + type: string + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + policyName: + example: org 레이블 요구 + type: string type: object - domain.Organization: + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo: properties: - createdAt: + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 type: string - creator: + type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryTemplateInfo: + properties: + description: + example: 파라미터로 설정된 레이블 검사 + type: string + mandatory: + type: boolean + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo' + type: array + templateId: + example: 708d1e5b-4e6f-40e9-87a3-329e2fd051a5 + type: string + templateName: + example: 레이블 요구 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.Match: + properties: + excludedNamespaces: + items: + type: string + type: array + kinds: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Kinds' + type: array + namespaces: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse: + properties: + children: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse' + type: array + isAllowed: + type: boolean + key: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse: + properties: + admin: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + createdAt: type: string description: type: string @@ -1364,22 +2336,34 @@ definitions: type: string name: type: string - phone: - type: string + policyTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse' + type: array primaryClusterId: type: string - status: + stackCount: type: integer + stackTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse' + type: array + status: + type: string statusDesc: type: string + systemNotificationTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse' + type: array updatedAt: type: string type: object - domain.PaginationResponse: + github_com_openinfradev_tks-api_pkg_domain.PaginationResponse: properties: filters: items: - $ref: '#/definitions/domain.FilterResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.FilterResponse' type: array pageNumber: type: integer @@ -1394,659 +2378,5927 @@ definitions: totalRows: type: integer type: object - domain.PingTokenRequest: + github_com_openinfradev_tks-api_pkg_domain.ParameterDef: properties: - organizationId: + children: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + defaultValue: type: string - token: + isArray: + type: boolean + isNew: + type: boolean + key: + type: string + type: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.PermissionResponse: + properties: + ID: + type: string + children: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' + type: array + endpoints: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.EndpointResponse' + type: array + isAllowed: + type: boolean + key: + type: string + name: type: string - required: - - organizationId - - token type: object - domain.PodCount: + github_com_openinfradev_tks-api_pkg_domain.PodCount: properties: day: type: integer value: type: integer type: object - domain.ResourceQuota: - properties: - quotas: - items: - $ref: '#/definitions/domain.ResourceQuotaAttr' - type: array - type: object - domain.ResourceQuotaAttr: + github_com_openinfradev_tks-api_pkg_domain.PolicyCount: properties: - quota: + deny: type: integer - required: + dryrun: type: integer - type: - type: string - usage: + fromOrgTemplate: + type: integer + fromTksTemplate: + type: integer + total: + type: integer + warn: type: integer type: object - domain.Role: + github_com_openinfradev_tks-api_pkg_domain.PolicyNotificationResponse: properties: + cluster: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse' createdAt: type: string - creator: - type: string - description: + grafanaUrl: type: string id: type: string - name: + messageActionProposal: type: string - updatedAt: + messageContent: type: string - type: object - domain.RollbackAppServeAppRequest: - properties: - taskId: + messageTitle: type: string - type: object - domain.SimpleCloudAccountResponse: - properties: - awsAccountId: + notificationType: type: string - cloudService: + organizationId: type: string - clusters: - type: integer - createdIAM: - type: boolean - description: + policyName: type: string - id: + rawData: type: string - name: + read: + type: boolean + severity: type: string - organizationId: + status: + type: string + updatedAt: type: string type: object - domain.SimpleClusterResponse: + github_com_openinfradev_tks-api_pkg_domain.PolicyParameter: properties: - id: - type: string name: type: string - organizationId: + type: + type: string + updatable: + type: boolean + value: type: string type: object - domain.SimpleStackTemplateResponse: + github_com_openinfradev_tks-api_pkg_domain.PolicyResponse: properties: - cloudService: + createdAt: + format: date-time type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' description: type: string + enforcementAction: + example: deny + type: string + filledParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff type: string - kubeType: + mandatory: + type: boolean + match: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match' + matchYaml: + example: "namespaces:\r\n- testns1" type: string - kubeVersion: + parameters: + example: '{"key":"value"}' type: string - name: + policyName: + example: label 정책 type: string - services: + policyResourceName: + example: labelpolicy + type: string + targetClusters: + description: TargetClusterIds []string `json:"targetClusterIds" + example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` items: - $ref: '#/definitions/domain.StackTemplateServiceResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse' type: array - template: - type: string - type: object - domain.SimpleUserResponse: - properties: - accountId: + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff type: string - id: + templateName: + example: 필수 Label 검사 type: string - name: + updatedAt: + format: date-time type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' type: object - domain.StackConfResponse: + github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse: properties: - tksCpNode: - type: integer - tksCpNodeMax: - type: integer - tksCpNodeType: - type: string - tksInfraNode: - maximum: 3 - minimum: 1 - type: integer - tksInfraNodeMax: - type: integer - tksInfraNodeType: + policyCount: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyCount' + templateCount: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateCount' + type: object + github_com_openinfradev_tks-api_pkg_domain.PolicyStatus: + properties: + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 type: string - tksUserNode: - maximum: 100 - minimum: 0 - type: integer - tksUserNodeMax: - type: integer - tksUserNodeType: + policyName: type: string - required: - - tksInfraNode - - tksUserNode + policyPolicyParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyParameter' + type: array type: object - domain.StackResponse: + github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse: properties: - cloudAccount: - $ref: '#/definitions/domain.SimpleCloudAccountResponse' - conf: - $ref: '#/definitions/domain.StackConfResponse' createdAt: type: string creator: - $ref: '#/definitions/domain.SimpleUserResponse' - description: - type: string - favorited: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + deprecated: + example: false type: boolean - grafanaUrl: + description: + example: 이 정책은 ... type: string id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff type: string - name: + kind: + example: K8sRequiredLabels type: string - organizationId: + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + rego: + example: rego 코드 type: string - primaryCluster: - type: boolean - resource: - $ref: '#/definitions/domain.DashboardStackResponse' - stackTemplate: - $ref: '#/definitions/domain.SimpleStackTemplateResponse' - status: + severity: + enum: + - low + - medium + - high + example: medium type: string - statusDesc: + templateName: + example: 필수 Label 검사 + type: string + type: + enum: + - tks + - organization + example: tks type: string updatedAt: type: string updator: - $ref: '#/definitions/domain.SimpleUserResponse' - userClusterEndpoint: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + version: + example: v1.0.1 type: string type: object - domain.StackStepStatus: + github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateStatistics: properties: - maxStep: - type: integer - stage: + organizationId: type: string - status: + organizationName: type: string - step: + usageCount: type: integer type: object - domain.StackTemplate: + github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateTwoVersionResponse: properties: - cloudService: - type: string createdAt: type: string creator: - $ref: '#/definitions/domain.User' - creatorId: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + currentVersion: + example: v1.0.1 type: string + deprecated: + example: false + type: boolean description: + example: 이 정책은 ... type: string id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff type: string - kubeType: - type: string - kubeVersion: - type: string - name: - type: string - organizationId: + kind: + example: K8sRequiredLabels type: string - platform: + latestVersion: + example: v1.0.1 type: string - services: + libs: + example: + - rego 코드 items: - type: integer + type: string type: array - template: + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + rego: + example: rego 코드 type: string - templateType: + severity: + enum: + - low + - medium + - high + example: medium type: string - updatedAt: + templateName: + example: 필수 Label 검사 type: string - updator: - $ref: '#/definitions/domain.User' - updatorId: + type: + enum: + - tks + - organization + example: tks type: string - version: + updatedAt: type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' type: object - domain.StackTemplateResponse: + github_com_openinfradev_tks-api_pkg_domain.ProjectDetailResponse: properties: - cloudService: - type: string createdAt: + description: |- + AppCount int `json:"appCount"` + NamespaceCount int `json:"namespaceCount"` + MemberCount int `json:"memberCount"` type: string - creator: - $ref: '#/definitions/domain.SimpleUserResponse' description: type: string id: type: string - kubeType: - type: string - kubeVersion: - type: string name: type: string - platform: + organizationId: type: string - services: - items: - $ref: '#/definitions/domain.StackTemplateServiceResponse' - type: array - template: + projectLeaderAccountId: type: string - templateType: + projectLeaderDepartment: type: string - updatedAt: + projectLeaderId: type: string - updator: - $ref: '#/definitions/domain.SimpleUserResponse' - version: + projectLeaderName: type: string - type: object - domain.StackTemplateServiceApplicationResponse: - properties: - description: + projectRoleId: type: string - name: + projectRoleName: type: string - version: + updatedAt: type: string type: object - domain.StackTemplateServiceResponse: + github_com_openinfradev_tks-api_pkg_domain.ProjectMemberRequest: properties: - applications: - items: - $ref: '#/definitions/domain.StackTemplateServiceApplicationResponse' - type: array - type: + projectRoleId: + type: string + projectUserId: type: string + required: + - projectRoleId + - projectUserId type: object - domain.StageResponse: + github_com_openinfradev_tks-api_pkg_domain.ProjectMemberResponse: properties: - actions: - items: - $ref: '#/definitions/domain.ActionResponse' - type: array - name: - description: BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백) + createdAt: type: string - result: + id: type: string - status: + projectId: type: string - type: object - domain.Unit: - properties: - data: - items: - type: string - type: array - name: + projectRoleId: + type: string + projectRoleName: + type: string + projectUserAccountId: + type: string + projectUserDepartment: + type: string + projectUserEmail: + type: string + projectUserId: + type: string + projectUserName: + type: string + updatedAt: type: string type: object - domain.UpdateAlertRequest: + github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceK8sResources: properties: - description: + cronjobs: + type: integer + daemonsets: + type: integer + deployments: + type: integer + ingresses: + type: integer + jobs: + type: integer + pods: + type: integer + pvcs: + type: integer + services: + type: integer + statefulsets: + type: integer + updatedAt: type: string type: object - domain.UpdateAppServeAppEndpointRequest: + github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResourcesUsage: properties: - endpointUrl: + cpu: type: string - helmRevision: - type: integer - previewEndpointUrl: + memory: type: string - taskId: + storage: type: string - required: - - taskId type: object - domain.UpdateAppServeAppRequest: + github_com_openinfradev_tks-api_pkg_domain.ProjectNamespaceResponse: properties: - abort: - type: boolean - appConfig: + appCount: + type: integer + createdAt: type: string - appSecret: + description: type: string - artifactUrl: + grafanaUrl: type: string - executablePath: - type: string - extraEnv: + namespace: type: string - imageUrl: + projectId: type: string - port: + stackId: type: string - profile: + stackName: type: string - promote: - description: Update Strategy - type: boolean - resourceSpec: + status: type: string - strategy: - description: Task + updatedAt: type: string type: object - domain.UpdateAppServeAppStatusRequest: + github_com_openinfradev_tks-api_pkg_domain.ProjectResponse: properties: - output: + appCount: + type: integer + createdAt: type: string - status: + description: type: string - taskId: + id: + type: string + isMyProject: + type: string + memberCount: + type: integer + name: + type: string + namespaceCount: + type: integer + organizationId: + type: string + projectLeaderId: + type: string + projectLeaderName: + type: string + projectRoleId: + type: string + projectRoleName: + type: string + updatedAt: type: string - required: - - status - - taskId type: object - domain.UpdateCloudAccountRequest: + github_com_openinfradev_tks-api_pkg_domain.ProjectRoleResponse: properties: + createdAt: + type: string + deletedAt: + type: string description: type: string + id: + type: string + name: + description: project-leader, project-member, project-viewer + type: string + updatedAt: + type: string type: object - domain.UpdateMyProfileRequest: + github_com_openinfradev_tks-api_pkg_domain.RegoCompieError: properties: - department: - maxLength: 50 - minLength: 0 + code: + example: P_INVALID_REGO_SYNTAX type: string - email: + message: + example: Invalid rego syntax type: string - name: - maxLength: 30 - minLength: 1 + status: + example: 400 + type: integer + text: + example: 'Rego 문법 에러입니다. 라인:2 컬럼:1 에러메시지: var testnum is not safe' type: string - password: + type: object + github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest: + properties: + libs: + items: + type: string + type: array + rego: + example: Rego 코드 type: string required: - - password + - rego type: object - domain.UpdateMyProfileResponse: + github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse: properties: - user: - properties: - accountId: - type: string - department: - type: string - email: - type: string - id: - type: string - name: - type: string - organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' - type: object + errors: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError' + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array type: object - domain.UpdateOrganizationRequest: + github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest: properties: - description: - maxLength: 100 - minLength: 0 - type: string - name: - maxLength: 30 - minLength: 1 - type: string - phone: + stackTemplateIds: + items: + type: string + type: array + required: + - stackTemplateIds + type: object + github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest: + properties: + systemNotificationTemplateIds: + items: + type: string + type: array + required: + - systemNotificationTemplateIds + type: object + github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest: + properties: + projectMembers: + items: + properties: + projectMemberId: + type: string + type: object + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest: + properties: + users: + items: + type: string + type: array + required: + - users + type: object + github_com_openinfradev_tks-api_pkg_domain.ResourceQuota: + properties: + quotas: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.ResourceQuotaAttr: + properties: + quota: + type: integer + required: + type: integer + type: type: string - primaryClusterId: + usage: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest: + properties: + taskId: type: string - required: - - name type: object - domain.UpdateOrganizationResponse: + github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest: + properties: + policies: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse: properties: + awsAccountId: + type: string + cloudService: + type: string + clusters: + type: integer + createdIAM: + type: boolean description: type: string id: type: string name: type: string - phone: + organizationId: type: string type: object - domain.UpdatePasswordRequest: + github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse: properties: - newPassword: + id: type: string - originPassword: + name: + type: string + organizationId: type: string - required: - - newPassword - - originPassword type: object - domain.UpdatePrimaryClusterRequest: + github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse: properties: - primaryClusterId: + description: + type: string + id: + type: string + name: type: string type: object - domain.UpdateStackRequest: + github_com_openinfradev_tks-api_pkg_domain.SimplePolicyTemplateResponse: properties: description: + example: 이 정책은 ... + type: string + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + templateName: + example: 필수 Label 검사 + type: string + type: + enum: + - tks + - organization + example: tks + type: string + version: + example: v1.0.1 type: string type: object - domain.UpdateStackTemplateRequest: + github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse: properties: - description: + id: + type: string + name: type: string type: object - domain.UpdateUserRequest: + github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse: properties: - department: - maxLength: 50 - minLength: 0 + cloudService: type: string description: - maxLength: 100 - minLength: 0 type: string - email: + id: + type: string + kubeType: + type: string + kubeVersion: type: string name: - maxLength: 30 - minLength: 1 type: string - role: - enum: - - admin - - user + services: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse' + type: array + template: type: string type: object - domain.UpdateUserResponse: + github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateServiceResponse: properties: - user: - properties: - accountId: - type: string - createdAt: - type: string - department: - type: string - description: - type: string - email: - type: string - id: - type: string - name: - type: string - organization: - $ref: '#/definitions/domain.Organization' - role: - $ref: '#/definitions/domain.Role' - updatedAt: - type: string - type: object + type: + type: string type: object - domain.User: + github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse: properties: - accountId: - type: string createdAt: type: string - creator: - type: string - department: - type: string description: type: string - email: - type: string id: type: string name: type: string - organization: - $ref: '#/definitions/domain.Organization' - password: - type: string - passwordExpired: - type: boolean - passwordUpdatedAt: - type: string - role: - $ref: '#/definitions/domain.Role' - token: - type: string updatedAt: type: string type: object - domain.VerifyIdentityForLostIdRequest: + github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse: properties: + accountId: + type: string + department: + type: string email: type: string - organizationId: + id: type: string - userName: + name: type: string - required: - - email - - organizationId - - userName type: object - domain.VerifyIdentityForLostIdResponse: + github_com_openinfradev_tks-api_pkg_domain.StackConfResponse: properties: - validityPeriod: + tksCpNode: + type: integer + tksCpNodeMax: + type: integer + tksCpNodeType: type: string - type: object - domain.VerifyIdentityForLostPasswordRequest: - properties: - accountId: + tksInfraNode: + maximum: 3 + minimum: 1 + type: integer + tksInfraNodeMax: + type: integer + tksInfraNodeType: type: string - email: + tksUserNode: + maximum: 100 + minimum: 0 + type: integer + tksUserNodeMax: + type: integer + tksUserNodeType: + type: string + required: + - tksInfraNode + - tksUserNode + type: object + github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics: + properties: + outofdatePolicyCount: + type: integer + outofdateTemplateCount: + type: integer + totalPolicyCount: + type: integer + totalTemplateCount: + type: integer + uptodatePolicyCount: + type: integer + uptodateTemplateCount: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatusResponse: + properties: + policyDescription: + example: org 레이블 설정 여부 검사 + type: string + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + policyMandatory: + type: boolean + policyName: + example: org 레이블 요구 + type: string + templateCurrentVersion: + example: v1.0.1 + type: string + templateDescription: + example: 파라미터로 설정된 레이블 검사 + type: string + templateId: + example: 708d1e5b-4e6f-40e9-87a3-329e2fd051a5 + type: string + templateLatestVersion: + example: v1.0.3 + type: string + templateName: + example: 레이블 요구 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.StackResponse: + properties: + appServeAppCnt: + type: integer + cloudAccount: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleCloudAccountResponse' + conf: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackConfResponse' + createdAt: + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + favorited: + type: boolean + grafanaUrl: + type: string + id: + type: string + name: + type: string + organizationId: + type: string + primaryCluster: + type: boolean + resource: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse' + stackTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleStackTemplateResponse' + status: + type: string + statusDesc: + type: string + updatedAt: + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + userClusterEndpoint: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.StackStepStatus: + properties: + maxStep: + type: integer + stage: + type: string + status: + type: string + step: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.StackTemplateResponse: + properties: + cloudService: + type: string + createdAt: + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + id: + type: string + kubeType: + type: string + kubeVersion: + type: string + name: + type: string + organizations: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse' + type: array + platform: + type: string + services: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse' + type: array + template: + type: string + templateType: + type: string + updatedAt: + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + version: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse: + properties: + description: + type: string + name: + type: string + version: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceResponse: + properties: + applications: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackTemplateServiceApplicationResponse' + type: array + type: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.StageResponse: + properties: + actions: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ActionResponse' + type: array + name: + description: BUILD (빌드), DEPLOY (배포), PROMOTE (프로모트), ROLLBACK (롤백) + type: string + result: + type: string + status: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse: + properties: + content: + type: string + createdAt: + type: string + id: + type: string + status: + type: string + systemNotificationId: + type: string + taker: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse: + properties: + duration: + type: string + enableEmail: + type: boolean + enablePortal: + type: boolean + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: + type: string + systemNotificationRuleId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse: + properties: + key: + type: string + order: + type: integer + value: + type: string + required: + - key + - order + - value + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter: + properties: + operator: + type: string + order: + type: integer + value: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationResponse: + properties: + closedAt: + type: string + cluster: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleClusterResponse' + createdAt: + type: string + firedAt: + type: string + grafanaUrl: + type: string + id: + type: string + lastTaker: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + messageActionProposal: + type: string + messageContent: + type: string + messageTitle: + type: string + name: + type: string + node: + type: string + notificationType: type: string organizationId: type: string - userName: + policyName: + type: string + processingSec: + type: integer + rawData: + type: string + read: + type: boolean + severity: + type: string + status: + type: string + systemNotificationActions: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationActionResponse' + type: array + takedAt: + type: string + takedSec: + type: integer + updatedAt: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationRuleResponse: + properties: + createdAt: + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + id: + type: string + isSystem: + type: boolean + messageActionProposal: + type: string + messageContent: + type: string + messageTitle: + type: string + name: + type: string + notificationType: + type: string + status: + type: string + systemNotificationCondition: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationConditionResponse' + systemNotificationTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleSystemNotificationTemplateResponse' + targetUsers: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: array + updatedAt: + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain.SystemNotificationTemplateResponse: + properties: + createdAt: + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + id: + type: string + isSystem: + type: boolean + metricParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse' + type: array + metricQuery: + type: string + name: + type: string + notificationType: + type: string + organizations: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse' + type: array + updatedAt: + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + required: + - metricQuery + type: object + github_com_openinfradev_tks-api_pkg_domain.TemplateCount: + properties: + organizationTemplate: + type: integer + tksTemplate: + type: integer + total: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain.TemplateResponse: + properties: + children: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.TemplateResponse' + type: array + isAllowed: + type: boolean + key: + type: string + name: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.Unit: + properties: + data: + items: + type: string + type: array + name: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UnitNumber: + properties: + data: + items: + type: integer + type: array + name: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest: + properties: + endpointUrl: + type: string + helmRevision: + type: integer + previewEndpointUrl: + type: string + taskId: + type: string + required: + - taskId + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest: + properties: + abort: + type: boolean + appConfig: + type: string + appSecret: + type: string + artifactUrl: + type: string + executablePath: + type: string + extraEnv: + type: string + imageUrl: + type: string + port: + type: string + profile: + type: string + promote: + description: Update Strategy + type: boolean + resourceSpec: + type: string + strategy: + description: Task + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest: + properties: + output: + type: string + status: + type: string + taskId: + type: string + required: + - status + - taskId + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest: + properties: + description: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest: + properties: + groupName: + type: string + sizeX: + type: integer + sizeY: + type: integer + widgets: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.WidgetResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest: + properties: + department: + maxLength: 50 + minLength: 0 + type: string + email: + type: string + name: + maxLength: 30 + minLength: 1 + type: string + password: + type: string + required: + - email + - name + - password + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse: + properties: + user: + properties: + accountId: + type: string + department: + type: string + email: + type: string + id: + type: string + name: + type: string + organization: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array + type: object + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest: + properties: + adminAccountId: + type: string + description: + maxLength: 100 + minLength: 0 + type: string + name: + maxLength: 30 + minLength: 1 + type: string + required: + - name + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse: + properties: + id: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest: + properties: + newPassword: + type: string + originPassword: + type: string + required: + - newPassword + - originPassword + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest: + properties: + ID: + type: string + isAllowed: + enum: + - true + - false + type: boolean + required: + - ID + - isAllowed + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest: + properties: + permissions: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionUpdateRequest' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest: + properties: + currentTargetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + newTargetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest: + properties: + description: + type: string + enforcementAction: + enum: + - deny + - dryrun + - warn + example: deny + type: string + mandatory: + type: boolean + match: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Match' + matchYaml: + type: string + parameters: + example: '{"labels":{"key":"owner","allowedRegex":"test*"}' + type: string + policyName: + example: label 정책 + type: string + targetClusterIds: + example: + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + - 83bf8081-f0c5-4b31-826d-23f6f366ec90 + items: + type: string + type: array + templateId: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest: + properties: + deprecated: + example: false + type: boolean + description: + type: string + permittedOrganizationIds: + items: + type: string + type: array + severity: + enum: + - low + - medium + - high + example: medium + type: string + templateName: + example: 필수 Label 검사 + type: string + required: + - templateName + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest: + properties: + primaryClusterId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest: + properties: + projectRoleId: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest: + properties: + projectMembers: + items: + properties: + projectMemberId: + type: string + projectRoleId: + type: string + type: object + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest: + properties: + description: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest: + properties: + description: + type: string + name: + type: string + projectLeaderId: + type: string + required: + - name + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest: + properties: + templateCurrentVersion: + example: v1.0.1 + type: string + templateTargetVerson: + example: v1.0.3 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest: + properties: + description: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest: + properties: + organizationIds: + items: + type: string + type: array + required: + - organizationIds + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest: + properties: + cloudService: + enum: + - AWS + - BYOH + type: string + description: + type: string + kubeType: + type: string + kubeVersion: + type: string + name: + type: string + organizationIds: + items: + type: string + type: array + platform: + type: string + serviceIds: + items: + type: string + type: array + template: + type: string + templateType: + enum: + - STANDARD + - MSA + type: string + version: + type: string + required: + - kubeType + - kubeVersion + - name + - organizationIds + - platform + - serviceIds + - template + - version + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest: + properties: + description: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest: + properties: + description: + type: string + messageActionProposal: + type: string + messageContent: + type: string + messageTitle: + type: string + name: + type: string + systemNotificationCondition: + properties: + duration: + type: string + enableEmail: + type: boolean + enablePortal: + type: boolean + parameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationParameter' + type: array + severity: + type: string + systemNotificationRuleId: + type: string + type: object + systemNotificationTemplateId: + type: string + targetUserIds: + items: + type: string + type: array + required: + - messageContent + - messageTitle + - name + - systemNotificationTemplateId + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest: + properties: + description: + type: string + metricParameters: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SystemNotificationMetricParameterResponse' + type: array + metricQuery: + type: string + name: + type: string + organizationIds: + items: + type: string + type: array + required: + - metricQuery + - name + - organizationIds + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest: + properties: + description: + maxLength: 100 + minLength: 0 + type: string + name: + maxLength: 100 + minLength: 0 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest: + properties: + department: + maxLength: 50 + minLength: 0 + type: string + description: + maxLength: 100 + minLength: 0 + type: string + email: + type: string + name: + type: string + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole' + type: array + required: + - email + - name + - roles + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse: + properties: + user: + properties: + accountId: + type: string + createdAt: + type: string + department: + type: string + description: + type: string + email: + type: string + id: + type: string + name: + type: string + organization: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleRoleResponse' + type: array + updatedAt: + type: string + type: object + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest: + properties: + users: + items: + properties: + accountId: + type: string + department: + maxLength: 50 + minLength: 0 + type: string + description: + maxLength: 100 + minLength: 0 + type: string + email: + type: string + name: + type: string + roles: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UserCreationRole' + type: array + required: + - accountId + - email + - name + - roles + type: object + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain.UpdatedPolicyTemplateParameter: + properties: + name: + type: string + type: + type: string + value: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.UserCreationRole: + properties: + id: + type: string + required: + - id + type: object + github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest: + properties: + email: + type: string + organizationId: + type: string + userName: + type: string + required: + - email + - organizationId + - userName + type: object + github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse: + properties: + validityPeriod: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest: + properties: + accountId: + type: string + email: + type: string + organizationId: + type: string + userName: + type: string + required: + - accountId + - email + - organizationId + - userName + type: object + github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse: + properties: + validityPeriod: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.WidgetResponse: + properties: + sizeX: + type: integer + sizeY: + type: integer + startX: + type: integer + startY: + type: integer + widgetKey: + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.WorkloadData: + properties: + name: + type: string + value: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest: + properties: + policyTemplateIds: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse: + properties: + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest: + properties: + deprecated: + example: false + type: boolean + description: + example: 이 정책은 ... + type: string + kind: + example: K8sRequiredLabels + type: string + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + permittedOrganizationIds: + items: + type: string + type: array + rego: + example: rego 코드 + type: string + severity: + enum: + - low + - medium + - high + example: medium + type: string + templateName: + example: 필수 Label 검사 + type: string + required: + - kind + - rego + - severity + - templateName + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest: + properties: + currentVersion: + example: v1.0.0 + type: string + expectedVersion: + example: v1.1.0 + type: string + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + rego: + example: rego 코드 + type: string + versionUpType: + enum: + - major + - minor + - patch + example: minor + type: string + required: + - currentVersion + - expectedVersion + - rego + - versionUpType + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse: + properties: + version: + example: v1.1.1 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest: + properties: + policyTemplateIds: + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse: + properties: + existed: + type: boolean + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest: + properties: + libs: + items: + type: string + type: array + rego: + example: Rego 코드 + type: string + required: + - rego + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse: + properties: + errors: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompieError' + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse: + properties: + deployVersion: + additionalProperties: + type: string + type: object + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse: + properties: + policyTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse: + properties: + policyTemplate: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse' + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse: + properties: + pagination: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse' + policyTemplates: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse: + properties: + policyTemplateStatistics: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics' + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse: + properties: + versions: + example: + - v1.1.0 + - v1.0.1 + - v1.0.0 + items: + type: string + type: array + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateResponse: + properties: + createdAt: + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + deprecated: + example: false + type: boolean + description: + example: 이 정책은 ... + type: string + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + kind: + example: K8sRequiredLabels + type: string + libs: + example: + - rego 코드 + items: + type: string + type: array + parametersSchema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ParameterDef' + type: array + permittedOrganizations: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleOrganizationResponse' + type: array + rego: + example: rego 코드 + type: string + severity: + enum: + - low + - medium + - high + example: medium + type: string + templateName: + example: 필수 Label 검사 + type: string + type: + enum: + - tks + - organization + example: tks + type: string + updatedAt: + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + version: + example: v1.0.1 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.PolicyTemplateStatistics: + properties: + organizationId: + type: string + organizationName: + type: string + usageCount: + type: integer + type: object + github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest: + properties: + deprecated: + example: false + type: boolean + description: + type: string + permittedOrganizationIds: + items: + type: string + type: array + severity: + enum: + - low + - medium + - high + example: medium + type: string + templateName: + example: 필수 Label 검사 + type: string + required: + - templateName + type: object + github_com_openinfradev_tks-api_pkg_httpErrors.RestError: + properties: + code: + type: string + message: + type: string + status: + type: integer + text: + type: string + type: object +host: tks-api-dev.taco-cat.xyz +info: + contact: + email: taekyu.kang@sk.com + name: taekyu.kang@sk.com + description: This is backend api service for tks platform + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: tks-api service + version: "1.0" +paths: + /admin/audits: + get: + consumes: + - application/json + description: Get Audits + parameters: + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filter + type: array + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: or + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditsResponse' + security: + - JWT: [] + summary: Get Audits + tags: + - Audits + post: + consumes: + - application/json + description: Create Audit + parameters: + - description: create audit request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAuditResponse' + security: + - JWT: [] + summary: Create Audit + tags: + - Audits + /admin/audits/{auditId}: + delete: + consumes: + - application/json + description: Delete Audit + parameters: + - description: auditId + in: path + name: auditId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete Audit 'NOT IMPLEMENTED' + tags: + - Audits + get: + consumes: + - application/json + description: Get Audit + parameters: + - description: auditId + in: path + name: auditId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAuditResponse' + security: + - JWT: [] + summary: Get Audit + tags: + - Audits + /admin/organizations: + post: + consumes: + - application/json + description: Create organization in Admin portal + parameters: + - description: create organization request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateOrganizationRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: object + security: + - JWT: [] + summary: Create organization in Admin portal + tags: + - Organizations + /admin/organizations/{organizationId}/policyTemplates: + post: + consumes: + - application/json + description: 특정 조직에 대해 허용된 tks 템플릿 목록을 추가한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: update pemitted policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.AddPermittedPolicyTemplatesForOrganizationRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[Admin_AddPermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks + 템플릿 목록 추가' + tags: + - PolicyTemplate + put: + consumes: + - application/json + description: 특정 조직에 대해 허용된 tks 템플릿의 허용 상태를 해제(제거)한다. tks 우형 템플릿에 대해서만 동작하고 organization + 유형 템플릿에 대해서는 거부된다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: delete pemitted policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.DeletePermittedPolicyTemplatesForOrganizationRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[Admin_DeletePermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 + tks 템플릿 목록 제거' + tags: + - PolicyTemplate + /admin/organizations/{organizationId}/projects: + get: + consumes: + - application/json + description: Get projects as admin + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project Name + in: query + name: projectName + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse' + security: + - JWT: [] + summary: Get projects as admin + tags: + - Projects + /admin/organizations/{organizationId}/roles: + get: + description: Admin List Tks Roles + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse' + security: + - JWT: [] + summary: Admin List Tks Roles + tags: + - Roles + /admin/organizations/{organizationId}/roles/{roleId}: + get: + description: Admin Get Tks Role + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Role ID + in: path + name: roleId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse' + security: + - JWT: [] + summary: Admin Get Tks Role + tags: + - Roles + /admin/organizations/{organizationId}/users: + post: + consumes: + - application/json + description: Create user by admin in Admin Portal + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: create user request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserRequest' + produces: + - application/json + responses: + "200": + description: create user response + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_CreateUserResponse' + security: + - JWT: [] + summary: Create user by admin in Admin Portal + tags: + - Users + /admin/organizations/{organizationId}/users/{accountId}: + delete: + consumes: + - application/json + description: Delete user by admin in Admin Portal + parameters: + - description: input + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserRequest' + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: accountId + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete user by admin in Admin Portal + tags: + - Users + put: + consumes: + - application/json + description: Update user by admin in Admin Portal + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: accountId + in: path + name: accountId + required: true + type: string + - description: input + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.Admin_UpdateUserResponse' + security: + - JWT: [] + summary: Update user by admin in Admin Portal + tags: + - Users + /admin/policy-templates: + get: + consumes: + - application/json + description: 정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다. + parameters: + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: sortColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateResponse' + security: + - JWT: [] + summary: '[Admin_ListPolicyTemplate] 정책 템플릿 목록 조회' + tags: + - PolicyTemplate + post: + consumes: + - application/json + description: 정책 템플릿을 신규 생성(v1.0.0을 생성)한다. + parameters: + - description: create policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateReponse' + security: + - JWT: [] + summary: '[Admin_CreatePolicyTemplate] 정책 템플릿 신규 생성' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}: + delete: + consumes: + - application/json + description: 정책 템플릿을 삭제한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[Admin_DeletePolicyTemplate] 정책 템플릿 삭제' + tags: + - PolicyTemplate + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateResponse' + security: + - JWT: [] + summary: '[Admin_GetPolicyTemplate] 정책 템플릿 조회(최신 버전)' + tags: + - PolicyTemplate + patch: + consumes: + - application/json + description: 정책 템플릿의 업데이트 가능한 필드들을 업데이트한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: update policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.UpdatePolicyTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[Admin_UpdatePolicyTemplate] 정책 템플릿 업데이트' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}/deploy: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateDeployResponse' + security: + - JWT: [] + summary: '[Admin_GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}/statistics: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 + tks로 설정한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateStatisticsResponse' + security: + - JWT: [] + summary: '[Admin_ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}/versions: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ListPolicyTemplateVersionsResponse' + security: + - JWT: [] + summary: '[Admin_ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회' + tags: + - PolicyTemplate + post: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: create policy template version request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.CreatePolicyTemplateVersionResponse' + security: + - JWT: [] + summary: '[Admin_CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}/versions/{version}: + delete: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 삭제할 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[Admin_DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제' + tags: + - PolicyTemplate + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 조회할 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.GetPolicyTemplateVersionResponse' + security: + - JWT: [] + summary: '[Admin_GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회' + tags: + - PolicyTemplate + /admin/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters: + post: + consumes: + - application/json + description: 정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다. + parameters: + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + - description: Rego 코드 + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExtractParametersResponse' + security: + - JWT: [] + summary: '[Admin_ExtractParameters] 정책 템플릿 파라미터 추출' + tags: + - PolicyTemplate + /admin/policy-templates/kind/{policyTemplateKind}/existence: + get: + consumes: + - application/json + description: 해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다. + parameters: + - description: 정책 템플릿 이름 + in: path + name: policyTemplateKind + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateKindResponse' + security: + - JWT: [] + summary: '[Admin_ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인' + tags: + - PolicyTemplate + /admin/policy-templates/name/{policyTemplateName}/existence: + get: + consumes: + - application/json + description: 해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다. + parameters: + - description: 정책 템플릿 이름 + in: path + name: policyTemplateName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain_admin.ExistsPolicyTemplateNameResponse' + security: + - JWT: [] + summary: '[Admin_ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인' + tags: + - PolicyTemplate + /admin/stack-templates: + get: + consumes: + - application/json + description: Get StackTemplates + parameters: + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse' + security: + - JWT: [] + summary: Get StackTemplates + tags: + - StackTemplates + post: + consumes: + - application/json + description: Create StackTemplate + parameters: + - description: create stack template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackTemplateResponse' + security: + - JWT: [] + summary: Create StackTemplate + tags: + - StackTemplates + /admin/stack-templates/{stackTemplateId}: + delete: + consumes: + - application/json + description: Delete StackTemplate + parameters: + - description: stackTemplateId + in: path + name: stackTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete StackTemplate + tags: + - StackTemplates + get: + consumes: + - application/json + description: Get StackTemplate + parameters: + - description: stackTemplateId + in: path + name: stackTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse' + security: + - JWT: [] + summary: Get StackTemplate + tags: + - StackTemplates + put: + consumes: + - application/json + description: Update StackTemplate + parameters: + - description: Update stack template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update StackTemplate + tags: + - StackTemplates + /admin/stack-templates/{stackTemplateId}/organizations: + put: + consumes: + - application/json + description: Update StackTemplate organizations + parameters: + - description: Update stack template organizations request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackTemplateOrganizationsRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update StackTemplate organizations + tags: + - StackTemplates + /admin/stack-templates/name/{name}/existence: + get: + consumes: + - application/json + description: Check name for stackTemplate + parameters: + - description: name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckStackTemplateNameResponse' + security: + - JWT: [] + summary: Check name for stackTemplate + tags: + - StackTemplates + /admin/stack-templates/services: + get: + consumes: + - application/json + description: Get GetStackTemplateServices + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateServicesResponse' + security: + - JWT: [] + summary: Get GetStackTemplateServices + tags: + - StackTemplates + /admin/stack-templates/template-ids: + get: + consumes: + - application/json + description: Get GetStackTemplateTemplateIds + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateTemplateIdsResponse' + security: + - JWT: [] + summary: Get GetStackTemplateTemplateIds + tags: + - StackTemplates + /admin/system-notification-templates: + get: + consumes: + - application/json + description: Get SystemNotificationTemplates + parameters: + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse' + security: + - JWT: [] + summary: Get SystemNotificationTemplates + tags: + - SystemNotificationTemplates + post: + consumes: + - application/json + description: Create alert template. ADMIN ONLY + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationTemplateRequest' + security: + - JWT: [] + summary: Create alert template. ADMIN ONLY + tags: + - SystemNotificationTemplates + /admin/system-notification-templates/{systemNotificationTemplateId}: + delete: + consumes: + - application/json + description: Delete SystemNotificationTemplate + parameters: + - description: systemNotificationTemplateId + in: path + name: systemNotificationTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete SystemNotificationTemplate + tags: + - SystemNotificationTemplates + get: + consumes: + - application/json + description: Get SystemNotificationTemplate + parameters: + - description: systemNotificationTemplateId + in: path + name: systemNotificationTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse' + security: + - JWT: [] + summary: Get SystemNotificationTemplate + tags: + - SystemNotificationTemplates + put: + consumes: + - application/json + description: Update SystemNotificationTemplate + parameters: + - description: systemNotificationTemplateId + in: path + name: systemNotificationTemplateId + required: true + type: string + - description: Update alert template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update SystemNotificationTemplate + tags: + - SystemNotificationTemplates + /admin/system-notification-templates/name/{name}/existence: + get: + consumes: + - application/json + description: Check name for systemNotificationTemplate + parameters: + - description: name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificaionTemplateNameResponse' + security: + - JWT: [] + summary: Check name for systemNotificationTemplate + tags: + - SystemNotificationTemplates + /app-groups: + delete: + consumes: + - application/json + description: Uninstall appGroup + parameters: + - description: body + in: body + name: object + required: true + schema: + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Uninstall appGroup + tags: + - AppGroups + get: + consumes: + - application/json + description: Get appGroup list by giving params + parameters: + - description: clusterId + in: query + name: clusterId + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupsResponse' + security: + - JWT: [] + summary: Get appGroup list + tags: + - AppGroups + post: + consumes: + - application/json + description: Install appGroup + parameters: + - description: create appgroup request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppGroupResponse' + security: + - JWT: [] + summary: Install appGroup + tags: + - AppGroups + /app-groups/{appGroupId}: + get: + consumes: + - application/json + description: Get appGroup detail by appGroupId + parameters: + - description: appGroupId + in: path + name: appGroupId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppGroupResponse' + security: + - JWT: [] + summary: Get appGroup detail + tags: + - AppGroups + /app-groups/{appGroupId}/applications: + get: + consumes: + - application/json + description: Get applications + parameters: + - description: appGroupId + in: path + name: appGroupId + required: true + type: string + - description: applicationType + in: query + name: applicationType + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetApplicationsResponse' + security: + - JWT: [] + summary: Get applications + tags: + - AppGroups + post: + consumes: + - application/json + description: Create application + parameters: + - description: body + in: body + name: object + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateApplicationRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Create application + tags: + - AppGroups + /auth/find-id/code: + post: + consumes: + - application/json + description: This API allows users to verify their identity for lost id by submitting + required information + parameters: + - description: Request body for verifying identity for lost id including {organization + ID, email, username} + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostIdResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError' + summary: Request to verify identity for lost id + tags: + - Auth + /auth/find-id/verification: + post: + consumes: + - application/json + description: This API allows users to find their account ID by submitting required + information + parameters: + - description: Request body for finding the account ID including {organization + ID, email, username, 6 digit code} + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindIdResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError' + summary: Request to find forgotten ID + tags: + - Auth + /auth/find-password/code: + post: + consumes: + - application/json + description: This API allows users to verify their identity for lost password + by submitting required information + parameters: + - description: Request body for verifying identity for lost password including + {organization ID, email, username, Account ID} + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.VerifyIdentityForLostPasswordResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError' + summary: Request to verify identity for lost password + tags: + - Auth + /auth/find-password/verification: + post: + consumes: + - application/json + description: This API allows users to reset their forgotten password by submitting + required information + parameters: + - description: Request body for finding the password including {organization + ID, email, username, Account ID, 6 digit code} + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.FindPasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError' + summary: Request to find forgotten password + tags: + - Auth + /auth/login: + post: + consumes: + - application/json + description: login + parameters: + - description: account info + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginRequest' + produces: + - application/json + responses: + "200": + description: user detail + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.LoginResponse' + summary: login + tags: + - Auth + /auth/logout: + post: + consumes: + - application/json + description: logout + produces: + - application/json + responses: {} + security: + - JWT: [] + summary: logout + tags: + - Auth + /clusters: + get: + consumes: + - application/json + description: Get cluster list + parameters: + - description: organizationId + in: query + name: organizationId + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClustersResponse' + security: + - JWT: [] + summary: Get clusters + tags: + - Clusters + post: + consumes: + - application/json + description: Create cluster + parameters: + - description: create cluster request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateClusterResponse' + security: + - JWT: [] + summary: Create cluster + tags: + - Clusters + /clusters/{clusterId}: + delete: + consumes: + - application/json + description: Delete cluster + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete cluster + tags: + - Clusters + get: + consumes: + - application/json + description: Get cluster detail + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterResponse' + security: + - JWT: [] + summary: Get cluster + tags: + - Clusters + /clusters/{clusterId}/bootstrap-kubeconfig: + get: + consumes: + - application/json + description: Get bootstrap kubeconfig for BYOH + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetBootstrapKubeconfigResponse' + security: + - JWT: [] + summary: Get bootstrap kubeconfig for BYOH + tags: + - Clusters + post: + consumes: + - application/json + description: Create bootstrap kubeconfig for BYOH + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateBootstrapKubeconfigResponse' + security: + - JWT: [] + summary: Create bootstrap kubeconfig for BYOH + tags: + - Clusters + /clusters/{clusterId}/install: + post: + consumes: + - application/json + description: Install cluster on tks cluster + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Install cluster on tks cluster + tags: + - Clusters + /clusters/{clusterId}/nodes: + get: + consumes: + - application/json + description: Get nodes information for BYOH + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetClusterNodesResponse' + security: + - JWT: [] + summary: Get nodes information for BYOH + tags: + - Clusters + /clusters/{clusterId}/site-values: + get: + consumes: + - application/json + description: Get cluster site values for creating + parameters: + - description: clusterId + in: path + name: clusterId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ClusterSiteValuesResponse' + security: + - JWT: [] + summary: Get cluster site values for creating + tags: + - Clusters + /clusters/import: + post: + consumes: + - application/json + description: Import cluster + parameters: + - description: import cluster request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ImportClusterResponse' + security: + - JWT: [] + summary: Import cluster + tags: + - Clusters + /organizations: + get: + consumes: + - application/json + description: Get organization list + parameters: + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListOrganizationResponse' + type: array + security: + - JWT: [] + summary: Get organization list + tags: + - Organizations + /organizations/{organizationId}: + delete: + consumes: + - application/json + description: Delete organization + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteOrganizationResponse' + security: + - JWT: [] + summary: Delete organization + tags: + - Organizations + get: + consumes: + - application/json + description: Get organization detail + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetOrganizationResponse' + security: + - JWT: [] + summary: Get organization detail + tags: + - Organizations + put: + consumes: + - application/json + description: Update organization detail + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: update organization request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateOrganizationResponse' + security: + - JWT: [] + summary: Update organization detail + tags: + - Organizations + /organizations/{organizationId}/cloud-accounts: + get: + consumes: + - application/json + description: Get CloudAccounts + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountsResponse' + security: + - JWT: [] + summary: Get CloudAccounts + tags: + - CloudAccounts + post: + consumes: + - application/json + description: Create CloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: create cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateCloudAccountResponse' + security: + - JWT: [] + summary: Create CloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/cloud-accounts/{cloudAccountId}: + delete: + consumes: + - application/json + description: Delete CloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: Delete cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteCloudAccountRequest' + - description: cloudAccountId + in: path + name: cloudAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete CloudAccount + tags: + - CloudAccounts + get: + consumes: + - application/json + description: Get CloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: cloudAccountId + in: path + name: cloudAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResponse' + security: + - JWT: [] + summary: Get CloudAccount + tags: + - CloudAccounts + put: + consumes: + - application/json + description: Update CloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: Update cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateCloudAccountRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update CloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error: + delete: + consumes: + - application/json + description: Delete Force CloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: cloudAccountId + in: path + name: cloudAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete Force CloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota: + get: + consumes: + - application/json + description: Get resource quota by cloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: cloudAccountId + in: path + name: cloudAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetCloudAccountResourceQuotaResponse' + security: + - JWT: [] + summary: Get resource quota by cloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence: + get: + consumes: + - application/json + description: Check awsAccountId for cloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: awsAccountId + in: path + name: awsAccountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountAwsAccountIdResponse' + security: + - JWT: [] + summary: Check awsAccountId for cloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/cloud-accounts/name/{name}/existence: + get: + consumes: + - application/json + description: Check name for cloudAccount + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckCloudAccountNameResponse' + security: + - JWT: [] + summary: Check name for cloudAccount + tags: + - CloudAccounts + /organizations/{organizationId}/dashboards: + post: + consumes: + - application/json + description: Create new dashboard + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Request body to create dashboard + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateDashboardResponse' + security: + - JWT: [] + summary: Create new dashboard + tags: + - Dashboards + /organizations/{organizationId}/dashboards/{dashboardKey}: + get: + consumes: + - application/json + description: Get dashboard + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Dashboard Key + in: path + name: dashboardKey + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResponse' + type: array + security: + - JWT: [] + summary: Get dashboard + tags: + - Dashboards + put: + consumes: + - application/json + description: Update dashboard + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Request body to update dashboard + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateDashboardRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonDashboardResponse' + security: + - JWT: [] + summary: Update dashboard + tags: + - Dashboards + /organizations/{organizationId}/dashboards/widgets/charts: + get: + consumes: + - application/json + description: Get charts data + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: chartType + in: query + name: chartType + type: string + - description: duration + in: query + name: duration + required: true + type: string + - description: interval + in: query + name: interval + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartsResponse' + security: + - JWT: [] + summary: Get charts data + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/charts/{chartType}: + get: + consumes: + - application/json + description: Get chart data + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: chartType + in: path + name: chartType + required: true + type: string + - description: duration + in: query + name: duration + required: true + type: string + - description: interval + in: query + name: interval + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardChartResponse' + security: + - JWT: [] + summary: Get chart data + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-enforcement: + get: + consumes: + - application/json + description: Get the number of policy enforcement + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyEnforcementResponse' + security: + - JWT: [] + summary: Get the number of policy enforcement + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-statistics: + get: + consumes: + - application/json + description: Get policy violation log + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatisticsResponse' + security: + - JWT: [] + summary: Get policy violation log + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-status: + get: + consumes: + - application/json + description: Get policy status + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyStatusResponse' + security: + - JWT: [] + summary: Get policy status + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-update: + get: + consumes: + - application/json + description: Get the number of policytemplates that need to be updated + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyUpdateResponse' + security: + - JWT: [] + summary: Get the number of policytemplates that need to be updated + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-violation: + get: + consumes: + - application/json + description: Get the number of policy violation + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: duration + in: query + name: duration + required: true + type: string + - description: interval + in: query + name: interval + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationResponse' + security: + - JWT: [] + summary: Get the number of policy violation + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-violation-log: + get: + consumes: + - application/json + description: Get policy violation log + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse' + security: + - JWT: [] + summary: Get policy violation log + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/policy-violation-top5: + get: + consumes: + - application/json + description: Get policy violation top5 + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: duration + in: query + name: duration + required: true + type: string + - description: interval + in: query + name: interval + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardPolicyViolationTop5Response' + security: + - JWT: [] + summary: Get policy violation top5 + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/resources: + get: + consumes: + - application/json + description: Get resources + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardResourcesResponse' + security: + - JWT: [] + summary: Get resources + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/stacks: + get: + consumes: + - application/json + description: Get stacks + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardStacksResponse' + security: + - JWT: [] + summary: Get stacks + tags: + - Dashboard Widgets + /organizations/{organizationId}/dashboards/widgets/workload: + get: + consumes: + - application/json + description: Get workloads + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetDashboardWorkloadResponse' + security: + - JWT: [] + summary: Get workloads + tags: + - Dashboard Widgets + /organizations/{organizationId}/mandatory-policies: + get: + consumes: + - application/json + description: 템플릿, 정책이 필수 인지 여부를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMandatoryPoliciesResponse' + security: + - JWT: [] + summary: '[GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회' + tags: + - Policy + patch: + consumes: + - application/json + description: 템플릿, 정책이 필수 인지 여부를 설정한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: update mandatory policy/policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SetMandatoryPoliciesRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정' + tags: + - Policy + /organizations/{organizationId}/my-profile: + delete: + consumes: + - application/json + description: Delete myProfile + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + security: + - JWT: [] + summary: Delete myProfile + tags: + - My-profile + get: + consumes: + - application/json + description: Get my profile detail + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse' + security: + - JWT: [] + summary: Get my profile detail + tags: + - My-profile + put: + consumes: + - application/json + description: Update my profile detail + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: 'Required fields: password due to double-check' + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateMyProfileResponse' + security: + - JWT: [] + summary: Update my profile detail + tags: + - My-profile + /organizations/{organizationId}/my-profile/next-password-change: + put: + consumes: + - application/json + description: Update user's password expired date to current date + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_httpErrors.RestError' + security: + - JWT: [] + summary: Update user's password expired date to current date + tags: + - My-profile + /organizations/{organizationId}/my-profile/password: + put: + consumes: + - application/json + description: Update user password detail + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: update user password request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePasswordRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update user password detail + tags: + - My-profile + /organizations/{organizationId}/policies: + get: + consumes: + - application/json + description: 정책 목록을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: filledParameter + in: query + name: filledParameter + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: sortColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse' + security: + - JWT: [] + summary: '[ListPolicy] 정책 목록 조회' + tags: + - Policy + post: + consumes: + - application/json + description: 새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. + 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: create policy request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse' + security: + - JWT: [] + summary: '[CreatePolicy] 정책 생성' + tags: + - Policy + /organizations/{organizationId}/policies/{policyId}: + delete: + consumes: + - application/json + description: 정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[DeletePolicy] 정책 삭제' + tags: + - Policy + get: + consumes: + - application/json + description: 정책 정보를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse' + security: + - JWT: [] + summary: '[GetPolicy] 정책 조회' + tags: + - Policy + patch: + consumes: + - application/json + description: 정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + - description: update policy set request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[UpdatePolicy] 정책을 업데이트' + tags: + - Policy + /organizations/{organizationId}/policies/{policyId}/clusters: + patch: + consumes: + - application/json + description: 정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 + 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + - description: update policy set request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정' + tags: + - Policy + /organizations/{organizationId}/policies/{policyId}/edit: + get: + consumes: + - application/json + description: 정책 정보를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 식별자(uuid) + in: path + name: policyId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyResponse' + security: + - JWT: [] + summary: '[GetPolicy] 정책 조회' + tags: + - Policy + /organizations/{organizationId}/policies/name/{policyName}/existence: + get: + consumes: + - application/json + description: 해당 이름을 가진 정책이 이미 존재하는지 확인한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 이름 + in: path + name: policyName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' + security: + - JWT: [] + summary: '[ExistsPolicyName] 정책 이름 존재 여부 확인' + tags: + - Policy + /organizations/{organizationId}/policies/resource-name/{policyResourceName}/existence: + get: + consumes: + - application/json + description: 해당 자원 이름을 가진 정책이 이미 존재하는지 확인한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 자원 이름(쿠버네티스 배포 시 자원 이름) + in: path + name: policyResourceName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' + security: + - JWT: [] + summary: '[ExistsPolicyResourceName] 정책 자원 이름 존재 여부 확인' + tags: + - Policy + /organizations/{organizationId}/policy-notifications: + get: + consumes: + - application/json + description: Get PolicyNotifications + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationsResponse' + security: + - JWT: [] + summary: Get PolicyNotifications + tags: + - PolicyNotifications + /organizations/{organizationId}/policy-notifications/{policyNotificationId}: + get: + consumes: + - application/json + description: Get PolicyNotification + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: policyNotificationId + in: path + name: policyNotificationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyNotificationResponse' + security: + - JWT: [] + summary: Get PolicyNotification + tags: + - PolicyNotifications + /organizations/{organizationId}/policy-statistics: + get: + consumes: + - application/json + description: 템플릿, 정책의 통계를 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyStatisticsResponse' + security: + - JWT: [] + summary: '[GetPolicyStatistics] 정책 템플릿, 정책 통계 조회' + tags: + - Policy + /organizations/{organizationId}/policy-templates: + get: + consumes: + - application/json + description: 정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다. + parameters: + - description: pageSize + in: query + name: limit + type: string + - description: pageNumber + in: query + name: page + type: string + - description: sortColumn + in: query + name: sortColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse' + security: + - JWT: [] + summary: '[ListPolicyTemplate] 정책 템플릿 목록 조회' + tags: + - PolicyTemplate + post: + consumes: + - application/json + description: 정책 템플릿을 신규 생성(v1.0.0을 생성)한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: create policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateReponse' + security: + - JWT: [] + summary: '[CreatePolicyTemplate] 정책 템플릿 신규 생성' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}: + delete: + consumes: + - application/json + description: 정책 템플릿을 삭제한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[DeletePolicyTemplate] 정책 템플릿 삭제' + tags: + - PolicyTemplate + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateResponse' + security: + - JWT: [] + summary: '[GetPolicyTemplate] 정책 템플릿 조회(최신 버전)' + tags: + - PolicyTemplate + patch: + consumes: + - application/json + description: 정책 템플릿의 업데이트 가능한 필드들을 업데이트한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: update policy template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[UpdatePolicyTemplate] 정책 템플릿 업데이트' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}/deploy: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateDeployResponse' + security: + - JWT: [] + summary: '[GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}/statistics: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 + tks로 설정한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateStatisticsResponse' + security: + - JWT: [] + summary: '[ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions: + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateVersionsResponse' + security: + - JWT: [] + summary: '[ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회' + tags: + - PolicyTemplate + post: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: create policy template version request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreatePolicyTemplateVersionResponse' + security: + - JWT: [] + summary: '[CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}: + delete: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 삭제할 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: '[DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제' + tags: + - PolicyTemplate + get: + consumes: + - application/json + description: 해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 조회할 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPolicyTemplateVersionResponse' + security: + - JWT: [] + summary: '[GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters: + post: + consumes: + - application/json + description: 정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId + required: true + type: string + - description: 버전(v0.0.0 형식) + in: path + name: version + required: true + type: string + - description: Rego 코드 + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExtractParametersResponse' + security: + - JWT: [] + summary: '[ExtractParameters] 정책 템플릿 파라미터 추출' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/kind/{policyTemplateKind}/existence: + get: + consumes: + - application/json + description: 해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 이름 + in: path + name: policyTemplateKind + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse' + security: + - JWT: [] + summary: '[ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인' + tags: + - PolicyTemplate + /organizations/{organizationId}/policy-templates/name/{policyTemplateName}/existence: + get: + consumes: + - application/json + description: 해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다. + parameters: + - description: 조직 식별자(o로 시작) + in: path + name: organizationId + required: true + type: string + - description: 정책 템플릿 이름 + in: path + name: policyTemplateName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateNameResponse' + security: + - JWT: [] + summary: '[ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인' + tags: + - PolicyTemplate + /organizations/{organizationId}/primary-cluster: + patch: + consumes: + - application/json + description: Update primary cluster + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: update primary cluster request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePrimaryClusterRequest' + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update primary cluster + tags: + - Organizations + /organizations/{organizationId}/projects: + get: + consumes: + - application/json + description: Get projects + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: (all | only) + in: query + name: query + type: string + - description: Project Name + in: query + name: projectName + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectsResponse' + security: + - JWT: [] + summary: Get projects + tags: + - Projects + post: + consumes: + - application/json + description: Create new project + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Request body to create project + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectResponse' + security: + - JWT: [] + summary: Create new project + tags: + - Projects + /organizations/{organizationId}/projects/{projectId}: + get: + consumes: + - application/json + description: Get projects + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectResponse' + security: + - JWT: [] + summary: Get projects + tags: + - Projects + put: + consumes: + - application/json + description: Update project + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Request body to update project + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Update project + tags: + - Projects + /organizations/{organizationId}/projects/{projectId}/app-serve-apps: + get: + consumes: + - application/json + description: Get appServeApp list by giving params + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Show all apps including deleted apps + in: query + name: showAll + type: boolean + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp' + type: array + security: + - JWT: [] + summary: Get appServeApp list + tags: + - AppServeApps + post: + consumes: + - application/json + description: Install appServeApp + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Request body to create app + in: body + name: object + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateAppServeAppRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - JWT: [] + summary: Install appServeApp + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}: + delete: + consumes: + - application/json + description: Uninstall appServeApp + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: App ID + in: path + name: appId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - JWT: [] + summary: Uninstall appServeApp + tags: + - AppServeApps + put: + consumes: + - application/json + description: Update appServeApp + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: App ID + in: path + name: appId + required: true + type: string + - description: Request body to update app + in: body + name: object + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - JWT: [] + summary: Update appServeApp + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/endpoint: + patch: + consumes: + - application/json + description: Update app endpoint + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: appId + in: path + name: appId + required: true + type: string + - description: Request body to update app endpoint + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppEndpointRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - JWT: [] + summary: Update app endpoint + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/exist: + get: + consumes: + - application/json + description: Get appServeApp by giving params + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: boolean + security: + - JWT: [] + summary: Get appServeApp + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/latest-task: + get: + consumes: + - application/json + description: Get latest task from appServeApp + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true type: string - required: - - accountId - - email - - organizationId - - userName - type: object - domain.VerifyIdentityForLostPasswordResponse: - properties: - validityPeriod: + - description: App ID + in: path + name: appId + required: true type: string - type: object - httpErrors.RestError: - properties: - code: + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse' + security: + - JWT: [] + summary: Get latest task from appServeApp + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/rollback: + post: + consumes: + - application/json + description: Rollback appServeApp + parameters: + - description: Organization ID + in: path + name: organizationId + required: true type: string - message: + - description: Project ID + in: path + name: projectId + required: true type: string - status: - type: integer - text: + - description: App ID + in: path + name: appId + required: true type: string - type: object -host: tks-api-ft.taco-cat.xyz -info: - contact: - email: taekyu.kang@sk.com - name: taekyu.kang@sk.com - description: This is backend api service for tks platform - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - title: tks-api service - version: "1.0" -paths: - /app-groups: - delete: + - description: Request body to rollback app + in: body + name: object + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RollbackAppServeAppRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + security: + - JWT: [] + summary: Rollback appServeApp + tags: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/status: + patch: consumes: - application/json - description: Uninstall appGroup + description: Update app status parameters: - - description: body + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: App ID + in: path + name: appId + required: true + type: string + - description: Request body to update app status in: body - name: object + name: body required: true schema: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateAppServeAppStatusRequest' produces: - application/json responses: "200": description: OK + schema: + type: string security: - JWT: [] - summary: Uninstall appGroup + summary: Update app status tags: - - AppGroups + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks: get: consumes: - application/json - description: Get appGroup list by giving params + description: Get appServeAppTask list by giving params parameters: - - description: clusterId - in: query - name: clusterId + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true type: string - description: pageSize in: query - name: limit + name: pageSize type: string - description: pageNumber in: query - name: page + name: pageNumber type: string - description: sortColumn in: query - name: soertColumn + name: sortColumn type: string - description: sortOrder in: query name: sortOrder type: string - - description: filters + - collectionFormat: csv + description: filters in: query items: type: string @@ -2058,44 +8310,71 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetAppGroupsResponse' + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_internal_model.AppServeApp' + type: array security: - JWT: [] - summary: Get appGroup list + summary: Get appServeAppTask list tags: - - AppGroups - post: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks/{taskId}: + get: consumes: - application/json - description: Install appGroup + description: Get task detail from appServeApp parameters: - - description: create appgroup request - in: body - name: body + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.CreateAppGroupRequest' + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: App ID + in: path + name: appId + required: true + type: string + - description: Task ID + in: path + name: taskId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.CreateAppGroupResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetAppServeAppTaskResponse' security: - JWT: [] - summary: Install appGroup + summary: Get task detail from appServeApp tags: - - AppGroups - /app-groups/{appGroupId}: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/count: get: consumes: - application/json - description: Get appGroup detail by appGroupId + description: Get number of apps on given stack parameters: - - description: appGroupId + - description: Organization ID in: path - name: appGroupId + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Stack ID + in: query + name: stackId required: true type: string produces: @@ -2104,26 +8383,31 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetAppGroupResponse' + type: integer security: - JWT: [] - summary: Get appGroup detail + summary: Get number of apps on given stack tags: - - AppGroups - /app-groups/{appGroupId}/applications: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/app-serve-apps/name/{name}/existence: get: consumes: - application/json - description: Get applications + description: Check duplicate appServeAppName by giving params parameters: - - description: appGroupId + - description: Organization ID in: path - name: appGroupId + name: organizationId required: true type: string - - description: applicationType - in: query - name: applicationType + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: name + in: path + name: name required: true type: string produces: @@ -2132,300 +8416,389 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetApplicationsResponse' + type: boolean security: - JWT: [] - summary: Get applications + summary: Check duplicate appServeAppName tags: - - AppGroups - post: + - AppServeApps + /organizations/{organizationId}/projects/{projectId}/kube-config: + get: consumes: - application/json - description: Create application + description: Get project kubeconfig parameters: - - description: body - in: body - name: object + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.CreateApplicationRequest' + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string produces: - application/json responses: "200": description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectKubeconfigResponse' security: - JWT: [] - summary: Create application + summary: Get project kubeconfig tags: - - AppGroups - /auth/find-id/code: - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/members: + delete: consumes: - application/json - description: This API allows users to verify their identity for lost id by submitting - required information + description: Remove project members to project parameters: - - description: Request body for verifying identity for lost id including {organization - ID, email, username} + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Request body to remove project member in: body - name: body + name: request required: true schema: - $ref: '#/definitions/domain.VerifyIdentityForLostIdRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveProjectMemberRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.VerifyIdentityForLostIdResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/httpErrors.RestError' - summary: Request to verify identity for lost id + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Remove project members to project tags: - - Auth - /auth/find-id/verification: - post: + - Projects + get: consumes: - application/json - description: This API allows users to find their account ID by submitting required - information + description: Get project members parameters: - - description: Request body for finding the account ID including {organization - ID, email, username, 6 digit code} - in: body - name: body + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId required: true - schema: - $ref: '#/definitions/domain.FindIdRequest' + type: string + - description: project member search by query (query=all), (query=leader), (query=member), + (query=viewer) + in: query + name: query + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.FindIdResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/httpErrors.RestError' - summary: Request to find forgotten ID + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMembersResponse' + security: + - JWT: [] + summary: Get project members tags: - - Auth - /auth/find-password/code: + - Projects post: consumes: - application/json - description: This API allows users to verify their identity for lost password - by submitting required information + description: Add project member to project parameters: - - description: Request body for verifying identity for lost password including - {organization ID, email, username, Account ID} + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Request body to add project member in: body - name: body + name: request required: true schema: - $ref: '#/definitions/domain.VerifyIdentityForLostPasswordRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddProjectMemberRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.VerifyIdentityForLostPasswordResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/httpErrors.RestError' - summary: Request to verify identity for lost password + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Add project member to project tags: - - Auth - /auth/find-password/verification: - post: + - Projects + put: consumes: - application/json - description: This API allows users to reset their forgotten password by submitting - required information + description: Update project member Role parameters: - - description: Request body for finding the password including {organization - ID, email, username, Account ID, 6 digit code} + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Request body to update project member role in: body - name: body + name: request required: true schema: - $ref: '#/definitions/domain.FindPasswordRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMembersRoleRequest' produces: - application/json responses: "200": description: OK - "400": - description: Bad Request schema: - $ref: '#/definitions/httpErrors.RestError' - summary: Request to find forgotten password + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Update project member Role tags: - - Auth - /auth/login: - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}: + delete: consumes: - application/json - description: login + description: Remove project members to project parameters: - - description: account info - in: body - name: body + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.LoginRequest' + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Project Member ID + in: path + name: projectMemberId + required: true + type: string produces: - application/json responses: "200": - description: user detail + description: OK schema: - $ref: '#/definitions/domain.LoginResponse' - summary: login + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Remove project members to project tags: - - Auth - /auth/logout: - post: + - Projects + get: consumes: - application/json - description: logout + description: Get project member + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Project Member ID + in: path + name: projectMemberId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.LogoutResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberResponse' security: - JWT: [] - summary: logout + summary: Get project member tags: - - Auth - /auth/ping: - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role: + put: consumes: - application/json - description: ping with token + description: Update project member Role parameters: - - description: token info + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Project Member ID + in: path + name: projectMemberId + required: true + type: string + - description: Request body to update project member role in: body - name: body + name: request required: true schema: - $ref: '#/definitions/domain.PingTokenRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectMemberRoleRequest' produces: - application/json responses: "200": description: OK - summary: ping with token + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' + security: + - JWT: [] + summary: Update project member Role tags: - - Auth - /clusters: + - Projects + /organizations/{organizationId}/projects/{projectId}/members/count: get: consumes: - application/json - description: Get cluster list + description: Get project member count group by project role parameters: - - description: organizationId - in: query + - description: Organization ID + in: path name: organizationId + required: true type: string - - description: pageSize - in: query - name: limit - type: string - - description: pageNumber - in: query - name: page - type: string - - description: sortColumn - in: query - name: soertColumn - type: string - - description: sortOrder - in: query - name: sortOrder + - description: Project ID + in: path + name: projectId + required: true type: string - - description: filters - in: query - items: - type: string - name: filters - type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetClustersResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectMemberCountResponse' security: - JWT: [] - summary: Get clusters + summary: Get project member count group by project role tags: - - Clusters - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces: + get: consumes: - application/json - description: Create cluster + description: Get project namespaces parameters: - - description: create cluster request - in: body - name: body + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.CreateClusterRequest' + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.CreateClusterResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespacesResponse' security: - JWT: [] - summary: Create cluster + summary: Get project namespaces tags: - - Clusters - /clusters/{clusterId}: - delete: + - Projects + post: consumes: - application/json - description: Delete cluster + description: Create project namespace parameters: - - description: clusterId + - description: Organization ID in: path - name: clusterId + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId required: true type: string + - description: Request body to create project namespace + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateProjectNamespaceRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.Cluster' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' security: - JWT: [] - summary: Delete cluster + summary: Create project namespace tags: - - Clusters - get: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}: + delete: consumes: - application/json - description: Get cluster detail + description: Delete project namespace parameters: - - description: clusterId + - description: Organization ID in: path - name: clusterId + 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: @@ -2434,54 +8807,116 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.Cluster' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' security: - JWT: [] - summary: Get cluster + summary: Delete project namespace tags: - - Clusters - /clusters/{clusterId}/bootstrap-kubeconfig: + - Projects get: consumes: - application/json - description: Get bootstrap kubeconfig for BYOH + description: Get 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: Project Namespace + in: path + name: projectNamespace + required: true + type: string + - description: Project Stack ID + in: path + name: stackId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetBootstrapKubeconfigResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResponse' security: - JWT: [] - summary: Get bootstrap kubeconfig for BYOH + summary: Get project namespace tags: - - Clusters - post: + - Projects + put: consumes: - application/json - description: Create bootstrap kubeconfig for BYOH + description: Update 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: Project Namespace + in: path + name: projectNamespace + required: true + type: string + - description: Project Stack ID + in: path + name: stackId + required: true + type: string + - description: Request body to update project namespace + in: body + name: request + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateProjectNamespaceRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.CreateBootstrapKubeconfigResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CommonProjectResponse' security: - JWT: [] - summary: Create bootstrap kubeconfig for BYOH + summary: Update project namespace tags: - - Clusters - /clusters/{clusterId}/install: - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence: + get: consumes: - application/json - description: Install cluster on tks cluster + description: Check project namespace exist parameters: - - description: clusterId + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Project ID + in: path + name: projectId + required: true + type: string + - description: Project Stack ID in: path - name: clusterId + name: stackId + required: true + type: string + - description: Project Namespace + in: path + name: projectNamespace required: true type: string produces: @@ -2489,20 +8924,37 @@ paths: responses: "200": description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] - summary: Install cluster on tks cluster + summary: Check project namespace exist tags: - - Clusters - /clusters/{clusterId}/nodes: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/k8s-resources: get: consumes: - application/json - description: Get nodes information for BYOH + description: Get k8s resources for project namespace parameters: - - description: clusterId + - description: Organization ID in: path - name: clusterId + 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: @@ -2511,21 +8963,36 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetClusterNodesResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceK8sResourcesResponse' security: - JWT: [] - summary: Get nodes information for BYOH + summary: Get k8s resources for project namespace tags: - - Clusters - /clusters/{clusterId}/site-values: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/kube-config: get: consumes: - application/json - description: Get cluster site values for creating + description: Get project namespace kubeconfig parameters: - - description: clusterId + - description: Organization ID in: path - name: clusterId + 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: @@ -2534,130 +9001,143 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.ClusterSiteValuesResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceKubeConfigResponse' security: - JWT: [] - summary: Get cluster site values for creating + summary: Get project namespace kubeconfig tags: - - Clusters - /clusters/import: - post: + - Projects + /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/resources-usage: + get: consumes: - application/json - description: Import cluster + description: Get resources usage for project namespace parameters: - - description: import cluster request - in: body - name: body + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.ImportClusterRequest' + 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.ImportClusterResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectNamespaceResourcesUsageResponse' security: - JWT: [] - summary: Import cluster + summary: Get resources usage for project namespace tags: - - Clusters - /organizations: + - Projects + /organizations/{organizationId}/projects/existence: get: consumes: - application/json - description: Get organization list + description: Check project name exist parameters: - - description: pageSize - in: query - name: limit - type: string - - description: pageNumber - in: query - name: page + - description: Organization ID + in: path + name: organizationId + required: true type: string - - description: sortColumn + - description: type (name) in: query - name: soertColumn + name: type type: string - - description: sortOrder + - description: value (project name) in: query - name: sortOrder + name: value + required: true type: string - - description: filters - in: query - items: - type: string - name: filters - type: array produces: - application/json responses: "200": description: OK schema: - items: - $ref: '#/definitions/domain.ListOrganizationBody' - type: array + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] - summary: Get organization list + summary: Check project name exist tags: - - Organizations - post: + - Projects + /organizations/{organizationId}/projects/project-roles: + get: consumes: - application/json - description: Create organization + description: Get project roles by giving params parameters: - - description: create organization request - in: body - name: body + - description: Organization ID + in: path + name: organizationId required: true - schema: - $ref: '#/definitions/domain.CreateOrganizationRequest' + type: string + - description: project role search by query (query=all), (query=leader), (query=member), + (query=viewer) + in: query + name: query + type: string produces: - application/json responses: "200": description: OK schema: - type: object + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRolesResponse' security: - JWT: [] - summary: Create organization + summary: Get project roles tags: - - Organizations - /organizations/{organizationId}: - delete: + - Projects + /organizations/{organizationId}/projects/project-roles/{projectRoleId}: + get: consumes: - application/json - description: Delete organization + description: Get project role by id parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string + - description: Project Role ID + in: path + name: projectRoleId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.Organization' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetProjectRoleResponse' security: - JWT: [] - summary: Delete organization + summary: Get project role tags: - - Organizations + - Projects + /organizations/{organizationId}/roles: get: - consumes: - - application/json - description: Get organization detail + description: List Tks Roles parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true @@ -2668,101 +9148,110 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetOrganizationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListTksRoleResponse' security: - JWT: [] - summary: Get organization detail + summary: List Tks Roles tags: - - Organizations - put: + - Roles + post: consumes: - application/json - description: Update organization detail + description: Create Tks Role parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string - - description: update organization request + - description: Create Tks Role Request in: body name: body required: true schema: - $ref: '#/definitions/domain.UpdateOrganizationRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.UpdateOrganizationResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateTksRoleResponse' security: - JWT: [] - summary: Update organization detail + summary: Create Tks Role tags: - - Organizations - /organizations/{organizationId}/alerts: - get: - consumes: - - application/json - description: Get Alerts + - Roles + /organizations/{organizationId}/roles/{roleId}: + delete: + description: Delete Tks Role parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string - - description: pageSize - in: query - name: limit - type: string - - description: pageNumber - in: query - name: page + - description: Role ID + in: path + name: roleId + required: true type: string - - description: sortColumn - in: query - name: soertColumn + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Delete Tks Role + tags: + - Roles + get: + description: Get Tks Role + parameters: + - description: Organization ID + in: path + name: organizationId + required: true type: string - - description: sortOrder - in: query - name: sortOrder + - description: Role ID + in: path + name: roleId + required: true type: string - - description: filters - in: query - items: - type: string - name: filters - type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetAlertsResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetTksRoleResponse' security: - JWT: [] - summary: Get Alerts + summary: Get Tks Role tags: - - Alerts - /organizations/{organizationId}/alerts/{alertId}: - delete: + - Roles + put: consumes: - application/json - description: Delete Alert + description: Update Tks Role parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string - - description: alertId + - description: Role ID in: path - name: alertId + name: roleId required: true type: string + - description: Update Tks Role Request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateTksRoleRequest' produces: - application/json responses: @@ -2770,22 +9259,21 @@ paths: description: OK security: - JWT: [] - summary: Delete Alert + summary: Update Tks Role tags: - - Alerts + - Roles + /organizations/{organizationId}/roles/{roleId}/permissions: get: - consumes: - - application/json - description: Get Alert + description: Get Permissions By Role ID parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string - - description: alertId + - description: Role ID in: path - name: alertId + name: roleId required: true type: string produces: @@ -2794,28 +9282,33 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetAlertResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionsByRoleIdResponse' security: - JWT: [] - summary: Get Alert + summary: Get Permissions By Role ID tags: - - Alerts + - Roles put: consumes: - application/json - description: Update Alert + description: Update Permissions By Role ID parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string - - description: Update cloud setting request + - description: Role ID + in: path + name: roleId + required: true + type: string + - description: Update Permissions By Role ID Request in: body name: body required: true schema: - $ref: '#/definitions/domain.UpdateAlertRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdatePermissionsByRoleIdRequest' produces: - application/json responses: @@ -2823,20 +9316,31 @@ paths: description: OK security: - JWT: [] - summary: Update Alert + summary: Update Permissions By Role ID tags: - - Alerts - /organizations/{organizationId}/alerts/{alertId}/actions: - post: + - Roles + /organizations/{organizationId}/roles/{roleId}/users: + delete: consumes: - application/json - description: Create alert action + description: Remove Users From Role parameters: - - description: organizationId + - description: Organization ID in: path name: organizationId required: true type: string + - description: Role ID + in: path + name: roleId + required: true + type: string + - description: Remove Users From Role Request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveUsersFromRoleRequest' produces: - application/json responses: @@ -2844,31 +9348,29 @@ paths: description: OK security: - JWT: [] - summary: Create alert action + summary: Remove Users From Role tags: - - Alerts - /organizations/{organizationId}/app-serve-apps: + - Roles get: - consumes: - - application/json - description: Get appServeApp list by giving params + description: Get Users By Role ID parameters: - description: Organization ID in: path name: organizationId required: true type: string - - description: Show all apps including deleted apps - in: query - name: showAll - type: boolean + - description: Role ID + in: path + name: roleId + required: true + type: string - description: pageSize in: query - name: limit + name: pageSize type: string - description: pageNumber in: query - name: page + name: pageNumber type: string - description: sortColumn in: query @@ -2878,7 +9380,8 @@ paths: in: query name: sortOrder type: string - - description: filters + - collectionFormat: csv + description: filters in: query items: type: string @@ -2890,56 +9393,55 @@ paths: "200": description: OK schema: - items: - $ref: '#/definitions/domain.AppServeApp' - type: array + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersInRoleIdResponse' security: - JWT: [] - summary: Get appServeApp list + summary: Get Users By Role ID tags: - - AppServeApps + - Roles post: consumes: - application/json - description: Install appServeApp + description: Append Users To Role parameters: - description: Organization ID in: path name: organizationId required: true type: string - - description: Request body to create app + - description: Role ID + in: path + name: roleId + required: true + type: string + - description: Append Users To Role Request in: body - name: object + name: body required: true schema: - $ref: '#/definitions/domain.CreateAppServeAppRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AppendUsersToRoleRequest' produces: - application/json responses: "200": description: OK - schema: - type: string security: - JWT: [] - summary: Install appServeApp + summary: Append Users To Role tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}: - delete: - consumes: - - application/json - description: Uninstall appServeApp + - Roles + /organizations/{organizationId}/roles/{roleName}/existence: + get: + description: Check whether the role name exists parameters: - description: Organization ID in: path name: organizationId required: true type: string - - description: App ID + - description: Role Name in: path - name: appId + name: roleName required: true type: string produces: @@ -2948,138 +9450,138 @@ paths: "200": description: OK schema: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckRoleNameResponse' security: - JWT: [] - summary: Uninstall appServeApp + summary: Check whether the role name exists tags: - - AppServeApps + - Roles + /organizations/{organizationId}/stack-templates: get: consumes: - application/json - description: Get appServeApp by giving params + description: Get Organization StackTemplates parameters: - - description: Organization ID - in: path - name: organizationId - required: true + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber type: string - - description: App ID - in: path - name: appId - required: true + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetAppServeAppResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplatesResponse' security: - JWT: [] - summary: Get appServeApp + summary: Get Organization StackTemplates tags: - - AppServeApps - put: + - StackTemplates + post: consumes: - application/json - description: Update appServeApp + description: Add organization stackTemplates parameters: - - description: Organization ID - in: path - name: organizationId - required: true - type: string - - description: App ID - in: path - name: appId - required: true - type: string - - description: Request body to update app + - description: Add organization stack templates request in: body - name: object + name: body required: true schema: - $ref: '#/definitions/domain.UpdateAppServeAppRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationStackTemplatesRequest' produces: - application/json responses: "200": description: OK - schema: - type: string security: - JWT: [] - summary: Update appServeApp + summary: Add organization stackTemplates tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}/endpoint: - patch: + - StackTemplates + put: consumes: - application/json - description: Update app endpoint + description: Remove organization stackTemplates parameters: - - description: Organization ID - in: path - name: organizationId - required: true - type: string - - description: appId - in: path - name: appId - required: true - type: string - - description: Request body to update app endpoint + - description: Remove organization stack templates request in: body name: body required: true schema: - $ref: '#/definitions/domain.UpdateAppServeAppEndpointRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationStackTemplatesRequest' produces: - application/json responses: "200": description: OK - schema: - type: string security: - JWT: [] - summary: Update app endpoint + summary: Remove organization stackTemplates tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}/exist: + - StackTemplates + /organizations/{organizationId}/stack-templates/{stackTemplateId}: get: consumes: - application/json - description: Get appServeApp by giving params + description: Get Organization StackTemplate produces: - application/json responses: "200": description: OK schema: - type: boolean + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackTemplateResponse' security: - JWT: [] - summary: Get appServeApp + summary: Get Organization StackTemplate tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}/latest-task: + - StackTemplates + /organizations/{organizationId}/stacks: get: consumes: - application/json - description: Get latest task from appServeApp + description: Get Stacks parameters: - - description: Organization ID + - description: organizationId in: path name: organizationId required: true type: string - - description: App ID - in: path - name: appId - required: true + - description: pageSize + in: query + name: limit + type: string + - description: pageNumber + in: query + name: page + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder type: string produces: - application/json @@ -3087,93 +9589,78 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetAppServeAppTaskResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStacksResponse' security: - JWT: [] - summary: Get latest task from appServeApp + summary: Get Stacks tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}/rollback: + - Stacks post: consumes: - application/json - description: Rollback appServeApp + description: Create Stack parameters: - - description: Organization ID + - description: organizationId in: path name: organizationId required: true type: string - - description: App ID - in: path - name: appId - required: true - type: string - - description: Request body to rollback app + - description: create cloud setting request in: body - name: object + name: body required: true schema: - $ref: '#/definitions/domain.RollbackAppServeAppRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackRequest' produces: - application/json responses: "200": description: OK schema: - type: string + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateStackResponse' security: - JWT: [] - summary: Rollback appServeApp + summary: Create Stack tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/{appId}/status: - patch: + - Stacks + /organizations/{organizationId}/stacks/{stackId}: + delete: consumes: - application/json - description: Update app status + description: Delete Stack parameters: - - description: Organization ID + - description: organizationId in: path name: organizationId required: true type: string - - description: App ID + - description: stackId in: path - name: appId + name: stackId required: true type: string - - description: Request body to update app status - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.UpdateAppServeAppStatusRequest' produces: - application/json responses: "200": description: OK - schema: - type: string security: - JWT: [] - summary: Update app status + summary: Delete Stack tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/count: + - Stacks get: consumes: - application/json - description: Get number of apps on given stack + description: Get Stack parameters: - - description: Organization ID + - description: organizationId in: path name: organizationId required: true type: string - - description: Stack ID - in: query + - description: stackId + in: path name: stackId required: true type: string @@ -3183,133 +9670,82 @@ paths: "200": description: OK schema: - type: integer + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackResponse' security: - JWT: [] - summary: Get number of apps on given stack + summary: Get Stack tags: - - AppServeApps - /organizations/{organizationId}/app-serve-apps/name/{name}/existence: - get: + - Stacks + put: consumes: - application/json - description: Check duplicate appServeAppName by giving params + description: Update Stack parameters: - - description: Organization ID + - description: organizationId in: path name: organizationId required: true type: string - - description: name + - description: stackId in: path - name: name + name: stackId required: true type: string + - description: Update cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackRequest' produces: - application/json responses: "200": description: OK - schema: - type: boolean security: - JWT: [] - summary: Check duplicate appServeAppName + summary: Update Stack tags: - - AppServeApps - /organizations/{organizationId}/cloud-accounts: - get: + - Stacks + /organizations/{organizationId}/stacks/{stackId}/favorite: + delete: consumes: - application/json - description: Get CloudAccounts + description: Delete favorite stack parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: pageSize - in: query - name: limit - type: string - - description: pageNumber - in: query - name: page - type: string - - description: sortColumn - in: query - name: soertColumn - type: string - - description: sortOrder - in: query - name: sortOrder - type: string - - description: filters - in: query - items: - type: string - name: filters - type: array - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.GetCloudAccountsResponse' - security: - - JWT: [] - summary: Get CloudAccounts - tags: - - CloudAccounts - post: - consumes: - - application/json - description: Create CloudAccount - parameters: - - description: organizationId + - description: stackId in: path - name: organizationId + name: stackId required: true type: string - - description: create cloud setting request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.CreateCloudAccountRequest' produces: - application/json responses: "200": description: OK - schema: - $ref: '#/definitions/domain.CreateCloudAccountResponse' security: - JWT: [] - summary: Create CloudAccount + summary: Delete favorite stack tags: - - CloudAccounts - /organizations/{organizationId}/cloud-accounts/{cloudAccountId}: - delete: + - Stacks + post: consumes: - application/json - description: Delete CloudAccount + description: Set favorite stack parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: Delete cloud setting request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.DeleteCloudAccountRequest' - - description: cloudAccountId + - description: stackId in: path - name: cloudAccountId + name: stackId required: true type: string produces: @@ -3319,22 +9755,23 @@ paths: description: OK security: - JWT: [] - summary: Delete CloudAccount + summary: Set favorite stack tags: - - CloudAccounts + - Stacks + /organizations/{organizationId}/stacks/{stackId}/kube-config: get: consumes: - application/json - description: Get CloudAccount + description: Get KubeConfig by stack parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: cloudAccountId + - description: organizationId in: path - name: cloudAccountId + name: stackId required: true type: string produces: @@ -3343,28 +9780,34 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetCloudAccountResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackKubeConfigResponse' security: - JWT: [] - summary: Get CloudAccount + summary: Get KubeConfig by stack tags: - - CloudAccounts - put: + - Stacks + /organizations/{organizationId}/stacks/{stackId}/policies: + post: consumes: - application/json - description: Update CloudAccount + description: 특정 스택의 정책 목록을 정책 식별자 리스트로 지정해서 추가한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: Update cloud setting request + - description: 스택 식별자 + in: path + name: stackId + required: true + type: string + - description: add policies for stack request in: body name: body required: true schema: - $ref: '#/definitions/domain.UpdateCloudAccountRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddPoliciesForStackRequest' produces: - application/json responses: @@ -3372,77 +9815,108 @@ paths: description: OK security: - JWT: [] - summary: Update CloudAccount + summary: '[AddPoliciesForStack] 특정 스택의 정책 목록 추가' tags: - - CloudAccounts - /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error: - delete: + - Policy + put: consumes: - application/json - description: Delete Force CloudAccount + description: 특정 스택에서 정책 식별자로 지정된 정책을 제거한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: cloudAccountId + - description: 스택 식별자 in: path - name: cloudAccountId + name: stackId required: true type: string + - description: delete policies for stack request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeletePoliciesForStackRequest' produces: - application/json - responses: - "200": - description: OK + responses: {} security: - JWT: [] - summary: Delete Force CloudAccount + summary: '[DeletePoliciesForStack] 특정 스택의 정책 제거' tags: - - CloudAccounts - /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota: + - Policy + /organizations/{organizationId}/stacks/{stackId}/policy-status: get: consumes: - application/json - description: Get resource quota by cloudAccount + description: 클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: cloudAccountId + - description: 스택 식별자 in: path - name: cloudAccountId + name: stackId required: true type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: sortColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetCloudAccountResourceQuotaResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListStackPolicyStatusResponse' security: - JWT: [] - summary: Get resource quota by cloudAccount + summary: '[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회' tags: - - CloudAccounts - /organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence: + - StackPolicyStatus + /organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId}: get: consumes: - application/json - description: Check awsAccountId for cloudAccount + description: 템플릿의 클러스터 버전 등 상태를 조회한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: awsAccountId + - description: 스택 식별자 in: path - name: awsAccountId + name: stackId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId required: true type: string produces: @@ -3451,101 +9925,113 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.CheckCloudAccountAwsAccountIdResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackPolicyTemplateStatusResponse' security: - JWT: [] - summary: Check awsAccountId for cloudAccount + summary: '[GetStackPolicyTemplateStatus] 클러스터 템플릿 상태 상세 조회' tags: - - CloudAccounts - /organizations/{organizationId}/cloud-accounts/name/{name}/existence: - get: + - StackPolicyStatus + patch: consumes: - application/json - description: Check name for cloudAccount + description: 해당 템플릿의 버전 업데이트 및 연관된 정책의 새 기본값을 설정한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: name + - description: 스택 식별자 in: path - name: name + name: stackId + required: true + type: string + - description: 정책 템플릿 식별자(uuid) + in: path + name: policyTemplateId required: true type: string + - description: update stack policy template status request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateStackPolicyTemplateStatusRequest' produces: - application/json responses: "200": description: OK - schema: - $ref: '#/definitions/domain.CheckCloudAccountNameResponse' security: - JWT: [] - summary: Check name for cloudAccount + summary: '[UpdateStackPolicyTemplateStatus] 템플릿 버전 업데이트' tags: - - CloudAccounts - /organizations/{organizationId}/dashboard/charts: + - StackPolicyStatus + /organizations/{organizationId}/stacks/{stackId}/statistics: get: consumes: - application/json - description: Get charts data + description: 클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다. parameters: - - description: organizationId + - description: 조직 식별자(o로 시작) in: path name: organizationId required: true type: string - - description: chartType + - description: 스택 식별자 + in: path + name: stackId + required: true + type: string + - description: pageSize in: query - name: chartType + name: pageSize type: string - - description: duration + - description: pageNumber in: query - name: duration - required: true + name: pageNumber type: string - - description: interval + - description: sortColumn in: query - name: interval - required: true + name: sortColumn type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetDashboardChartsResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.StackPolicyStatistics' security: - JWT: [] - summary: Get charts data + summary: '[ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회' tags: - - Dashboards - /organizations/{organizationId}/dashboard/charts/{chartType}: + - StackPolicyStatus + /organizations/{organizationId}/stacks/{stackId}/status: get: consumes: - application/json - description: Get chart data + description: Get Stack Status parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: chartType + - description: stackId in: path - name: chartType - required: true - type: string - - description: duration - in: query - name: duration - required: true - type: string - - description: interval - in: query - name: interval + name: stackId required: true type: string produces: @@ -3554,173 +10040,214 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetDashboardChartResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetStackStatusResponse' security: - JWT: [] - summary: Get chart data + summary: Get Stack Status tags: - - Dashboards - /organizations/{organizationId}/dashboard/resources: + - Stacks + /organizations/{organizationId}/stacks/name/{name}/existence: get: consumes: - application/json - description: Get resources + description: Check name for stack parameters: - description: organizationId in: path name: organizationId required: true type: string + - description: stackId + in: path + name: stackId + required: true + type: string + - description: name + in: path + name: name + required: true + type: string produces: - application/json responses: "200": description: OK - schema: - $ref: '#/definitions/domain.GetDashboardResourcesResponse' security: - JWT: [] - summary: Get resources + summary: Check name for stack tags: - - Dashboards - /organizations/{organizationId}/dashboard/stacks: + - Stacks + /organizations/{organizationId}/system-notification-rules: get: consumes: - application/json - description: Get stacks + description: Get SystemNotificationRules parameters: - description: organizationId in: path name: organizationId required: true type: string + - description: pageSize + in: query + name: pageSize + type: string + - description: pageNumber + in: query + name: pageNumber + type: string + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder + type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetDashboardStacksResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRulesResponse' security: - JWT: [] - summary: Get stacks + summary: Get SystemNotificationRules tags: - - Dashboards - /organizations/{organizationId}/my-profile: - delete: + - SystemNotificationRules + post: consumes: - application/json - description: Delete myProfile + description: Create SystemNotificationRule parameters: - description: organizationId in: path name: organizationId required: true type: string + - description: create stack template request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleRequest' produces: - application/json responses: "200": description: OK - "400": - description: Bad Request + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateSystemNotificationRuleResponse' security: - JWT: [] - summary: Delete myProfile + summary: Create SystemNotificationRule tags: - - My-profile - get: + - SystemNotificationRules + /organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}: + delete: consumes: - application/json - description: Get my profile detail + description: Delete SystemNotificationRule parameters: - description: organizationId in: path name: organizationId required: true type: string + - description: systemNotificationRuleId + in: path + name: systemNotificationRuleId + required: true + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetMyProfileResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteSystemNotificationRuleResponse' security: - JWT: [] - summary: Get my profile detail + summary: Delete SystemNotificationRule tags: - - My-profile - put: + - SystemNotificationRules + get: consumes: - application/json - description: Update my profile detail + description: Get SystemNotificationRule parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: 'Required fields: password due to double-check' - in: body - name: body + - description: systemNotificationRuleId + in: path + name: systemNotificationRuleId required: true - schema: - $ref: '#/definitions/domain.UpdateMyProfileRequest' + type: string produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.UpdateMyProfileResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationRuleResponse' security: - JWT: [] - summary: Update my profile detail + summary: Get SystemNotificationRule tags: - - My-profile - /organizations/{organizationId}/my-profile/next-password-change: + - SystemNotificationRules put: consumes: - application/json - description: Update user's password expired date to current date + description: Update SystemNotificationRule parameters: - description: organizationId in: path name: organizationId required: true type: string + - description: systemNotificationRuleId + in: path + name: systemNotificationRuleId + required: true + type: string + - description: Update systemNotificationRule request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRuleRequest' produces: - application/json responses: "200": description: OK - "400": - description: Bad Request - schema: - $ref: '#/definitions/httpErrors.RestError' security: - JWT: [] - summary: Update user's password expired date to current date + summary: Update SystemNotificationRule tags: - - My-profile - /organizations/{organizationId}/my-profile/password: - put: + - SystemNotificationRules + /organizations/{organizationId}/system-notification-rules/default-system-rules: + post: consumes: - application/json - description: Update user password detail + description: MakeDefaultSystemNotificationRules parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: update user password request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.UpdatePasswordRequest' produces: - application/json responses: @@ -3728,54 +10255,50 @@ paths: description: OK security: - JWT: [] - summary: Update user password detail + summary: MakeDefaultSystemNotificationRules tags: - - My-profile - /organizations/{organizationId}/primary-cluster: - patch: + - SystemNotificationRules + /organizations/{organizationId}/system-notification-rules/name/{name}/existence: + get: consumes: - application/json - description: Update primary cluster + description: Check name for systemNotificationRule parameters: + - description: name + in: path + name: name + required: true + type: string - description: organizationId in: path name: organizationId required: true type: string - - description: update primary cluster request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.UpdatePrimaryClusterRequest' produces: - application/json responses: "200": description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckSystemNotificationRuleNameResponse' security: - JWT: [] - summary: Update primary cluster + summary: Check name for systemNotificationRule tags: - - Organizations - /organizations/{organizationId}/stacks: + - SystemNotificationRules + /organizations/{organizationId}/system-notification-templates: get: consumes: - application/json - description: Get Stacks + description: Get Organization SystemNotificationTemplates parameters: - - description: organizationId - in: path - name: organizationId - required: true - type: string - description: pageSize in: query - name: limit + name: pageSize type: string - description: pageNumber in: query - name: page + name: pageNumber type: string - description: sortColumn in: query @@ -3785,66 +10308,67 @@ paths: in: query name: sortOrder type: string - - description: combinedFilter + - collectionFormat: csv + description: filters in: query - name: combinedFilter - type: string + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetStacksResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplatesResponse' security: - JWT: [] - summary: Get Stacks + summary: Get Organization SystemNotificationTemplates tags: - - Stacks + - SystemNotificationTemplates post: consumes: - application/json - description: Create Stack + description: Add organization systemNotificationTemplates parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: create cloud setting request + - description: Add organization systemNotification templates request in: body name: body required: true schema: - $ref: '#/definitions/domain.CreateStackRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.AddOrganizationSystemNotificationTemplatesRequest' produces: - application/json responses: "200": description: OK - schema: - $ref: '#/definitions/domain.CreateStackResponse' security: - JWT: [] - summary: Create Stack + summary: Add organization systemNotificationTemplates tags: - - Stacks - /organizations/{organizationId}/stacks/{stackId}: - delete: + - SystemNotificationTemplates + put: consumes: - application/json - description: Delete Stack + description: Remove organization systemNotificationTemplates parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: stackId - in: path - name: stackId + - description: Remove organization systemNotification templates request + in: body + name: body required: true - type: string + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RemoveOrganizationSystemNotificationTemplatesRequest' produces: - application/json responses: @@ -3852,22 +10376,18 @@ paths: description: OK security: - JWT: [] - summary: Delete Stack + summary: Remove organization systemNotificationTemplates tags: - - Stacks + - SystemNotificationTemplates + /organizations/{organizationId}/system-notification-templates/{systemNotificationTemplateId}: get: consumes: - application/json - description: Get Stack + description: Get Organization SystemNotificationTemplate parameters: - - description: organizationId - in: path - name: organizationId - required: true - type: string - - description: stackId + - description: systemNotificationTemplateId in: path - name: stackId + name: systemNotificationTemplateId required: true type: string produces: @@ -3876,82 +10396,72 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetStackResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationTemplateResponse' security: - JWT: [] - summary: Get Stack + summary: Get Organization SystemNotificationTemplate tags: - - Stacks - put: + - SystemNotificationTemplates + /organizations/{organizationId}/system-notifications: + get: consumes: - application/json - description: Update Stack + description: Get SystemNotifications parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: stackId - in: path - name: stackId - required: true + - description: pageSize + in: query + name: pageSize type: string - - description: Update cloud setting request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.UpdateStackRequest' - produces: - - application/json - responses: - "200": - description: OK - security: - - JWT: [] - summary: Update Stack - tags: - - Stacks - /organizations/{organizationId}/stacks/{stackId}/favorite: - delete: - consumes: - - application/json - description: Delete favorite stack - parameters: - - description: organizationId - in: path - name: organizationId - required: true + - description: pageNumber + in: query + name: pageNumber type: string - - description: stackId - in: path - name: stackId - required: true + - description: sortColumn + in: query + name: soertColumn + type: string + - description: sortOrder + in: query + name: sortOrder type: string + - collectionFormat: csv + description: filters + in: query + items: + type: string + name: filters + type: array produces: - application/json responses: "200": description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationsResponse' security: - JWT: [] - summary: Delete favorite stack + summary: Get SystemNotifications tags: - - Stacks - post: + - SystemNotifications + /organizations/{organizationId}/system-notifications/{systemNotificationId}: + delete: consumes: - application/json - description: Set favorite stack + description: Delete SystemNotification parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: stackId + - description: systemNotificationId in: path - name: stackId + name: systemNotificationId required: true type: string produces: @@ -3961,23 +10471,22 @@ paths: description: OK security: - JWT: [] - summary: Set favorite stack + summary: Delete SystemNotification tags: - - Stacks - /organizations/{organizationId}/stacks/{stackId}/kube-config: + - SystemNotifications get: consumes: - application/json - description: Get KubeConfig by stack + description: Get SystemNotification parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: organizationId + - description: systemNotificationId in: path - name: stackId + name: systemNotificationId required: true type: string produces: @@ -3986,59 +10495,57 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetStackKubeConfigResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetSystemNotificationResponse' security: - JWT: [] - summary: Get KubeConfig by stack + summary: Get SystemNotification tags: - - Stacks - /organizations/{organizationId}/stacks/{stackId}/status: - get: + - SystemNotifications + put: consumes: - application/json - description: Get Stack Status + description: Update SystemNotification parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: stackId + - description: systemNotificationId in: path - name: stackId + name: systemNotificationId required: true type: string + - description: Update cloud setting request + in: body + name: body + required: true + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateSystemNotificationRequest' produces: - application/json responses: "200": description: OK - schema: - $ref: '#/definitions/domain.GetStackStatusResponse' security: - JWT: [] - summary: Get Stack Status + summary: Update SystemNotification tags: - - Stacks - /organizations/{organizationId}/stacks/name/{name}/existence: - get: + - SystemNotifications + /organizations/{organizationId}/system-notifications/{systemNotificationId}/actions: + post: consumes: - application/json - description: Check name for stack + description: Create systemNotification action parameters: - description: organizationId in: path name: organizationId required: true type: string - - description: stackId - in: path - name: stackId - required: true - type: string - - description: name + - description: systemNotificationId in: path - name: name + name: systemNotificationId required: true type: string produces: @@ -4048,9 +10555,9 @@ paths: description: OK security: - JWT: [] - summary: Check name for stack + summary: Create systemNotification action tags: - - Stacks + - SystemNotifications /organizations/{organizationId}/users: get: consumes: @@ -4064,11 +10571,11 @@ paths: type: string - description: pageSize in: query - name: limit + name: pageSize type: string - description: pageNumber in: query - name: page + name: pageNumber type: string - description: sortColumn in: query @@ -4078,7 +10585,8 @@ paths: in: query name: sortOrder type: string - - description: filters + - collectionFormat: csv + description: filters in: query items: type: string @@ -4091,7 +10599,7 @@ paths: description: OK schema: items: - $ref: '#/definitions/domain.ListUserBody' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ListUserBody' type: array security: - JWT: [] @@ -4113,19 +10621,47 @@ paths: name: body required: true schema: - $ref: '#/definitions/domain.CreateUserRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserRequest' produces: - application/json responses: "200": description: create user response schema: - $ref: '#/definitions/domain.CreateUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CreateUserResponse' security: - JWT: [] summary: Create user tags: - Users + put: + consumes: + - application/json + description: Update multiple users + parameters: + - description: organizationId + in: path + name: organizationId + required: true + type: string + - description: input + in: body + name: body + required: true + schema: + items: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUsersRequest' + type: array + produces: + - application/json + responses: + "200": + description: OK + security: + - JWT: [] + summary: Update multiple users + tags: + - Users /organizations/{organizationId}/users/{accountId}: delete: consumes: @@ -4148,7 +10684,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.User' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DeleteUserResponse' security: - JWT: [] summary: Delete user @@ -4175,7 +10711,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.GetUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUserResponse' security: - JWT: [] summary: Get user detail @@ -4201,19 +10737,45 @@ paths: name: body required: true schema: - $ref: '#/definitions/domain.UpdateUserRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.UpdateUserResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.UpdateUserResponse' security: - JWT: [] summary: Update user tags: - Users + /organizations/{organizationId}/users/{accountId}/permissions: + get: + description: Get Permissions By Account ID + parameters: + - description: Organization ID + in: path + name: organizationId + required: true + type: string + - description: Account ID + in: path + name: accountId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetUsersPermissionsResponse' + security: + - JWT: [] + summary: Get Permissions By Account ID + tags: + - Users /organizations/{organizationId}/users/{accountId}/reset-password: put: consumes: @@ -4261,7 +10823,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.CheckExistedResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] summary: Get user id existence @@ -4287,84 +10849,21 @@ paths: "200": description: OK schema: - $ref: '#/definitions/domain.CheckExistedResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] summary: Get user email existence tags: - Users - /stack-templates: + /organizations/name/{name}/existence: get: consumes: - application/json - description: Get StackTemplates - parameters: - - description: pageSize - in: query - name: limit - type: string - - description: pageNumber - in: query - name: page - type: string - - description: sortColumn - in: query - name: soertColumn - type: string - - description: sortOrder - in: query - name: sortOrder - type: string - - description: filters - in: query - items: - type: string - name: filters - type: array - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.GetStackTemplatesResponse' - security: - - JWT: [] - summary: Get StackTemplates - tags: - - StackTemplates - post: - consumes: - - application/json - description: Create StackTemplate - parameters: - - description: create stack template request - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.CreateStackTemplateRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.CreateStackTemplateResponse' - security: - - JWT: [] - summary: Create StackTemplate 'NOT IMPLEMENTED' - tags: - - StackTemplates - /stack-templates/{stackTemplateId}: - delete: - consumes: - - application/json - description: Delete StackTemplate + description: Check name for organization parameters: - - description: stackTemplateId + - description: name in: path - name: stackTemplateId + name: name required: true type: string produces: @@ -4374,57 +10873,61 @@ paths: description: OK security: - JWT: [] - summary: Delete StackTemplate 'NOT IMPLEMENTED' + summary: Check name for organization tags: - - StackTemplates + - Organizations + /permissions/templates: get: consumes: - application/json - description: Get StackTemplate - parameters: - - description: stackTemplateId - in: path - name: stackTemplateId - required: true - type: string + description: Get Permission Templates produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/domain.GetStackTemplateResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.GetPermissionTemplatesResponse' security: - JWT: [] - summary: Get StackTemplate + summary: Get Permission Templates tags: - - StackTemplates - put: + - Permission + /policy-templates/rego-compile: + post: consumes: - application/json - description: Update StackTemplate + description: Rego 코드 컴파일 및 파라미터 파싱을 수행한다. 파라미터 파싱을 위해서는 먼저 컴파일이 성공해야 하며, parseParameter를 + false로 하면 컴파일만 수행할 수 있다. parameters: - - description: Update stack template request + - description: 파라미터 파싱 여부 + in: query + name: parseParameter + required: true + type: boolean + - description: Rego 코드 in: body name: body required: true schema: - $ref: '#/definitions/domain.UpdateStackTemplateRequest' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileRequest' produces: - application/json responses: "200": description: OK + schema: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.RegoCompileResponse' security: - JWT: [] - summary: Update StackTemplate 'NOT IMPLEMENTED' + summary: '[CompileRego] Rego 코드 컴파일 및 파라미터 파싱' tags: - - StackTemplates - /system-api/organizations/{organizationId}/alerts: + - PolicyTemplate + /system-api/organizations/{organizationId}/system-notifications: post: consumes: - application/json - description: Create alert. ADMIN ONLY + description: Create systemNotification. ADMIN ONLY parameters: - description: organizationId in: path @@ -4438,9 +10941,9 @@ paths: description: OK security: - JWT: [] - summary: Create alert. ADMIN ONLY + summary: Create systemNotification. ADMIN ONLY tags: - - Alerts + - SystemNotifications securityDefinitions: JWT: in: header diff --git a/cmd/server/main.go b/cmd/server/main.go index eea9f26d..d5e62bde 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,15 +1,17 @@ package main import ( + "context" "flag" "fmt" "net/http" "strconv" + "strings" "github.com/spf13/pflag" "github.com/spf13/viper" - _ "github.com/openinfradev/tks-api/api/swagger" + "github.com/openinfradev/tks-api/api/swagger" "github.com/openinfradev/tks-api/internal/database" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/mail" @@ -42,7 +44,7 @@ func init() { flag.String("console-address", "https://tks-console-dev.taco-cat.xyz", "service address for console") // app-serve-apps - flag.String("image-registry-url", "harbor-dev.taco-cat.xyz/appserving", "URL of image registry") + flag.String("image-registry-url", "harbor.taco-cat.xyz/appserving", "URL of image registry") flag.String("harbor-pw-secret", "harbor-core", "name of harbor password secret") flag.String("git-repository-url", "github.com/openinfradev", "URL of git repository") @@ -71,34 +73,42 @@ func init() { flag.Parse() if err := viper.BindPFlags(pflag.CommandLine); err != nil { - log.Error(err) + log.Error(context.Background(), err) } + address := viper.GetString("external-address") + arr := strings.Split(address, "//") + if len(arr) >= 2 { + address = arr[1] + } + + swagger.SwaggerInfo.Host = address } -// @title tks-api service -// @version 1.0 -// @description This is backend api service for tks platform +// @title tks-api service +// @version 1.0 +// @description This is backend api service for tks platform -// @contact.name taekyu.kang@sk.com -// @contact.url -// @contact.email taekyu.kang@sk.com +// @contact.name taekyu.kang@sk.com +// @contact.url +// @contact.email taekyu.kang@sk.com -// @license.name Apache 2.0 -// @license.url http://www.apache.org/licenses/LICENSE-2.0.html +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html -// @securitydefinitions.apikey JWT -// @in header -// @name Authorization +// @securitydefinitions.apikey JWT +// @in header +// @name Authorization -// @host tks-api-ft.taco-cat.xyz -// @BasePath /api/1.0/ +// @host tks-api-dev.taco-cat.xyz +// @BasePath /api/1.0/ func main() { - log.Info("*** Arguments *** ") + ctx := context.Background() + log.Info(ctx, "*** Arguments *** ") for i, s := range viper.AllSettings() { - log.Info(fmt.Sprintf("%s : %v", i, s)) + log.Info(ctx, fmt.Sprintf("%s : %v", i, s)) } - log.Info("****************** ") + log.Info(ctx, "****************** ") // For web service asset := route.NewAssetHandler(viper.GetString("web-root")) @@ -106,7 +116,13 @@ func main() { // Initialize database db, err := database.InitDB() if err != nil { - log.Fatal("cannot connect gormDB") + log.Fatal(ctx, "cannot connect gormDB") + } + + // Ensure default rows in database + err = database.EnsureDefaultRows(db) + if err != nil { + log.Fatal(ctx, "cannot Initializing Default Rows in Database: ", err) } // Initialize external client @@ -114,12 +130,12 @@ func main() { if viper.GetString("argo-address") == "" || viper.GetInt("argo-port") == 0 { argoClient, err = argowf.NewMock() if err != nil { - log.Fatal("failed to create argowf client : ", err) + log.Fatal(ctx, "failed to create argowf client : ", err) } } else { argoClient, err = argowf.New(viper.GetString("argo-address"), viper.GetInt("argo-port"), false, "") if err != nil { - log.Fatal("failed to create argowf client : ", err) + log.Fatal(ctx, "failed to create argowf client : ", err) } } @@ -130,20 +146,20 @@ func main() { ClientSecret: viper.GetString("keycloak-client-secret"), }) - err = keycloak.InitializeKeycloak() + err = keycloak.InitializeKeycloak(ctx) if err != nil { - log.Fatal("failed to initialize keycloak : ", err) + log.Fatal(ctx, "failed to initialize keycloak : ", err) } - err = mail.Initialize() + err = mail.Initialize(ctx) if err != nil { - log.Fatal("failed to initialize ses : ", err) + log.Fatal(ctx, "failed to initialize ses : ", err) } route := route.SetupRouter(db, argoClient, keycloak, asset) - log.Info("Starting server on ", viper.GetInt("port")) + log.Info(ctx, "Starting server on ", viper.GetInt("port")) err = http.ListenAndServe("0.0.0.0:"+strconv.Itoa(viper.GetInt("port")), route) if err != nil { - log.Fatal(err) + log.Fatal(ctx, err) } } diff --git a/go.mod b/go.mod index ff813bc2..14113d1d 100644 --- a/go.mod +++ b/go.mod @@ -1,143 +1,156 @@ module github.com/openinfradev/tks-api -go 1.18 +go 1.21 require ( - github.com/Nerzal/gocloak/v13 v13.1.0 - github.com/aws/aws-sdk-go-v2 v1.20.1 - github.com/aws/aws-sdk-go-v2/config v1.18.32 - github.com/aws/aws-sdk-go-v2/credentials v1.13.31 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.110.1 - github.com/aws/aws-sdk-go-v2/service/eks v1.29.2 - github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.16.2 - github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.20.2 - github.com/aws/aws-sdk-go-v2/service/servicequotas v1.15.1 - github.com/aws/aws-sdk-go-v2/service/ses v1.15.7 - github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 - github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/Masterminds/semver/v3 v3.2.0 + github.com/Nerzal/gocloak/v13 v13.9.0 + github.com/aws/aws-sdk-go-v2 v1.25.0 + github.com/aws/aws-sdk-go-v2/config v1.27.0 + github.com/aws/aws-sdk-go-v2/credentials v1.17.0 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.148.0 + github.com/aws/aws-sdk-go-v2/service/eks v1.39.0 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.23.0 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.29.0 + github.com/aws/aws-sdk-go-v2/service/servicequotas v1.20.1 + github.com/aws/aws-sdk-go-v2/service/ses v1.21.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 + github.com/deckarep/golang-set/v2 v2.6.0 github.com/go-playground/locales v0.14.1 github.com/go-playground/universal-translator v0.18.1 - github.com/go-playground/validator/v10 v10.13.0 - github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/go-playground/validator/v10 v10.18.0 + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.3.0 - github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.4.2 + github.com/google/uuid v1.6.0 + github.com/gorilla/handlers v1.5.2 + github.com/gorilla/mux v1.8.1 + github.com/gorilla/websocket v1.5.1 + github.com/iancoleman/strcase v0.3.0 + github.com/open-policy-agent/opa v0.62.1 github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.9.0 + github.com/sirupsen/logrus v1.9.3 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.12.0 - github.com/swaggo/http-swagger v1.3.3 - github.com/swaggo/swag v1.8.5 + github.com/spf13/viper v1.18.2 + github.com/swaggo/http-swagger v1.3.4 + github.com/swaggo/swag v1.16.3 github.com/thoas/go-funk v0.9.3 - github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.4.0 - golang.org/x/crypto v0.7.0 - golang.org/x/net v0.8.0 - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 + github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.5.0 + github.com/xeipuuv/gojsonschema v1.2.0 + golang.org/x/crypto v0.21.0 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/net v0.22.0 + golang.org/x/oauth2 v0.17.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 - gorm.io/datatypes v1.1.1 - gorm.io/driver/postgres v1.4.5 - gorm.io/gorm v1.25.0 - k8s.io/apimachinery v0.25.4 - k8s.io/client-go v0.25.2 - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed + gorm.io/datatypes v1.2.0 + gorm.io/driver/postgres v1.5.6 + gorm.io/gorm v1.25.7 + goyave.dev/goyave/v4 v4.4.11 + k8s.io/api v0.26.1 + k8s.io/apiextensions-apiserver v0.26.1 + k8s.io/apimachinery v0.26.4 + k8s.io/client-go v0.26.1 + k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 ) require ( + github.com/Code-Hex/uniseg v0.2.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 // indirect - github.com/aws/smithy-go v1.14.1 // indirect + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/PuerkitoBio/goquery v1.9.1 // indirect + github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 // indirect + github.com/aws/smithy-go v1.20.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/felixge/httpsnoop v1.0.1 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.6 // indirect - github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/go-resty/resty/v2 v2.7.0 // indirect - github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.13.0 // indirect - github.com/jackc/pgio v1.0.0 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.1 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.12.0 // indirect - github.com/jackc/pgx/v4 v4.17.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/leodido/go-urn v1.2.3 // indirect - github.com/lib/pq v1.10.4 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/ksuid v1.0.4 // indirect - github.com/spf13/afero v1.8.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/subosito/gotenv v1.3.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect - golang.org/x/tools v0.6.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gorm.io/driver/mysql v1.4.7 // indirect - k8s.io/api v0.25.4 // indirect - k8s.io/apiextensions-apiserver v0.23.5 // indirect - k8s.io/component-base v0.25.2 // indirect + gorm.io/driver/mysql v1.5.0 // indirect + k8s.io/component-base v0.26.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - sigs.k8s.io/cluster-api v1.1.3 // indirect - sigs.k8s.io/controller-runtime v0.11.2 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/cluster-api v1.4.4 // indirect + sigs.k8s.io/controller-runtime v0.14.5 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) replace github.com/openinfradev/tks-api => ./ diff --git a/go.sum b/go.sum index 4150f430..ef39f393 100644 --- a/go.sum +++ b/go.sum @@ -1,283 +1,157 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Code-Hex/uniseg v0.2.0 h1:QB/2UJFvEuRLSZqe+Sb1XQBTWjqGVbZoC6oSWzQRKws= +github.com/Code-Hex/uniseg v0.2.0/go.mod h1:/ndS2tP+X1lk2HUOcXWGtVTxVq0lWilwgMa4CbzdRsg= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/Nerzal/gocloak/v13 v13.1.0 h1:ret4pZTIsSQGZHURDMJ4jXnUmHyEoRykBqDTsAKoj8c= -github.com/Nerzal/gocloak/v13 v13.1.0/go.mod h1:rRBtEdh5N0+JlZZEsrfZcB2sRMZWbgSxI2EIv9jpJp4= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzSVZhw= +github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= +github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.20.0/go.mod h1:uWOr0m0jDsiWw8nnXiqZ+YG6LdvAlGYDLLf2NmHZoy4= -github.com/aws/aws-sdk-go-v2 v1.20.1 h1:rZBf5DWr7YGrnlTK4kgDQGn1ltqOg5orCYb/UhOFZkg= -github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac= -github.com/aws/aws-sdk-go-v2/config v1.18.32 h1:tqEOvkbTxwEV7hToRcJ1xZRjcATqwDVsWbAscgRKyNI= -github.com/aws/aws-sdk-go-v2/config v1.18.32/go.mod h1:U3ZF0fQRRA4gnbn9GGvOWLoT2EzzZfAWeKwnVrm1rDc= -github.com/aws/aws-sdk-go-v2/credentials v1.13.31 h1:vJyON3lG7R8VOErpJJBclBADiWTwzcwdkQpTKx8D2sk= -github.com/aws/aws-sdk-go-v2/credentials v1.13.31/go.mod h1:T4sESjBtY2lNxLgkIASmeP57b5j7hTQqCbqG0tWnxC4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 h1:X3H6+SU21x+76LRglk21dFRgMTJMa5QcpW+SqUf5BBg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7/go.mod h1:3we0V09SwcJBzNlnyovrR2wWJhWmVdqAsmVs4uronv8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 h1:c8ed/T9T2K5I+h/JzmF5tpI46+OODQ74dzmdo+QnaMg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38/go.mod h1:qggunOChCMu9ZF/UkAfhTz25+U2rLVb3ya0Ua6TTfCA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 h1:hNeAAymUY5gu11WrrmFb3CVIp9Dar9hbo44yzzcQpzA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32/go.mod h1:0ZXSqrty4FtQ7p8TEuRde/SZm9X05KT18LAUlR40Ln0= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 h1:+i1DOFrW3YZ3apE45tCal9+aDKK6kNEbW6Ib7e1nFxE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38/go.mod h1:1/jLp0OgOaWIetycOmycW+vYTYgTZFPttJQRgsI1PoU= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.110.1 h1:OaDeV+sdve2NV+kUheZX5bToHFmfIkflgOlZTKij0Bo= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.110.1/go.mod h1:Ie0Kp61cLk223argiS+t8vO29SpbFIphzlPflIvYcv0= -github.com/aws/aws-sdk-go-v2/service/eks v1.29.2 h1:ZwK94/wSfjVrsp9UucUZOb3QCn6zL9Wru5KauaYQCBg= -github.com/aws/aws-sdk-go-v2/service/eks v1.29.2/go.mod h1:B7NksWEFv8tWu1ob60c1bU1oa1iCIhoQ7AtnT3dE3+8= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.16.2 h1:AnXbttLaoY2bgyTotlmKJxozPUQdDQ3WGIQ7MwnznPE= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.16.2/go.mod h1:DYqqNwjoFTL0URDn5odwM4Pq455DHNlBmQXSAI2DbWs= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.20.2 h1:8TXQKxY0UjNAt/9nkDtfGechuri15a+yw9QtsEuOLtE= -github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.20.2/go.mod h1:AZv/T0/2rhNBLiY2k109TT6HJ7Z0P8Z+SYvs0jqVkXE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31/go.mod h1:3+lloe3sZuBQw1aBc5MyndvodzQlyqCZ7x1QPDHaWP4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 h1:dGAseBFEYxth10V23b5e2mAS+tX7oVbfYHD6dnDdAsg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32/go.mod h1:4jwAWKEkCR0anWk5+1RbfSg1R5Gzld7NLiuaq5bTR/Y= -github.com/aws/aws-sdk-go-v2/service/servicequotas v1.15.1 h1:ZL1ul+QW+JGT7my64SG8cPTudyLXRZXifPgJrpIA8qI= -github.com/aws/aws-sdk-go-v2/service/servicequotas v1.15.1/go.mod h1:2rmOo0RfixF8lcXxkquMIb1SY9oPY0iY/Mau8w8nFNs= -github.com/aws/aws-sdk-go-v2/service/ses v1.15.7 h1:eS3hpWtxVYnrysF+NEcjZo5zVvmgNTk22zRwJbtmCZY= -github.com/aws/aws-sdk-go-v2/service/ses v1.15.7/go.mod h1:sDSPw06IV4uB+RByvHkqDZKfP7SgIataOehYkchSups= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 h1:DSNpSbfEgFXRV+IfEcKE5kTbqxm+MeF5WgyeRlsLnHY= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.1/go.mod h1:TC9BubuFMVScIU+TLKamO6VZiYTkYoEHqlSQwAe2omw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 h1:hd0SKLMdOL/Sl6Z0np1PX9LeH2gqNtBe0MhTedA8MGI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1/go.mod h1:XO/VcyoQ8nKyKfFW/3DMsRQXsfh/052tHTWmg3xBXRg= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 h1:pAOJj+80tC8sPVgSDHzMYD6KLWsaLQ1kZw31PTeORbs= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.1/go.mod h1:G8SbvL0rFk4WOJroU8tKBczhsbhj2p/YY7qeJezJ3CI= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs= -github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/aws/aws-sdk-go-v2 v1.25.0 h1:sv7+1JVJxOu/dD/sz/csHX7jFqmP001TIY7aytBWDSQ= +github.com/aws/aws-sdk-go-v2 v1.25.0/go.mod h1:G104G1Aho5WqF+SR3mDIobTABQzpYV0WxMsKxlMggOA= +github.com/aws/aws-sdk-go-v2/config v1.27.0 h1:J5sdGCAHuWKIXLeXiqr8II/adSvetkx0qdZwdbXXpb0= +github.com/aws/aws-sdk-go-v2/config v1.27.0/go.mod h1:cfh8v69nuSUohNFMbIISP2fhmblGmYEOKs5V53HiHnk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.0 h1:lMW2x6sKBsiAJrpi1doOXqWFyEPoE886DTb1X0wb7So= +github.com/aws/aws-sdk-go-v2/credentials v1.17.0/go.mod h1:uT41FIH8cCIxOdUYIL0PYyHlL1NoneDuDSCwg5VE/5o= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 h1:xWCwjjvVz2ojYTP4kBKUuUh9ZrXfcAXpflhOUUeXg1k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0/go.mod h1:j3fACuqXg4oMTQOR2yY7m0NmJY0yBK4L4sLsRXq1Ins= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 h1:NPs/EqVO+ajwOoq56EfcGKa3L3ruWuazkIw1BqxwOPw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0/go.mod h1:D+duLy2ylgatV+yTlQ8JTuLfDD0BnFvnQRc+o6tbZ4M= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 h1:ks7KGMVUMoDzcxNWUlEdI+/lokMFD136EL6DWmUOV80= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0/go.mod h1:hL6BWM/d/qz113fVitZjbXR0E+RCTU1+x+1Idyn5NgE= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.148.0 h1:7imiXQvuqyUEu6wdcn6xRjR3zIJjDuAnS2e1S3ND+C0= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.148.0/go.mod h1:ntWksNNQcXImRQMdxab74tp+H94neF/TwQJ9Ndxb04k= +github.com/aws/aws-sdk-go-v2/service/eks v1.39.0 h1:0kuYeUF+PtxQbuIj74KQY9eUVYp06HRWWZGSExmPXqI= +github.com/aws/aws-sdk-go-v2/service/eks v1.39.0/go.mod h1:5OIWnEO/Vlng8uQmOSCxkTCuz5uh4091V3iOASiDZPQ= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.23.0 h1:DNOrYgqzRj9728Dh7Sf0cKLa3yG+z5w8ILz/X+BUnSc= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.23.0/go.mod h1:3AUoqMlKZDo28l0bjM706TIvYoJpq8siDNYYGVhqHEU= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.29.0 h1:6NKKRfzXW5KYHHuZp/QVfoj3sWFk5wZGuSnmY7EhPR8= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.29.0/go.mod h1:wBfYhqVwYqHxYkU3l5WZCdAyorLCFZf8T5ZnY6CPyw4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 h1:a33HuFlO0KsveiP90IUJh8Xr/cx9US2PqkSroaLc+o8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0/go.mod h1:SxIkWpByiGbhbHYTo9CMTUnx2G4p4ZQMrDPcRRy//1c= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 h1:SHN/umDLTmFTmYfI+gkanz6da3vK8Kvj/5wkqnTHbuA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0/go.mod h1:l8gPU5RYGOFHJqWEpPMoRTP0VoaWQSkJdKo+hwWnnDA= +github.com/aws/aws-sdk-go-v2/service/servicequotas v1.20.1 h1:RP62bFan0ocUpERLjEIgFzpcZkIccs2P3yxvNdPzETc= +github.com/aws/aws-sdk-go-v2/service/servicequotas v1.20.1/go.mod h1:qyFFLkY1mrTC8HV/GMtO5InUd6xGLtGoZulZVRl3o+o= +github.com/aws/aws-sdk-go-v2/service/ses v1.21.0 h1:0LOo7FveHh6sm7Oi08dPR4SurWRAONcf2/1Ld+z9VX8= +github.com/aws/aws-sdk-go-v2/service/ses v1.21.0/go.mod h1:uk3in5nX2gMAdKQf/Bpn9dHdPuJxnxDsAkTlgehuhLg= +github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 h1:u6OkVDxtBPnxPkZ9/63ynEe+8kHbtS5IfaC4PzVxzWM= +github.com/aws/aws-sdk-go-v2/service/sso v1.19.0/go.mod h1:YqbU3RS/pkDVu+v+Nwxvn0i1WB0HkNWEePWbmODEbbs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 h1:6DL0qu5+315wbsAEEmzK+P9leRwNbkp+lGjPC+CEvb8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0/go.mod h1:olUAyg+FaoFaL/zFaeQQONjOZ9HXoxgvI/c7mQTYz7M= +github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 h1:cjTRjh700H36MQ8M0LnDn33W3JmwC77mdxIIyPWCdpM= +github.com/aws/aws-sdk-go-v2/service/sts v1.27.0/go.mod h1:nXfOBMWPokIbOY+Gi7a1psWMSvskUCemZzI+SMB7Akc= +github.com/aws/smithy-go v1.20.0 h1:6+kZsCXZwKxZS9RfISnPc4EXlHoyAkm2hPuM8X2BrrQ= +github.com/aws/smithy-go v1.20.0/go.mod h1:uo5RKksAl4PzhqaAbjd4rLgFoq5koTsQKYuGe7dklGc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/spec v0.20.6 h1:ich1RQ3WDbfoeTqTAb+5EIxNmpKVJZWBNah9RAT0jIQ= github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= -github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/flect v0.2.4 h1:BSYA8+T60cdyq+vynaSUjqSVI9mDEg9ZfQUXKmfjo4I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= +github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -287,367 +161,148 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= -github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= -github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= -github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= -github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= -github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= -github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= +github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA= -github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/microsoft/go-mssqldb v0.21.0 h1:p2rpHIL7TlSv1QrbXJUAcbyRKnIT0C9rRkH2E4OjLn8= +github.com/microsoft/go-mssqldb v0.21.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ= +github.com/onsi/gomega v1.27.5/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/open-policy-agent/opa v0.62.1 h1:UcxBQ0fe6NEjkYc775j4PWoUFFhx4f6yXKIKSTAuTVk= +github.com/open-policy-agent/opa v0.62.1/go.mod h1:YqiSIIuvKwyomtnnXkJvy0E3KtVKbavjPJ/hNMuOmeM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -655,486 +310,161 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe h1:K8pHPVoTgxFJt1lXuIzzOX7zZhZFldJQK/CgKx9BFIc= github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= -github.com/swaggo/http-swagger v1.3.3 h1:Hu5Z0L9ssyBLofaama21iYaF2VbWyA8jdohaaCGpHsc= -github.com/swaggo/http-swagger v1.3.3/go.mod h1:sE+4PjD89IxMPm77FnkDz0sdO+p5lbXzrVWT6OTVVGo= -github.com/swaggo/swag v1.8.5 h1:7NgtfXsXE+jrcOwRyiftGKW7Ppydj7tZiVenuRf1fE4= -github.com/swaggo/swag v1.8.5/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg= +github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= +github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.4.0 h1:sP0eIUypeNWLgUXhkko79xEnfuhBjURyPxef7+4SjjE= -github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.4.0/go.mod h1:qpFFjYuV3ATwNkHuUIWXePY/DcNUXFGlNXy0lDqoVlo= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.5.0 h1:BBu+4LBAa6kMzOwhA5hchMa8NVW2JlyUMldoRrcd67g= +github.com/vmware-tanzu/cluster-api-provider-bringyourownhost v0.5.0/go.mod h1:4N15SpYBrqBFtblYzwp3RkRXVYlT69UZIZKbpU96FTQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1144,118 +474,77 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/datatypes v1.1.1 h1:XAjO7NNfUKVUvnS3+BkqMrPXxCAcxDlpOYbjnizxNCw= -gorm.io/datatypes v1.1.1/go.mod h1:u8GEgFjJ+GpsGfgHmBUcQqHm/937t3sj/SO9dvbndTg= -gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y= -gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc= -gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= -gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg= -gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= -gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= -gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= -gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU= -gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco= +gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04= +gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM= +gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo= +gorm.io/driver/postgres v1.5.6 h1:ydr9xEd5YAM0vxVDY0X139dyzNz10spDiDlC7+ibLeU= +gorm.io/driver/postgres v1.5.6/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= +gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= +gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= +gorm.io/driver/sqlserver v1.4.3 h1:ji91Zb/zS7NWrMRJBfoM9C0gyxCSh9anQoJApqEkiV8= +gorm.io/driver/sqlserver v1.4.3/go.mod h1:6ytXpaVImPf267JVRZ74Hn+gZaBRUX/HkvYx+3aS+78= +gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +goyave.dev/goyave/v4 v4.4.11 h1:HdJJ82ZWrkB7sBsituVRgQhcDvmAavfVbd6xynaUN8Y= +goyave.dev/goyave/v4 v4.4.11/go.mod h1:kMbB3bf7ZhUD4Cq1Zy6nM4fHBHYO5mPSkTF6WStf4ik= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8= -k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs= -k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ= -k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI= -k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ= -k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc= -k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw= -k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4= -k8s.io/client-go v0.25.2 h1:SUPp9p5CwM0yXGQrwYurw9LWz+YtMwhWd0GqOsSiefo= -k8s.io/client-go v0.25.2/go.mod h1:i7cNU7N+yGQmJkewcRD2+Vuj4iz7b30kI8OcL3horQ4= -k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk= -k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0= -k8s.io/component-base v0.25.2 h1:Nve/ZyHLUBHz1rqwkjXm/Re6IniNa5k7KgzxZpTfSQY= -k8s.io/component-base v0.25.2/go.mod h1:90W21YMr+Yjg7MX+DohmZLzjsBtaxQDDwaX4YxDkl60= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/api v0.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= +k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= +k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= +k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= +k8s.io/apimachinery v0.26.4 h1:rZccKdBLg9vP6J09JD+z8Yr99Ce8gk3Lbi9TCx05Jzs= +k8s.io/apimachinery v0.26.4/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= +k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= k8s.io/kubectl v0.25.2 h1:2993lTeVimxKSWx/7z2PiJxUILygRa3tmC4QhFaeioA= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw= -sigs.k8s.io/cluster-api v1.1.3 h1:t682KcIPFeKGwe2SlxGvZa/HVmLA80XJ45KHBhzUETM= -sigs.k8s.io/cluster-api v1.1.3/go.mod h1:XqFZ0s9+KKjI/K39/EzHyAb4Sljprqvnm/XKWPgPp3Y= -sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA= -sigs.k8s.io/controller-runtime v0.11.2/go.mod h1:P6QCzrEjLaZGqHsfd+os7JQ+WFZhvB8MRFsn4dWF7O4= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= +k8s.io/kubectl v0.25.2/go.mod h1:eoBGJtKUj7x38KXelz+dqVtbtbKwCqyKzJWmBHU0prg= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= +k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/cluster-api v1.4.4 h1:+V+mzDe+gsWYclosvWlH0wadC8vIUsBvZLcWOYnVWRA= +sigs.k8s.io/cluster-api v1.4.4/go.mod h1:/SeFds4NXJ+Gp2etqHyoNuO6yoxTfVq6Zmd2OGxd/qM= +sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s= +sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/go.work b/go.work deleted file mode 100644 index 9a21ec73..00000000 --- a/go.work +++ /dev/null @@ -1,5 +0,0 @@ -go 1.18 - -use ( - . -) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 06c1ca6b..00000000 --- a/go.work.sum +++ /dev/null @@ -1,118 +0,0 @@ -cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= -cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= -cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc= -cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= -cloud.google.com/go/firestore v1.6.1 h1:8rBq3zRjnHx8UtBvaOWqBB1xq9jH6/wltfQLlTMh2Fw= -cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= -cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= -github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8Uo= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/getkin/kin-openapi v0.76.0 h1:j77zg3Ec+k+r+GA3d8hBoXpAc6KX9TbBPrwQGBIy2sY= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5 h1:zIaiqGYDQwa4HVx5wGRTXbx38Pqxjemn4BP98wpzpXo= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= -github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/serf v0.9.7 h1:hkdgbqizGQHuU5IPqYM1JdSMV8nKfpuOnZYXssk9muY= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= -github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= -github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/sagikazarmark/crypt v0.6.0 h1:REOEXCs/NFY/1jOCEouMuT4zEniE5YoXbvpC5X/TLF8= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= -go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= -go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= -go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao= -go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -google.golang.org/api v0.81.0 h1:o8WF5AvfidafWbFjsRyupxyEQJNUWxLZJCK5NXrxZZ8= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= -honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= diff --git a/hack/ast-generator.go b/hack/ast-generator.go new file mode 100644 index 00000000..4d023040 --- /dev/null +++ b/hack/ast-generator.go @@ -0,0 +1,21 @@ +package main + +//func main() { +// fset := token.NewFileSet() +// +// node, err := parser.ParseFile(fset, "./internal/delivery/api/endpoint.go", nil, parser.ParseComments) +// if err != nil { +// log.Fatalf("파싱 오류: %v", err) +// } +// +// ast.Print(fset, node) +// +// // write ast to file +// f, err := os.Create("ast.txt") +// if err != nil { +// log.Fatalf("파일 생성 오류: %v", err) +// } +// defer f.Close() +// ast.Fprint(f, fset, node, nil) +// +//} diff --git a/hack/endpoint-codegen.go b/hack/endpoint-codegen.go new file mode 100644 index 00000000..354082b3 --- /dev/null +++ b/hack/endpoint-codegen.go @@ -0,0 +1,192 @@ +//go:build ignore + +package main + +import ( + "bytes" + "go/ast" + "go/parser" + "go/token" + "html/template" + "io/ioutil" + "log" + "strings" +) + +const endpointFilePath = "./internal/delivery/api/endpoint.go" + +type endpointDecl struct { + Name string + Group string +} + +const indexTemplateStr = ` // This is generated code. DO NOT EDIT. + +package api + +` + +//const endpointTemplateStr = `// Comment below is special purpose for code generation. +//// Do not edit this comment. +//// Endpoint for Code Generation +//const ( +//{{- range .}} +// {{.Name}} Endpoint = iota +//{{- end}} +//) +//` + +const apiMapTemplateStr = `var ApiMap = map[Endpoint]EndpointInfo{ +{{- range .}} + {{.Name}}: { + Name: "{{.Name}}", + Group: "{{.Group}}", + }, +{{- end}} +} +` + +const stringFunctionTemplateStr = `func (e Endpoint) String() string { + switch e { +{{- range .}} + case {{.Name}}: + return "{{.Name}}" +{{- end}} + default: + return "" + } +} +` + +const getEndpointFunctionTemplateStr = `func GetEndpoint(name string) Endpoint { + switch name { +{{- range .}} + case "{{.Name}}": + return {{.Name}} +{{- end}} + default: + return -1 + } +} +` + +func main() { + fset := token.NewFileSet() + node, err := parser.ParseFile(fset, endpointFilePath, nil, parser.ParseComments) + if err != nil { + log.Fatalf("failed to parse file: %v", err) + } + + var endpoints []endpointDecl + var currentGroup string + + // AST를 탐색합니다. + ast.Inspect(node, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.GenDecl: + if x.Tok == token.CONST { + if x.Doc != nil { + for _, comment := range x.Doc.List { + if strings.Contains(comment.Text, "Endpoint for Code Generation") { + continue + } + if strings.HasPrefix(comment.Text, "//") { + currentGroup = strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")) + } + } + } + for _, spec := range x.Specs { + vs, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + if vs.Doc != nil { + for _, comment := range vs.Doc.List { + if strings.HasPrefix(comment.Text, "//") { + currentGroup = strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")) + } + } + } + + for _, name := range vs.Names { + endpoints = append(endpoints, endpointDecl{ + Name: name.Name, + Group: currentGroup, + }) + } + } + } + } + return true + }) + + for _, ep := range endpoints { + log.Printf("Endpoint: %s, Group: %s\n", ep.Name, ep.Group) + } + + // contents for index + indexTemplate := template.New("index") + indexTemplate, err = indexTemplate.Parse(indexTemplateStr) + if err != nil { + log.Fatalf("failed to parse template: %v", err) + } + var indexCode bytes.Buffer + if err := indexTemplate.Execute(&indexCode, endpoints); err != nil { + log.Fatalf("failed to execute template: %v", err) + } + + //// contents for endpoint + //endpointTemplate := template.New("endpoint") + //endpointTemplate, err = endpointTemplate.Parse(endpointTemplateStr) + //if err != nil { + // log.Fatalf("failed to parse template: %v", err) + //} + //var endpointCode bytes.Buffer + //if err := endpointTemplate.Execute(&endpointCode, endpoints); err != nil { + // log.Fatalf("failed to execute template: %v", err) + //} + + // contents for apiMap + apiMapTemplate := template.New("apiMap") + apiMapTemplate, err = apiMapTemplate.Parse(apiMapTemplateStr) + if err != nil { + log.Fatalf("failed to parse template: %v", err) + } + var apiMapCode bytes.Buffer + if err := apiMapTemplate.Execute(&apiMapCode, endpoints); err != nil { + log.Fatalf("failed to execute template: %v", err) + } + + // contents for stringFunction + stringFunctionTemplate := template.New("stringFunction") + stringFunctionTemplate, err = stringFunctionTemplate.Parse(stringFunctionTemplateStr) + if err != nil { + log.Fatalf("failed to parse template: %v", err) + } + var stringFunctionCode bytes.Buffer + if err := stringFunctionTemplate.Execute(&stringFunctionCode, endpoints); err != nil { + log.Fatalf("failed to execute template: %v", err) + } + + // contents for getEndpointFunction + getEndpointFunctionTemplate := template.New("getEndpointFunction") + getEndpointFunctionTemplate, err = getEndpointFunctionTemplate.Parse(getEndpointFunctionTemplateStr) + if err != nil { + log.Fatalf("failed to parse template: %v", err) + } + var getEndpointFunctionCode bytes.Buffer + if err := getEndpointFunctionTemplate.Execute(&getEndpointFunctionCode, endpoints); err != nil { + log.Fatalf("failed to execute template: %v", err) + } + + // replace original file(endpointFilePath) with new contents + //contents := indexCode.String() + endpointCode.String() + apiMapCode.String() + stringFunctionCode.String() + getEndpointFunctionCode.String() + contents := indexCode.String() + apiMapCode.String() + stringFunctionCode.String() + getEndpointFunctionCode.String() + newFilePath := strings.Replace(endpointFilePath, "endpoint", "generated_endpoints.go", 1) + + if err := ioutil.WriteFile(newFilePath, []byte(contents), 0644); err != nil { + log.Fatalf("failed to write file: %v", err) + } + + log.Println("Code generation is done.") +} diff --git a/internal/constants.go b/internal/constants.go index 21a57eba..7210560a 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -16,3 +16,63 @@ const ( SYSTEM_API_VERSION = "/1.0" SYSTEM_API_PREFIX = "/system-api" ) + +// 일단 DB 로 데이터를 관리하지 않고, 하드코딩 처리함. +const SERVICE_LMA = `{ + "name": "Logging,Monitoring,Alerting", + "type": "LMA", + "applications": [ + { + "name": "thanos", + "version": "0.30.2", + "description": "다중클러스터의 모니터링 데이터 통합 질의처리" + }, + { + "name": "prometheus-stack", + "version": "v0.66.0", + "description": "모니터링 데이터 수집/저장 및 질의처리" + }, + { + "name": "alertmanager", + "version": "v0.25.0", + "description": "알람 처리를 위한 노티피케이션 서비스" + }, + { + "name": "loki", + "version": "2.6.1", + "description": "로그데이터 저장 및 질의처리" + }, + { + "name": "grafana", + "version": "8.3.3", + "description": "모니터링/로그 통합대시보드" + } + ] + }` + +const SERVICE_SERVICE_MESH = ` { + "name": "MSA", + "type": "SERVICE_MESH", + "applications": [ + { + "name": "istio", + "version": "v1.17.2", + "description": "MSA 플랫폼" + }, + { + "name": "jagger", + "version": "1.35.0", + "description": "분산 서비스간 트랜잭션 추적을 위한 플랫폼" + }, + { + "name": "kiali", + "version": "v1.63.0", + "description": "MSA 구조 및 성능을 볼 수 있는 Dashboard" + }, + { + "name": "k8ssandra", + "version": "1.6.0", + "description": "분산 서비스간 호출 로그를 저장하는 스토리지" + } + ] +}` diff --git a/internal/database/database.go b/internal/database/database.go index 7d7ff222..9fcb1a37 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -1,17 +1,23 @@ package database import ( + "context" "fmt" "os" "strings" + "github.com/openinfradev/tks-api/internal/pagination" + + "github.com/openinfradev/tks-api/internal/delivery/api" + + internal_gorm "github.com/openinfradev/tks-api/internal/gorm" "github.com/spf13/viper" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/repository" - "github.com/openinfradev/tks-api/pkg/domain" ) func InitDB() (*gorm.DB, error) { @@ -35,9 +41,10 @@ func InitDB() (*gorm.DB, error) { default: level = logger.Silent } + newLogger := internal_gorm.NewGormLogger().LogMode(level) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ - Logger: logger.Default.LogMode(level), + Logger: newLogger, }) if err != nil { return nil, err @@ -53,65 +60,86 @@ func InitDB() (*gorm.DB, error) { } func migrateSchema(db *gorm.DB) error { - // Auth - if err := db.AutoMigrate(&repository.CacheEmailCode{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.User{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.Role{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.Policy{}); err != nil { - return err - } - - // Organization - if err := db.AutoMigrate(&repository.Organization{}); err != nil { - return err - } - - // CloudAccount - if err := db.AutoMigrate(&repository.CloudAccount{}); err != nil { - return err - } - - // StackTemplate - if err := db.AutoMigrate(&repository.StackTemplate{}); err != nil { - return err - } - - // Cluster - if err := db.AutoMigrate(&repository.Cluster{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.ClusterFavorite{}); err != nil { - return err - } - - // Services - if err := db.AutoMigrate(&repository.AppGroup{}); err != nil { - return err - } - if err := db.AutoMigrate(&repository.Application{}); err != nil { + if err := db.AutoMigrate(&model.CacheEmailCode{}, + &model.ExpiredTokenTime{}, + &model.Role{}, + &model.CloudAccount{}, + &model.StackTemplate{}, + &model.Organization{}, + &model.User{}, + &model.Cluster{}, + &model.ClusterFavorite{}, + &model.AppGroup{}, + &model.Application{}, + &model.AppServeApp{}, + &model.AppServeAppTask{}, + &model.SystemNotification{}, + &model.SystemNotificationAction{}, + &model.SystemNotificationMetricParameter{}, + &model.SystemNotificationTemplate{}, + &model.SystemNotificationCondition{}, + &model.SystemNotificationRule{}, + &model.Permission{}, + &model.Endpoint{}, + &model.Project{}, + &model.ProjectMember{}, + &model.ProjectNamespace{}, + &model.ProjectRole{}, + &model.Audit{}, + &model.PolicyTemplateSupportedVersion{}, + &model.PolicyTemplate{}, + &model.Policy{}, + &model.Dashboard{}, + ); err != nil { return err } + return nil +} - // AppServe - if err := db.AutoMigrate(&domain.AppServeApp{}); err != nil { - return err - } - if err := db.AutoMigrate(&domain.AppServeAppTask{}); err != nil { +func EnsureDefaultRows(db *gorm.DB) error { + // Create default rows + repoFactory := repository.Repository{ + Auth: repository.NewAuthRepository(db), + User: repository.NewUserRepository(db), + Cluster: repository.NewClusterRepository(db), + Organization: repository.NewOrganizationRepository(db), + AppGroup: repository.NewAppGroupRepository(db), + AppServeApp: repository.NewAppServeAppRepository(db), + CloudAccount: repository.NewCloudAccountRepository(db), + StackTemplate: repository.NewStackTemplateRepository(db), + SystemNotification: repository.NewSystemNotificationRepository(db), + SystemNotificationRule: repository.NewSystemNotificationRuleRepository(db), + SystemNotificationTemplate: repository.NewSystemNotificationTemplateRepository(db), + Role: repository.NewRoleRepository(db), + Permission: repository.NewPermissionRepository(db), + Endpoint: repository.NewEndpointRepository(db), + Project: repository.NewProjectRepository(db), + Dashboard: repository.NewDashboardRepository(db), + } + + // + + ctx := context.Background() + pg := pagination.NewPagination(nil) + pg.Limit = 1000 + eps, err := repoFactory.Endpoint.List(ctx, pg) + if err != nil { return err } - // Alert - if err := db.AutoMigrate(&repository.Alert{}); err != nil { - return err + var storedEps = make(map[string]struct{}) + for _, ep := range eps { + storedEps[ep.Name] = struct{}{} } - if err := db.AutoMigrate(&repository.AlertAction{}); err != nil { - return err + for _, ep := range api.ApiMap { + if _, ok := storedEps[ep.Name]; !ok { + if err := repoFactory.Endpoint.Create(ctx, &model.Endpoint{ + Name: ep.Name, + Group: ep.Group, + }); err != nil { + return err + } + } } return nil diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go new file mode 100644 index 00000000..c847c189 --- /dev/null +++ b/internal/delivery/api/endpoint.go @@ -0,0 +1,301 @@ +package api + +type Endpoint int +type EndpointInfo struct { + Name string + Group string +} + +// Comment below is special purpose for code generation. +// Do not edit this comment. +// Endpoint for Code Generation +const ( + // Auth + Login Endpoint = iota + Logout + RefreshToken + FindId + FindPassword + VerifyIdentityForLostId + VerifyIdentityForLostPassword + VerifyToken + + // User + CreateUser + ListUser + GetUser + DeleteUser + UpdateUsers + UpdateUser + ResetPassword + CheckId + CheckEmail + GetPermissionsByAccountId + + // MyProfile + GetMyProfile + UpdateMyProfile + UpdateMyPassword + RenewPasswordExpiredDate + DeleteMyProfile + + // Organization + Admin_CreateOrganization + Admin_DeleteOrganization + GetOrganizations + GetOrganization + CheckOrganizationName + UpdateOrganization + UpdatePrimaryCluster + + // Cluster + CreateCluster + GetClusters + ImportCluster + GetCluster + DeleteCluster + GetClusterSiteValues + InstallCluster + CreateBootstrapKubeconfig + GetBootstrapKubeconfig + GetNodes + + //Appgroup + CreateAppgroup + GetAppgroups + GetAppgroup + DeleteAppgroup + GetApplications + CreateApplication + + // AppServeApp + GetAppServeAppTasksByAppId + GetAppServeAppTaskDetail + CreateAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + GetAppServeApps // 프로젝트 관리/앱 서빙/조회 + GetNumOfAppsOnStack // 프로젝트 관리/앱 서빙/조회 + GetAppServeApp // 프로젝트 관리/앱 서빙/조회 + GetAppServeAppLatestTask // 프로젝트 관리/앱 서빙/조회 + IsAppServeAppExist // 프로젝트 관리/앱 서빙/조회 // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + IsAppServeAppNameExist // 프로젝트 관리/앱 서빙/조회 // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + DeleteAppServeApp // 프로젝트 관리/앱 서빙/삭제 + UpdateAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + UpdateAppServeAppStatus // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + UpdateAppServeAppEndpoint // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + RollbackAppServeApp // 프로젝트 관리/앱 서빙/배포 // 프로젝트 관리/앱 서빙/빌드 + + // CloudAccount + GetCloudAccounts + CreateCloudAccount + CheckCloudAccountName + CheckAwsAccountId + GetCloudAccount + UpdateCloudAccount + DeleteCloudAccount + DeleteForceCloudAccount + GetResourceQuota + + // StackTemplate + Admin_GetStackTemplates + Admin_GetStackTemplate + Admin_GetStackTemplateServices + Admin_GetStackTemplateTemplateIds + Admin_CreateStackTemplate + Admin_UpdateStackTemplate + Admin_DeleteStackTemplate + Admin_UpdateStackTemplateOrganizations + Admin_CheckStackTemplateName + GetOrganizationStackTemplates + GetOrganizationStackTemplate + AddOrganizationStackTemplates + RemoveOrganizationStackTemplates + + // Dashboard + CreateDashboard + GetDashboard + UpdateDashboard + GetChartsDashboard // 대시보드/대시보드/조회 + GetChartDashboard // 대시보드/대시보드/조회 + GetStacksDashboard // 대시보드/대시보드/조회 + GetResourcesDashboard // 대시보드/대시보드/조회 + GetPolicyStatusDashboard + GetPolicyUpdateDashboard + GetPolicyEnforcementDashboard + GetPolicyViolationDashboard + GetPolicyViolationLogDashboard + GetPolicyStatisticsDashboard + GetWorkloadDashboard + GetPolicyViolationTop5Dashboard + + // SystemNotificationTemplate + Admin_CreateSystemNotificationTemplate + Admin_UpdateSystemNotificationTemplate + Admin_DeleteSystemNotificationTemplate + Admin_GetSystemNotificationTemplates + Admin_GetSystemNotificationTemplate + Admin_CheckSystemNotificationTemplateName + GetOrganizationSystemNotificationTemplates + GetOrganizationSystemNotificationTemplate + AddOrganizationSystemNotificationTemplates + RemoveOrganizationSystemNotificationTemplates + + // SystemNotificationRule + CreateSystemNotificationRule + GetSystemNotificationRules + GetSystemNotificationRule + CheckSystemNotificationRuleName + DeleteSystemNotificationRule + UpdateSystemNotificationRule + MakeDefaultSystemNotificationRules + + // SystemNotification + CreateSystemNotification + GetSystemNotifications + GetSystemNotification + DeleteSystemNotification + UpdateSystemNotification + CreateSystemNotificationAction + + // PolicyNotification + GetPolicyNotifications + GetPolicyNotification + + // Stack + GetStacks // 스택관리/조회 + CreateStack // 스택관리/생성 + CheckStackName // 스택관리/조회 + GetStack // 스택관리/조회 + UpdateStack // 스택관리/수정 + DeleteStack // 스택관리/삭제 + GetStackKubeConfig // 스택관리/조회 + GetStackStatus // 스택관리/조회 + SetFavoriteStack // 스택관리/조회 + DeleteFavoriteStack // 스택관리/조회 + InstallStack // 스택관리 / 조회 + + // Project + CreateProject // 프로젝트 관리/프로젝트/생성 + GetProjectRoles // 프로젝트 관리/설정-일반/조회 // 프로젝트 관리/설정-멤버/조회 + GetProjectRole // 프로젝트 관리/설정-일반/조회 // 프로젝트 관리/설정-멤버/조회 + GetProjects // 프로젝트 관리/프로젝트/조회 // 프로젝트 관리/설정-일반/조회 + GetProject // 프로젝트 관리/프로젝트/조회 // 프로젝트 관리/설정-일반/조회 + UpdateProject // 프로젝트 관리/설정-일반/수정 + DeleteProject // 프로젝트 관리/설정-일반/삭제 + AddProjectMember // 프로젝트 관리/설정-멤버/생성 + GetProjectMember // 프로젝트 관리/설정-멤버/조회 + GetProjectMembers // 프로젝트 관리/설정-멤버/조회 + RemoveProjectMember // 프로젝트 관리/설정-멤버/삭제 + UpdateProjectMemberRole // 프로젝트 관리/설정-멤버/수정 + CreateProjectNamespace // 프로젝트 관리/설정-네임스페이스/생성 + GetProjectNamespaces // 프로젝트 관리/설정-네임스페이스/조회 + GetProjectNamespace // 프로젝트 관리/설정-네임스페이스/조회 + UpdateProjectNamespace + DeleteProjectNamespace // 프로젝트 관리/설정-네임스페이스/삭제 + SetFavoriteProject + SetFavoriteProjectNamespace + UnSetFavoriteProject + UnSetFavoriteProjectNamespace + GetProjectKubeconfig + GetProjectNamespaceK8sResources + GetProjectNamespaceKubeconfig + + // Audit + GetAudits + GetAudit + DeleteAudit + + // Role + CreateTksRole + ListTksRoles + GetTksRole + DeleteTksRole + UpdateTksRole + GetPermissionsByRoleId + UpdatePermissionsByRoleId + IsRoleNameExisted + AppendUsersToRole + GetUsersInRoleId + RemoveUsersFromRole + + // Permission + GetPermissionTemplates + + // Admin_User + Admin_CreateUser + Admin_ListUser + Admin_GetUser + Admin_DeleteUser + Admin_UpdateUser + + // Admin Role + Admin_ListTksRoles + Admin_GetTksRole + + // Admin Project + Admin_GetProjects + + // PolicyTemplate + Admin_ListPolicyTemplate + Admin_CreatePolicyTemplate + Admin_DeletePolicyTemplate + Admin_GetPolicyTemplate + Admin_UpdatePolicyTemplate + Admin_GetPolicyTemplateDeploy + Admin_ListPolicyTemplateStatistics + Admin_ListPolicyTemplateVersions + Admin_CreatePolicyTemplateVersion + Admin_DeletePolicyTemplateVersion + Admin_GetPolicyTemplateVersion + Admin_ExistsPolicyTemplateKind + Admin_ExistsPolicyTemplateName + Admin_ExtractParameters + Admin_AddPermittedPolicyTemplatesForOrganization + Admin_DeletePermittedPolicyTemplatesForOrganization + + // StackPolicyStatus + ListStackPolicyStatus + GetStackPolicyTemplateStatus + UpdateStackPolicyTemplateStatus + + // Policy + GetMandatoryPolicies + SetMandatoryPolicies + GetPolicyStatistics + ListPolicy + CreatePolicy + DeletePolicy + GetPolicy + UpdatePolicy + UpdatePolicyTargetClusters + ExistsPolicyName + ExistsPolicyResourceName + GetPolicyEdit + AddPoliciesForStack + DeletePoliciesForStack + StackPolicyStatistics + + // OrganizationPolicyTemplate + ListPolicyTemplate + CreatePolicyTemplate + DeletePolicyTemplate + GetPolicyTemplate + UpdatePolicyTemplate + GetPolicyTemplateDeploy + ListPolicyTemplateStatistics + ListPolicyTemplateVersions + CreatePolicyTemplateVersion + DeletePolicyTemplateVersion + GetPolicyTemplateVersion + ExistsPolicyTemplateKind + ExistsPolicyTemplateName + ExtractParameters + + // PolicyTemplateExample + ListPolicyTemplateExample + GetPolicyTemplateExample + UpdatePolicyTemplateExample + DeletePolicyTemplateExample + + // Utility + CompileRego +) diff --git a/internal/delivery/api/endpoints_permission_test.go b/internal/delivery/api/endpoints_permission_test.go new file mode 100644 index 00000000..b012f456 --- /dev/null +++ b/internal/delivery/api/endpoints_permission_test.go @@ -0,0 +1,70 @@ +package api_test + +import ( + "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/model" + "testing" +) + +func TestEndpointsUsage(t *testing.T) { + var allEndpoints []string + for _, v := range api.ApiMap { + allEndpoints = append(allEndpoints, v.Name) + } + //allEndpoints := []Endpoint{ + // Login, Logout, RefreshToken, FindId, // 계속해서 모든 Endpoint 추가 + // // 나머지 Endpoint 상수들을 여기에 추가 + //} + usageCount := make(map[string]int) + ps := model.NewAdminPermissionSet() + + permissions := []*model.Permission{ + ps.Dashboard, + ps.Notification, + ps.Configuration, + ps.ProjectManagement, + ps.Stack, + ps.Policy, + ps.Common, + ps.Admin, + } + + leafPermissions := make([]*model.Permission, 0) + + for _, perm := range permissions { + leafPermissions = model.GetEdgePermission(perm, leafPermissions, nil) + } + + // Permission 설정에서 Endpoint 사용 횟수 카운트 + for _, perm := range leafPermissions { + countEndpoints(perm, usageCount) + } + + var unusedEndpoints, duplicatedEndpoints []string + + // 미사용 또는 중복 사용된 Endpoint 확인 및 출력 + for _, endpoint := range allEndpoints { + count, exists := usageCount[endpoint] + if !exists { + unusedEndpoints = append(unusedEndpoints, endpoint) + } else if count > 1 { + duplicatedEndpoints = append(duplicatedEndpoints, endpoint) + } + } + + for _, endpoint := range unusedEndpoints { + t.Logf("Unused Endpoint: %s", endpoint) + } + + t.Logf("\n") + for _, endpoint := range duplicatedEndpoints { + t.Logf("Duplicated Endpoint: %s", endpoint) + } + +} + +func countEndpoints(perm *model.Permission, usageCount map[string]int) { + for _, endpoint := range perm.Endpoints { + usageCount[endpoint.Name]++ + } +} diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go new file mode 100644 index 00000000..937cb4d0 --- /dev/null +++ b/internal/delivery/api/generated_endpoints.go.go @@ -0,0 +1,1882 @@ + // This is generated code. DO NOT EDIT. + +package api + +var ApiMap = map[Endpoint]EndpointInfo{ + Login: { + Name: "Login", + Group: "Auth", + }, + Logout: { + Name: "Logout", + Group: "Auth", + }, + RefreshToken: { + Name: "RefreshToken", + Group: "Auth", + }, + FindId: { + Name: "FindId", + Group: "Auth", + }, + FindPassword: { + Name: "FindPassword", + Group: "Auth", + }, + VerifyIdentityForLostId: { + Name: "VerifyIdentityForLostId", + Group: "Auth", + }, + VerifyIdentityForLostPassword: { + Name: "VerifyIdentityForLostPassword", + Group: "Auth", + }, + VerifyToken: { + Name: "VerifyToken", + Group: "Auth", + }, + CreateUser: { + Name: "CreateUser", + Group: "User", + }, + ListUser: { + Name: "ListUser", + Group: "User", + }, + GetUser: { + Name: "GetUser", + Group: "User", + }, + DeleteUser: { + Name: "DeleteUser", + Group: "User", + }, + UpdateUsers: { + Name: "UpdateUsers", + Group: "User", + }, + UpdateUser: { + Name: "UpdateUser", + Group: "User", + }, + ResetPassword: { + Name: "ResetPassword", + Group: "User", + }, + CheckId: { + Name: "CheckId", + Group: "User", + }, + CheckEmail: { + Name: "CheckEmail", + Group: "User", + }, + GetPermissionsByAccountId: { + Name: "GetPermissionsByAccountId", + Group: "User", + }, + GetMyProfile: { + Name: "GetMyProfile", + Group: "MyProfile", + }, + UpdateMyProfile: { + Name: "UpdateMyProfile", + Group: "MyProfile", + }, + UpdateMyPassword: { + Name: "UpdateMyPassword", + Group: "MyProfile", + }, + RenewPasswordExpiredDate: { + Name: "RenewPasswordExpiredDate", + Group: "MyProfile", + }, + DeleteMyProfile: { + Name: "DeleteMyProfile", + Group: "MyProfile", + }, + Admin_CreateOrganization: { + Name: "Admin_CreateOrganization", + Group: "Organization", + }, + Admin_DeleteOrganization: { + Name: "Admin_DeleteOrganization", + Group: "Organization", + }, + GetOrganizations: { + Name: "GetOrganizations", + Group: "Organization", + }, + GetOrganization: { + Name: "GetOrganization", + Group: "Organization", + }, + CheckOrganizationName: { + Name: "CheckOrganizationName", + Group: "Organization", + }, + UpdateOrganization: { + Name: "UpdateOrganization", + Group: "Organization", + }, + UpdatePrimaryCluster: { + Name: "UpdatePrimaryCluster", + Group: "Organization", + }, + CreateCluster: { + Name: "CreateCluster", + Group: "Cluster", + }, + GetClusters: { + Name: "GetClusters", + Group: "Cluster", + }, + ImportCluster: { + Name: "ImportCluster", + Group: "Cluster", + }, + GetCluster: { + Name: "GetCluster", + Group: "Cluster", + }, + DeleteCluster: { + Name: "DeleteCluster", + Group: "Cluster", + }, + GetClusterSiteValues: { + Name: "GetClusterSiteValues", + Group: "Cluster", + }, + InstallCluster: { + Name: "InstallCluster", + Group: "Cluster", + }, + CreateBootstrapKubeconfig: { + Name: "CreateBootstrapKubeconfig", + Group: "Cluster", + }, + GetBootstrapKubeconfig: { + Name: "GetBootstrapKubeconfig", + Group: "Cluster", + }, + GetNodes: { + Name: "GetNodes", + Group: "Cluster", + }, + CreateAppgroup: { + Name: "CreateAppgroup", + Group: "Appgroup", + }, + GetAppgroups: { + Name: "GetAppgroups", + Group: "Appgroup", + }, + GetAppgroup: { + Name: "GetAppgroup", + Group: "Appgroup", + }, + DeleteAppgroup: { + Name: "DeleteAppgroup", + Group: "Appgroup", + }, + GetApplications: { + Name: "GetApplications", + Group: "Appgroup", + }, + CreateApplication: { + Name: "CreateApplication", + Group: "Appgroup", + }, + GetAppServeAppTasksByAppId: { + Name: "GetAppServeAppTasksByAppId", + Group: "AppServeApp", + }, + GetAppServeAppTaskDetail: { + Name: "GetAppServeAppTaskDetail", + Group: "AppServeApp", + }, + CreateAppServeApp: { + Name: "CreateAppServeApp", + Group: "AppServeApp", + }, + GetAppServeApps: { + Name: "GetAppServeApps", + Group: "AppServeApp", + }, + GetNumOfAppsOnStack: { + Name: "GetNumOfAppsOnStack", + Group: "AppServeApp", + }, + GetAppServeApp: { + Name: "GetAppServeApp", + Group: "AppServeApp", + }, + GetAppServeAppLatestTask: { + Name: "GetAppServeAppLatestTask", + Group: "AppServeApp", + }, + IsAppServeAppExist: { + Name: "IsAppServeAppExist", + Group: "AppServeApp", + }, + IsAppServeAppNameExist: { + Name: "IsAppServeAppNameExist", + Group: "AppServeApp", + }, + DeleteAppServeApp: { + Name: "DeleteAppServeApp", + Group: "AppServeApp", + }, + UpdateAppServeApp: { + Name: "UpdateAppServeApp", + Group: "AppServeApp", + }, + UpdateAppServeAppStatus: { + Name: "UpdateAppServeAppStatus", + Group: "AppServeApp", + }, + UpdateAppServeAppEndpoint: { + Name: "UpdateAppServeAppEndpoint", + Group: "AppServeApp", + }, + RollbackAppServeApp: { + Name: "RollbackAppServeApp", + Group: "AppServeApp", + }, + GetCloudAccounts: { + Name: "GetCloudAccounts", + Group: "CloudAccount", + }, + CreateCloudAccount: { + Name: "CreateCloudAccount", + Group: "CloudAccount", + }, + CheckCloudAccountName: { + Name: "CheckCloudAccountName", + Group: "CloudAccount", + }, + CheckAwsAccountId: { + Name: "CheckAwsAccountId", + Group: "CloudAccount", + }, + GetCloudAccount: { + Name: "GetCloudAccount", + Group: "CloudAccount", + }, + UpdateCloudAccount: { + Name: "UpdateCloudAccount", + Group: "CloudAccount", + }, + DeleteCloudAccount: { + Name: "DeleteCloudAccount", + Group: "CloudAccount", + }, + DeleteForceCloudAccount: { + Name: "DeleteForceCloudAccount", + Group: "CloudAccount", + }, + GetResourceQuota: { + Name: "GetResourceQuota", + Group: "CloudAccount", + }, + Admin_GetStackTemplates: { + Name: "Admin_GetStackTemplates", + Group: "StackTemplate", + }, + Admin_GetStackTemplate: { + Name: "Admin_GetStackTemplate", + Group: "StackTemplate", + }, + Admin_GetStackTemplateServices: { + Name: "Admin_GetStackTemplateServices", + Group: "StackTemplate", + }, + Admin_GetStackTemplateTemplateIds: { + Name: "Admin_GetStackTemplateTemplateIds", + Group: "StackTemplate", + }, + Admin_CreateStackTemplate: { + Name: "Admin_CreateStackTemplate", + Group: "StackTemplate", + }, + Admin_UpdateStackTemplate: { + Name: "Admin_UpdateStackTemplate", + Group: "StackTemplate", + }, + Admin_DeleteStackTemplate: { + Name: "Admin_DeleteStackTemplate", + Group: "StackTemplate", + }, + Admin_UpdateStackTemplateOrganizations: { + Name: "Admin_UpdateStackTemplateOrganizations", + Group: "StackTemplate", + }, + Admin_CheckStackTemplateName: { + Name: "Admin_CheckStackTemplateName", + Group: "StackTemplate", + }, + GetOrganizationStackTemplates: { + Name: "GetOrganizationStackTemplates", + Group: "StackTemplate", + }, + GetOrganizationStackTemplate: { + Name: "GetOrganizationStackTemplate", + Group: "StackTemplate", + }, + AddOrganizationStackTemplates: { + Name: "AddOrganizationStackTemplates", + Group: "StackTemplate", + }, + RemoveOrganizationStackTemplates: { + Name: "RemoveOrganizationStackTemplates", + Group: "StackTemplate", + }, + CreateDashboard: { + Name: "CreateDashboard", + Group: "Dashboard", + }, + GetDashboard: { + Name: "GetDashboard", + Group: "Dashboard", + }, + UpdateDashboard: { + Name: "UpdateDashboard", + Group: "Dashboard", + }, + GetChartsDashboard: { + Name: "GetChartsDashboard", + Group: "Dashboard", + }, + GetChartDashboard: { + Name: "GetChartDashboard", + Group: "Dashboard", + }, + GetStacksDashboard: { + Name: "GetStacksDashboard", + Group: "Dashboard", + }, + GetResourcesDashboard: { + Name: "GetResourcesDashboard", + Group: "Dashboard", + }, + GetPolicyStatusDashboard: { + Name: "GetPolicyStatusDashboard", + Group: "Dashboard", + }, + GetPolicyUpdateDashboard: { + Name: "GetPolicyUpdateDashboard", + Group: "Dashboard", + }, + GetPolicyEnforcementDashboard: { + Name: "GetPolicyEnforcementDashboard", + Group: "Dashboard", + }, + GetPolicyViolationDashboard: { + Name: "GetPolicyViolationDashboard", + Group: "Dashboard", + }, + GetPolicyViolationLogDashboard: { + Name: "GetPolicyViolationLogDashboard", + Group: "Dashboard", + }, + GetPolicyStatisticsDashboard: { + Name: "GetPolicyStatisticsDashboard", + Group: "Dashboard", + }, + GetWorkloadDashboard: { + Name: "GetWorkloadDashboard", + Group: "Dashboard", + }, + GetPolicyViolationTop5Dashboard: { + Name: "GetPolicyViolationTop5Dashboard", + Group: "Dashboard", + }, + Admin_CreateSystemNotificationTemplate: { + Name: "Admin_CreateSystemNotificationTemplate", + Group: "SystemNotificationTemplate", + }, + Admin_UpdateSystemNotificationTemplate: { + Name: "Admin_UpdateSystemNotificationTemplate", + Group: "SystemNotificationTemplate", + }, + Admin_DeleteSystemNotificationTemplate: { + Name: "Admin_DeleteSystemNotificationTemplate", + Group: "SystemNotificationTemplate", + }, + Admin_GetSystemNotificationTemplates: { + Name: "Admin_GetSystemNotificationTemplates", + Group: "SystemNotificationTemplate", + }, + Admin_GetSystemNotificationTemplate: { + Name: "Admin_GetSystemNotificationTemplate", + Group: "SystemNotificationTemplate", + }, + Admin_CheckSystemNotificationTemplateName: { + Name: "Admin_CheckSystemNotificationTemplateName", + Group: "SystemNotificationTemplate", + }, + GetOrganizationSystemNotificationTemplates: { + Name: "GetOrganizationSystemNotificationTemplates", + Group: "SystemNotificationTemplate", + }, + GetOrganizationSystemNotificationTemplate: { + Name: "GetOrganizationSystemNotificationTemplate", + Group: "SystemNotificationTemplate", + }, + AddOrganizationSystemNotificationTemplates: { + Name: "AddOrganizationSystemNotificationTemplates", + Group: "SystemNotificationTemplate", + }, + RemoveOrganizationSystemNotificationTemplates: { + Name: "RemoveOrganizationSystemNotificationTemplates", + Group: "SystemNotificationTemplate", + }, + CreateSystemNotificationRule: { + Name: "CreateSystemNotificationRule", + Group: "SystemNotificationRule", + }, + GetSystemNotificationRules: { + Name: "GetSystemNotificationRules", + Group: "SystemNotificationRule", + }, + GetSystemNotificationRule: { + Name: "GetSystemNotificationRule", + Group: "SystemNotificationRule", + }, + CheckSystemNotificationRuleName: { + Name: "CheckSystemNotificationRuleName", + Group: "SystemNotificationRule", + }, + DeleteSystemNotificationRule: { + Name: "DeleteSystemNotificationRule", + Group: "SystemNotificationRule", + }, + UpdateSystemNotificationRule: { + Name: "UpdateSystemNotificationRule", + Group: "SystemNotificationRule", + }, + MakeDefaultSystemNotificationRules: { + Name: "MakeDefaultSystemNotificationRules", + Group: "SystemNotificationRule", + }, + CreateSystemNotification: { + Name: "CreateSystemNotification", + Group: "SystemNotification", + }, + GetSystemNotifications: { + Name: "GetSystemNotifications", + Group: "SystemNotification", + }, + GetSystemNotification: { + Name: "GetSystemNotification", + Group: "SystemNotification", + }, + DeleteSystemNotification: { + Name: "DeleteSystemNotification", + Group: "SystemNotification", + }, + UpdateSystemNotification: { + Name: "UpdateSystemNotification", + Group: "SystemNotification", + }, + CreateSystemNotificationAction: { + Name: "CreateSystemNotificationAction", + Group: "SystemNotification", + }, + GetPolicyNotifications: { + Name: "GetPolicyNotifications", + Group: "PolicyNotification", + }, + GetPolicyNotification: { + Name: "GetPolicyNotification", + Group: "PolicyNotification", + }, + GetStacks: { + Name: "GetStacks", + Group: "Stack", + }, + CreateStack: { + Name: "CreateStack", + Group: "Stack", + }, + CheckStackName: { + Name: "CheckStackName", + Group: "Stack", + }, + GetStack: { + Name: "GetStack", + Group: "Stack", + }, + UpdateStack: { + Name: "UpdateStack", + Group: "Stack", + }, + DeleteStack: { + Name: "DeleteStack", + Group: "Stack", + }, + GetStackKubeConfig: { + Name: "GetStackKubeConfig", + Group: "Stack", + }, + GetStackStatus: { + Name: "GetStackStatus", + Group: "Stack", + }, + SetFavoriteStack: { + Name: "SetFavoriteStack", + Group: "Stack", + }, + DeleteFavoriteStack: { + Name: "DeleteFavoriteStack", + Group: "Stack", + }, + InstallStack: { + Name: "InstallStack", + Group: "Stack", + }, + CreateProject: { + Name: "CreateProject", + Group: "Project", + }, + GetProjectRoles: { + Name: "GetProjectRoles", + Group: "Project", + }, + GetProjectRole: { + Name: "GetProjectRole", + Group: "Project", + }, + GetProjects: { + Name: "GetProjects", + Group: "Project", + }, + GetProject: { + Name: "GetProject", + Group: "Project", + }, + UpdateProject: { + Name: "UpdateProject", + Group: "Project", + }, + DeleteProject: { + Name: "DeleteProject", + Group: "Project", + }, + AddProjectMember: { + Name: "AddProjectMember", + Group: "Project", + }, + GetProjectMember: { + Name: "GetProjectMember", + Group: "Project", + }, + GetProjectMembers: { + Name: "GetProjectMembers", + Group: "Project", + }, + RemoveProjectMember: { + Name: "RemoveProjectMember", + Group: "Project", + }, + UpdateProjectMemberRole: { + Name: "UpdateProjectMemberRole", + Group: "Project", + }, + CreateProjectNamespace: { + Name: "CreateProjectNamespace", + Group: "Project", + }, + GetProjectNamespaces: { + Name: "GetProjectNamespaces", + Group: "Project", + }, + GetProjectNamespace: { + Name: "GetProjectNamespace", + Group: "Project", + }, + UpdateProjectNamespace: { + Name: "UpdateProjectNamespace", + Group: "Project", + }, + DeleteProjectNamespace: { + Name: "DeleteProjectNamespace", + Group: "Project", + }, + SetFavoriteProject: { + Name: "SetFavoriteProject", + Group: "Project", + }, + SetFavoriteProjectNamespace: { + Name: "SetFavoriteProjectNamespace", + Group: "Project", + }, + UnSetFavoriteProject: { + Name: "UnSetFavoriteProject", + Group: "Project", + }, + UnSetFavoriteProjectNamespace: { + Name: "UnSetFavoriteProjectNamespace", + Group: "Project", + }, + GetProjectKubeconfig: { + Name: "GetProjectKubeconfig", + Group: "Project", + }, + GetProjectNamespaceK8sResources: { + Name: "GetProjectNamespaceK8sResources", + Group: "Project", + }, + GetProjectNamespaceKubeconfig: { + Name: "GetProjectNamespaceKubeconfig", + Group: "Project", + }, + GetAudits: { + Name: "GetAudits", + Group: "Audit", + }, + GetAudit: { + Name: "GetAudit", + Group: "Audit", + }, + DeleteAudit: { + Name: "DeleteAudit", + Group: "Audit", + }, + CreateTksRole: { + Name: "CreateTksRole", + Group: "Role", + }, + ListTksRoles: { + Name: "ListTksRoles", + Group: "Role", + }, + GetTksRole: { + Name: "GetTksRole", + Group: "Role", + }, + DeleteTksRole: { + Name: "DeleteTksRole", + Group: "Role", + }, + UpdateTksRole: { + Name: "UpdateTksRole", + Group: "Role", + }, + GetPermissionsByRoleId: { + Name: "GetPermissionsByRoleId", + Group: "Role", + }, + UpdatePermissionsByRoleId: { + Name: "UpdatePermissionsByRoleId", + Group: "Role", + }, + IsRoleNameExisted: { + Name: "IsRoleNameExisted", + Group: "Role", + }, + AppendUsersToRole: { + Name: "AppendUsersToRole", + Group: "Role", + }, + GetUsersInRoleId: { + Name: "GetUsersInRoleId", + Group: "Role", + }, + RemoveUsersFromRole: { + Name: "RemoveUsersFromRole", + Group: "Role", + }, + GetPermissionTemplates: { + Name: "GetPermissionTemplates", + Group: "Permission", + }, + Admin_CreateUser: { + Name: "Admin_CreateUser", + Group: "Admin_User", + }, + Admin_ListUser: { + Name: "Admin_ListUser", + Group: "Admin_User", + }, + Admin_GetUser: { + Name: "Admin_GetUser", + Group: "Admin_User", + }, + Admin_DeleteUser: { + Name: "Admin_DeleteUser", + Group: "Admin_User", + }, + Admin_UpdateUser: { + Name: "Admin_UpdateUser", + Group: "Admin_User", + }, + Admin_ListTksRoles: { + Name: "Admin_ListTksRoles", + Group: "Admin Role", + }, + Admin_GetTksRole: { + Name: "Admin_GetTksRole", + Group: "Admin Role", + }, + Admin_GetProjects: { + Name: "Admin_GetProjects", + Group: "Admin Project", + }, + Admin_ListPolicyTemplate: { + Name: "Admin_ListPolicyTemplate", + Group: "PolicyTemplate", + }, + Admin_CreatePolicyTemplate: { + Name: "Admin_CreatePolicyTemplate", + Group: "PolicyTemplate", + }, + Admin_DeletePolicyTemplate: { + Name: "Admin_DeletePolicyTemplate", + Group: "PolicyTemplate", + }, + Admin_GetPolicyTemplate: { + Name: "Admin_GetPolicyTemplate", + Group: "PolicyTemplate", + }, + Admin_UpdatePolicyTemplate: { + Name: "Admin_UpdatePolicyTemplate", + Group: "PolicyTemplate", + }, + Admin_GetPolicyTemplateDeploy: { + Name: "Admin_GetPolicyTemplateDeploy", + Group: "PolicyTemplate", + }, + Admin_ListPolicyTemplateStatistics: { + Name: "Admin_ListPolicyTemplateStatistics", + Group: "PolicyTemplate", + }, + Admin_ListPolicyTemplateVersions: { + Name: "Admin_ListPolicyTemplateVersions", + Group: "PolicyTemplate", + }, + Admin_CreatePolicyTemplateVersion: { + Name: "Admin_CreatePolicyTemplateVersion", + Group: "PolicyTemplate", + }, + Admin_DeletePolicyTemplateVersion: { + Name: "Admin_DeletePolicyTemplateVersion", + Group: "PolicyTemplate", + }, + Admin_GetPolicyTemplateVersion: { + Name: "Admin_GetPolicyTemplateVersion", + Group: "PolicyTemplate", + }, + Admin_ExistsPolicyTemplateKind: { + Name: "Admin_ExistsPolicyTemplateKind", + Group: "PolicyTemplate", + }, + Admin_ExistsPolicyTemplateName: { + Name: "Admin_ExistsPolicyTemplateName", + Group: "PolicyTemplate", + }, + Admin_ExtractParameters: { + Name: "Admin_ExtractParameters", + Group: "PolicyTemplate", + }, + Admin_AddPermittedPolicyTemplatesForOrganization: { + Name: "Admin_AddPermittedPolicyTemplatesForOrganization", + Group: "PolicyTemplate", + }, + Admin_DeletePermittedPolicyTemplatesForOrganization: { + Name: "Admin_DeletePermittedPolicyTemplatesForOrganization", + Group: "PolicyTemplate", + }, + ListStackPolicyStatus: { + Name: "ListStackPolicyStatus", + Group: "StackPolicyStatus", + }, + GetStackPolicyTemplateStatus: { + Name: "GetStackPolicyTemplateStatus", + Group: "StackPolicyStatus", + }, + UpdateStackPolicyTemplateStatus: { + Name: "UpdateStackPolicyTemplateStatus", + Group: "StackPolicyStatus", + }, + GetMandatoryPolicies: { + Name: "GetMandatoryPolicies", + Group: "Policy", + }, + SetMandatoryPolicies: { + Name: "SetMandatoryPolicies", + Group: "Policy", + }, + GetPolicyStatistics: { + Name: "GetPolicyStatistics", + Group: "Policy", + }, + ListPolicy: { + Name: "ListPolicy", + Group: "Policy", + }, + CreatePolicy: { + Name: "CreatePolicy", + Group: "Policy", + }, + DeletePolicy: { + Name: "DeletePolicy", + Group: "Policy", + }, + GetPolicy: { + Name: "GetPolicy", + Group: "Policy", + }, + UpdatePolicy: { + Name: "UpdatePolicy", + Group: "Policy", + }, + UpdatePolicyTargetClusters: { + Name: "UpdatePolicyTargetClusters", + Group: "Policy", + }, + ExistsPolicyName: { + Name: "ExistsPolicyName", + Group: "Policy", + }, + ExistsPolicyResourceName: { + Name: "ExistsPolicyResourceName", + Group: "Policy", + }, + GetPolicyEdit: { + Name: "GetPolicyEdit", + Group: "Policy", + }, + AddPoliciesForStack: { + Name: "AddPoliciesForStack", + Group: "Policy", + }, + DeletePoliciesForStack: { + Name: "DeletePoliciesForStack", + Group: "Policy", + }, + StackPolicyStatistics: { + Name: "StackPolicyStatistics", + Group: "Policy", + }, + ListPolicyTemplate: { + Name: "ListPolicyTemplate", + Group: "OrganizationPolicyTemplate", + }, + CreatePolicyTemplate: { + Name: "CreatePolicyTemplate", + Group: "OrganizationPolicyTemplate", + }, + DeletePolicyTemplate: { + Name: "DeletePolicyTemplate", + Group: "OrganizationPolicyTemplate", + }, + GetPolicyTemplate: { + Name: "GetPolicyTemplate", + Group: "OrganizationPolicyTemplate", + }, + UpdatePolicyTemplate: { + Name: "UpdatePolicyTemplate", + Group: "OrganizationPolicyTemplate", + }, + GetPolicyTemplateDeploy: { + Name: "GetPolicyTemplateDeploy", + Group: "OrganizationPolicyTemplate", + }, + ListPolicyTemplateStatistics: { + Name: "ListPolicyTemplateStatistics", + Group: "OrganizationPolicyTemplate", + }, + ListPolicyTemplateVersions: { + Name: "ListPolicyTemplateVersions", + Group: "OrganizationPolicyTemplate", + }, + CreatePolicyTemplateVersion: { + Name: "CreatePolicyTemplateVersion", + Group: "OrganizationPolicyTemplate", + }, + DeletePolicyTemplateVersion: { + Name: "DeletePolicyTemplateVersion", + Group: "OrganizationPolicyTemplate", + }, + GetPolicyTemplateVersion: { + Name: "GetPolicyTemplateVersion", + Group: "OrganizationPolicyTemplate", + }, + ExistsPolicyTemplateKind: { + Name: "ExistsPolicyTemplateKind", + Group: "OrganizationPolicyTemplate", + }, + ExistsPolicyTemplateName: { + Name: "ExistsPolicyTemplateName", + Group: "OrganizationPolicyTemplate", + }, + ExtractParameters: { + Name: "ExtractParameters", + Group: "OrganizationPolicyTemplate", + }, + ListPolicyTemplateExample: { + Name: "ListPolicyTemplateExample", + Group: "PolicyTemplateExample", + }, + GetPolicyTemplateExample: { + Name: "GetPolicyTemplateExample", + Group: "PolicyTemplateExample", + }, + UpdatePolicyTemplateExample: { + Name: "UpdatePolicyTemplateExample", + Group: "PolicyTemplateExample", + }, + DeletePolicyTemplateExample: { + Name: "DeletePolicyTemplateExample", + Group: "PolicyTemplateExample", + }, + CompileRego: { + Name: "CompileRego", + Group: "Utility", + }, +} +func (e Endpoint) String() string { + switch e { + case Login: + return "Login" + case Logout: + return "Logout" + case RefreshToken: + return "RefreshToken" + case FindId: + return "FindId" + case FindPassword: + return "FindPassword" + case VerifyIdentityForLostId: + return "VerifyIdentityForLostId" + case VerifyIdentityForLostPassword: + return "VerifyIdentityForLostPassword" + case VerifyToken: + return "VerifyToken" + case CreateUser: + return "CreateUser" + case ListUser: + return "ListUser" + case GetUser: + return "GetUser" + case DeleteUser: + return "DeleteUser" + case UpdateUsers: + return "UpdateUsers" + case UpdateUser: + return "UpdateUser" + case ResetPassword: + return "ResetPassword" + case CheckId: + return "CheckId" + case CheckEmail: + return "CheckEmail" + case GetPermissionsByAccountId: + return "GetPermissionsByAccountId" + case GetMyProfile: + return "GetMyProfile" + case UpdateMyProfile: + return "UpdateMyProfile" + case UpdateMyPassword: + return "UpdateMyPassword" + case RenewPasswordExpiredDate: + return "RenewPasswordExpiredDate" + case DeleteMyProfile: + return "DeleteMyProfile" + case Admin_CreateOrganization: + return "Admin_CreateOrganization" + case Admin_DeleteOrganization: + return "Admin_DeleteOrganization" + case GetOrganizations: + return "GetOrganizations" + case GetOrganization: + return "GetOrganization" + case CheckOrganizationName: + return "CheckOrganizationName" + case UpdateOrganization: + return "UpdateOrganization" + case UpdatePrimaryCluster: + return "UpdatePrimaryCluster" + case CreateCluster: + return "CreateCluster" + case GetClusters: + return "GetClusters" + case ImportCluster: + return "ImportCluster" + case GetCluster: + return "GetCluster" + case DeleteCluster: + return "DeleteCluster" + case GetClusterSiteValues: + return "GetClusterSiteValues" + case InstallCluster: + return "InstallCluster" + case CreateBootstrapKubeconfig: + return "CreateBootstrapKubeconfig" + case GetBootstrapKubeconfig: + return "GetBootstrapKubeconfig" + case GetNodes: + return "GetNodes" + case CreateAppgroup: + return "CreateAppgroup" + case GetAppgroups: + return "GetAppgroups" + case GetAppgroup: + return "GetAppgroup" + case DeleteAppgroup: + return "DeleteAppgroup" + case GetApplications: + return "GetApplications" + case CreateApplication: + return "CreateApplication" + case GetAppServeAppTasksByAppId: + return "GetAppServeAppTasksByAppId" + case GetAppServeAppTaskDetail: + return "GetAppServeAppTaskDetail" + case CreateAppServeApp: + return "CreateAppServeApp" + case GetAppServeApps: + return "GetAppServeApps" + case GetNumOfAppsOnStack: + return "GetNumOfAppsOnStack" + case GetAppServeApp: + return "GetAppServeApp" + case GetAppServeAppLatestTask: + return "GetAppServeAppLatestTask" + case IsAppServeAppExist: + return "IsAppServeAppExist" + case IsAppServeAppNameExist: + return "IsAppServeAppNameExist" + case DeleteAppServeApp: + return "DeleteAppServeApp" + case UpdateAppServeApp: + return "UpdateAppServeApp" + case UpdateAppServeAppStatus: + return "UpdateAppServeAppStatus" + case UpdateAppServeAppEndpoint: + return "UpdateAppServeAppEndpoint" + case RollbackAppServeApp: + return "RollbackAppServeApp" + case GetCloudAccounts: + return "GetCloudAccounts" + case CreateCloudAccount: + return "CreateCloudAccount" + case CheckCloudAccountName: + return "CheckCloudAccountName" + case CheckAwsAccountId: + return "CheckAwsAccountId" + case GetCloudAccount: + return "GetCloudAccount" + case UpdateCloudAccount: + return "UpdateCloudAccount" + case DeleteCloudAccount: + return "DeleteCloudAccount" + case DeleteForceCloudAccount: + return "DeleteForceCloudAccount" + case GetResourceQuota: + return "GetResourceQuota" + case Admin_GetStackTemplates: + return "Admin_GetStackTemplates" + case Admin_GetStackTemplate: + return "Admin_GetStackTemplate" + case Admin_GetStackTemplateServices: + return "Admin_GetStackTemplateServices" + case Admin_GetStackTemplateTemplateIds: + return "Admin_GetStackTemplateTemplateIds" + case Admin_CreateStackTemplate: + return "Admin_CreateStackTemplate" + case Admin_UpdateStackTemplate: + return "Admin_UpdateStackTemplate" + case Admin_DeleteStackTemplate: + return "Admin_DeleteStackTemplate" + case Admin_UpdateStackTemplateOrganizations: + return "Admin_UpdateStackTemplateOrganizations" + case Admin_CheckStackTemplateName: + return "Admin_CheckStackTemplateName" + case GetOrganizationStackTemplates: + return "GetOrganizationStackTemplates" + case GetOrganizationStackTemplate: + return "GetOrganizationStackTemplate" + case AddOrganizationStackTemplates: + return "AddOrganizationStackTemplates" + case RemoveOrganizationStackTemplates: + return "RemoveOrganizationStackTemplates" + case CreateDashboard: + return "CreateDashboard" + case GetDashboard: + return "GetDashboard" + case UpdateDashboard: + return "UpdateDashboard" + case GetChartsDashboard: + return "GetChartsDashboard" + case GetChartDashboard: + return "GetChartDashboard" + case GetStacksDashboard: + return "GetStacksDashboard" + case GetResourcesDashboard: + return "GetResourcesDashboard" + case GetPolicyStatusDashboard: + return "GetPolicyStatusDashboard" + case GetPolicyUpdateDashboard: + return "GetPolicyUpdateDashboard" + case GetPolicyEnforcementDashboard: + return "GetPolicyEnforcementDashboard" + case GetPolicyViolationDashboard: + return "GetPolicyViolationDashboard" + case GetPolicyViolationLogDashboard: + return "GetPolicyViolationLogDashboard" + case GetPolicyStatisticsDashboard: + return "GetPolicyStatisticsDashboard" + case GetWorkloadDashboard: + return "GetWorkloadDashboard" + case GetPolicyViolationTop5Dashboard: + return "GetPolicyViolationTop5Dashboard" + case Admin_CreateSystemNotificationTemplate: + return "Admin_CreateSystemNotificationTemplate" + case Admin_UpdateSystemNotificationTemplate: + return "Admin_UpdateSystemNotificationTemplate" + case Admin_DeleteSystemNotificationTemplate: + return "Admin_DeleteSystemNotificationTemplate" + case Admin_GetSystemNotificationTemplates: + return "Admin_GetSystemNotificationTemplates" + case Admin_GetSystemNotificationTemplate: + return "Admin_GetSystemNotificationTemplate" + case Admin_CheckSystemNotificationTemplateName: + return "Admin_CheckSystemNotificationTemplateName" + case GetOrganizationSystemNotificationTemplates: + return "GetOrganizationSystemNotificationTemplates" + case GetOrganizationSystemNotificationTemplate: + return "GetOrganizationSystemNotificationTemplate" + case AddOrganizationSystemNotificationTemplates: + return "AddOrganizationSystemNotificationTemplates" + case RemoveOrganizationSystemNotificationTemplates: + return "RemoveOrganizationSystemNotificationTemplates" + case CreateSystemNotificationRule: + return "CreateSystemNotificationRule" + case GetSystemNotificationRules: + return "GetSystemNotificationRules" + case GetSystemNotificationRule: + return "GetSystemNotificationRule" + case CheckSystemNotificationRuleName: + return "CheckSystemNotificationRuleName" + case DeleteSystemNotificationRule: + return "DeleteSystemNotificationRule" + case UpdateSystemNotificationRule: + return "UpdateSystemNotificationRule" + case MakeDefaultSystemNotificationRules: + return "MakeDefaultSystemNotificationRules" + case CreateSystemNotification: + return "CreateSystemNotification" + case GetSystemNotifications: + return "GetSystemNotifications" + case GetSystemNotification: + return "GetSystemNotification" + case DeleteSystemNotification: + return "DeleteSystemNotification" + case UpdateSystemNotification: + return "UpdateSystemNotification" + case CreateSystemNotificationAction: + return "CreateSystemNotificationAction" + case GetPolicyNotifications: + return "GetPolicyNotifications" + case GetPolicyNotification: + return "GetPolicyNotification" + case GetStacks: + return "GetStacks" + case CreateStack: + return "CreateStack" + case CheckStackName: + return "CheckStackName" + case GetStack: + return "GetStack" + case UpdateStack: + return "UpdateStack" + case DeleteStack: + return "DeleteStack" + case GetStackKubeConfig: + return "GetStackKubeConfig" + case GetStackStatus: + return "GetStackStatus" + case SetFavoriteStack: + return "SetFavoriteStack" + case DeleteFavoriteStack: + return "DeleteFavoriteStack" + case InstallStack: + return "InstallStack" + case CreateProject: + return "CreateProject" + case GetProjectRoles: + return "GetProjectRoles" + case GetProjectRole: + return "GetProjectRole" + case GetProjects: + return "GetProjects" + case GetProject: + return "GetProject" + case UpdateProject: + return "UpdateProject" + case DeleteProject: + return "DeleteProject" + case AddProjectMember: + return "AddProjectMember" + case GetProjectMember: + return "GetProjectMember" + case GetProjectMembers: + return "GetProjectMembers" + case RemoveProjectMember: + return "RemoveProjectMember" + case UpdateProjectMemberRole: + return "UpdateProjectMemberRole" + case CreateProjectNamespace: + return "CreateProjectNamespace" + case GetProjectNamespaces: + return "GetProjectNamespaces" + case GetProjectNamespace: + return "GetProjectNamespace" + case UpdateProjectNamespace: + return "UpdateProjectNamespace" + case DeleteProjectNamespace: + return "DeleteProjectNamespace" + case SetFavoriteProject: + return "SetFavoriteProject" + case SetFavoriteProjectNamespace: + return "SetFavoriteProjectNamespace" + case UnSetFavoriteProject: + return "UnSetFavoriteProject" + case UnSetFavoriteProjectNamespace: + return "UnSetFavoriteProjectNamespace" + case GetProjectKubeconfig: + return "GetProjectKubeconfig" + case GetProjectNamespaceK8sResources: + return "GetProjectNamespaceK8sResources" + case GetProjectNamespaceKubeconfig: + return "GetProjectNamespaceKubeconfig" + case GetAudits: + return "GetAudits" + case GetAudit: + return "GetAudit" + case DeleteAudit: + return "DeleteAudit" + case CreateTksRole: + return "CreateTksRole" + case ListTksRoles: + return "ListTksRoles" + case GetTksRole: + return "GetTksRole" + case DeleteTksRole: + return "DeleteTksRole" + case UpdateTksRole: + return "UpdateTksRole" + case GetPermissionsByRoleId: + return "GetPermissionsByRoleId" + case UpdatePermissionsByRoleId: + return "UpdatePermissionsByRoleId" + case IsRoleNameExisted: + return "IsRoleNameExisted" + case AppendUsersToRole: + return "AppendUsersToRole" + case GetUsersInRoleId: + return "GetUsersInRoleId" + case RemoveUsersFromRole: + return "RemoveUsersFromRole" + case GetPermissionTemplates: + return "GetPermissionTemplates" + case Admin_CreateUser: + return "Admin_CreateUser" + case Admin_ListUser: + return "Admin_ListUser" + case Admin_GetUser: + return "Admin_GetUser" + case Admin_DeleteUser: + return "Admin_DeleteUser" + case Admin_UpdateUser: + return "Admin_UpdateUser" + case Admin_ListTksRoles: + return "Admin_ListTksRoles" + case Admin_GetTksRole: + return "Admin_GetTksRole" + case Admin_GetProjects: + return "Admin_GetProjects" + case Admin_ListPolicyTemplate: + return "Admin_ListPolicyTemplate" + case Admin_CreatePolicyTemplate: + return "Admin_CreatePolicyTemplate" + case Admin_DeletePolicyTemplate: + return "Admin_DeletePolicyTemplate" + case Admin_GetPolicyTemplate: + return "Admin_GetPolicyTemplate" + case Admin_UpdatePolicyTemplate: + return "Admin_UpdatePolicyTemplate" + case Admin_GetPolicyTemplateDeploy: + return "Admin_GetPolicyTemplateDeploy" + case Admin_ListPolicyTemplateStatistics: + return "Admin_ListPolicyTemplateStatistics" + case Admin_ListPolicyTemplateVersions: + return "Admin_ListPolicyTemplateVersions" + case Admin_CreatePolicyTemplateVersion: + return "Admin_CreatePolicyTemplateVersion" + case Admin_DeletePolicyTemplateVersion: + return "Admin_DeletePolicyTemplateVersion" + case Admin_GetPolicyTemplateVersion: + return "Admin_GetPolicyTemplateVersion" + case Admin_ExistsPolicyTemplateKind: + return "Admin_ExistsPolicyTemplateKind" + case Admin_ExistsPolicyTemplateName: + return "Admin_ExistsPolicyTemplateName" + case Admin_ExtractParameters: + return "Admin_ExtractParameters" + case Admin_AddPermittedPolicyTemplatesForOrganization: + return "Admin_AddPermittedPolicyTemplatesForOrganization" + case Admin_DeletePermittedPolicyTemplatesForOrganization: + return "Admin_DeletePermittedPolicyTemplatesForOrganization" + case ListStackPolicyStatus: + return "ListStackPolicyStatus" + case GetStackPolicyTemplateStatus: + return "GetStackPolicyTemplateStatus" + case UpdateStackPolicyTemplateStatus: + return "UpdateStackPolicyTemplateStatus" + case GetMandatoryPolicies: + return "GetMandatoryPolicies" + case SetMandatoryPolicies: + return "SetMandatoryPolicies" + case GetPolicyStatistics: + return "GetPolicyStatistics" + case ListPolicy: + return "ListPolicy" + case CreatePolicy: + return "CreatePolicy" + case DeletePolicy: + return "DeletePolicy" + case GetPolicy: + return "GetPolicy" + case UpdatePolicy: + return "UpdatePolicy" + case UpdatePolicyTargetClusters: + return "UpdatePolicyTargetClusters" + case ExistsPolicyName: + return "ExistsPolicyName" + case ExistsPolicyResourceName: + return "ExistsPolicyResourceName" + case GetPolicyEdit: + return "GetPolicyEdit" + case AddPoliciesForStack: + return "AddPoliciesForStack" + case DeletePoliciesForStack: + return "DeletePoliciesForStack" + case StackPolicyStatistics: + return "StackPolicyStatistics" + case ListPolicyTemplate: + return "ListPolicyTemplate" + case CreatePolicyTemplate: + return "CreatePolicyTemplate" + case DeletePolicyTemplate: + return "DeletePolicyTemplate" + case GetPolicyTemplate: + return "GetPolicyTemplate" + case UpdatePolicyTemplate: + return "UpdatePolicyTemplate" + case GetPolicyTemplateDeploy: + return "GetPolicyTemplateDeploy" + case ListPolicyTemplateStatistics: + return "ListPolicyTemplateStatistics" + case ListPolicyTemplateVersions: + return "ListPolicyTemplateVersions" + case CreatePolicyTemplateVersion: + return "CreatePolicyTemplateVersion" + case DeletePolicyTemplateVersion: + return "DeletePolicyTemplateVersion" + case GetPolicyTemplateVersion: + return "GetPolicyTemplateVersion" + case ExistsPolicyTemplateKind: + return "ExistsPolicyTemplateKind" + case ExistsPolicyTemplateName: + return "ExistsPolicyTemplateName" + case ExtractParameters: + return "ExtractParameters" + case ListPolicyTemplateExample: + return "ListPolicyTemplateExample" + case GetPolicyTemplateExample: + return "GetPolicyTemplateExample" + case UpdatePolicyTemplateExample: + return "UpdatePolicyTemplateExample" + case DeletePolicyTemplateExample: + return "DeletePolicyTemplateExample" + case CompileRego: + return "CompileRego" + default: + return "" + } +} +func GetEndpoint(name string) Endpoint { + switch name { + case "Login": + return Login + case "Logout": + return Logout + case "RefreshToken": + return RefreshToken + case "FindId": + return FindId + case "FindPassword": + return FindPassword + case "VerifyIdentityForLostId": + return VerifyIdentityForLostId + case "VerifyIdentityForLostPassword": + return VerifyIdentityForLostPassword + case "VerifyToken": + return VerifyToken + case "CreateUser": + return CreateUser + case "ListUser": + return ListUser + case "GetUser": + return GetUser + case "DeleteUser": + return DeleteUser + case "UpdateUsers": + return UpdateUsers + case "UpdateUser": + return UpdateUser + case "ResetPassword": + return ResetPassword + case "CheckId": + return CheckId + case "CheckEmail": + return CheckEmail + case "GetPermissionsByAccountId": + return GetPermissionsByAccountId + case "GetMyProfile": + return GetMyProfile + case "UpdateMyProfile": + return UpdateMyProfile + case "UpdateMyPassword": + return UpdateMyPassword + case "RenewPasswordExpiredDate": + return RenewPasswordExpiredDate + case "DeleteMyProfile": + return DeleteMyProfile + case "Admin_CreateOrganization": + return Admin_CreateOrganization + case "Admin_DeleteOrganization": + return Admin_DeleteOrganization + case "GetOrganizations": + return GetOrganizations + case "GetOrganization": + return GetOrganization + case "CheckOrganizationName": + return CheckOrganizationName + case "UpdateOrganization": + return UpdateOrganization + case "UpdatePrimaryCluster": + return UpdatePrimaryCluster + case "CreateCluster": + return CreateCluster + case "GetClusters": + return GetClusters + case "ImportCluster": + return ImportCluster + case "GetCluster": + return GetCluster + case "DeleteCluster": + return DeleteCluster + case "GetClusterSiteValues": + return GetClusterSiteValues + case "InstallCluster": + return InstallCluster + case "CreateBootstrapKubeconfig": + return CreateBootstrapKubeconfig + case "GetBootstrapKubeconfig": + return GetBootstrapKubeconfig + case "GetNodes": + return GetNodes + case "CreateAppgroup": + return CreateAppgroup + case "GetAppgroups": + return GetAppgroups + case "GetAppgroup": + return GetAppgroup + case "DeleteAppgroup": + return DeleteAppgroup + case "GetApplications": + return GetApplications + case "CreateApplication": + return CreateApplication + case "GetAppServeAppTasksByAppId": + return GetAppServeAppTasksByAppId + case "GetAppServeAppTaskDetail": + return GetAppServeAppTaskDetail + case "CreateAppServeApp": + return CreateAppServeApp + case "GetAppServeApps": + return GetAppServeApps + case "GetNumOfAppsOnStack": + return GetNumOfAppsOnStack + case "GetAppServeApp": + return GetAppServeApp + case "GetAppServeAppLatestTask": + return GetAppServeAppLatestTask + case "IsAppServeAppExist": + return IsAppServeAppExist + case "IsAppServeAppNameExist": + return IsAppServeAppNameExist + case "DeleteAppServeApp": + return DeleteAppServeApp + case "UpdateAppServeApp": + return UpdateAppServeApp + case "UpdateAppServeAppStatus": + return UpdateAppServeAppStatus + case "UpdateAppServeAppEndpoint": + return UpdateAppServeAppEndpoint + case "RollbackAppServeApp": + return RollbackAppServeApp + case "GetCloudAccounts": + return GetCloudAccounts + case "CreateCloudAccount": + return CreateCloudAccount + case "CheckCloudAccountName": + return CheckCloudAccountName + case "CheckAwsAccountId": + return CheckAwsAccountId + case "GetCloudAccount": + return GetCloudAccount + case "UpdateCloudAccount": + return UpdateCloudAccount + case "DeleteCloudAccount": + return DeleteCloudAccount + case "DeleteForceCloudAccount": + return DeleteForceCloudAccount + case "GetResourceQuota": + return GetResourceQuota + case "Admin_GetStackTemplates": + return Admin_GetStackTemplates + case "Admin_GetStackTemplate": + return Admin_GetStackTemplate + case "Admin_GetStackTemplateServices": + return Admin_GetStackTemplateServices + case "Admin_GetStackTemplateTemplateIds": + return Admin_GetStackTemplateTemplateIds + case "Admin_CreateStackTemplate": + return Admin_CreateStackTemplate + case "Admin_UpdateStackTemplate": + return Admin_UpdateStackTemplate + case "Admin_DeleteStackTemplate": + return Admin_DeleteStackTemplate + case "Admin_UpdateStackTemplateOrganizations": + return Admin_UpdateStackTemplateOrganizations + case "Admin_CheckStackTemplateName": + return Admin_CheckStackTemplateName + case "GetOrganizationStackTemplates": + return GetOrganizationStackTemplates + case "GetOrganizationStackTemplate": + return GetOrganizationStackTemplate + case "AddOrganizationStackTemplates": + return AddOrganizationStackTemplates + case "RemoveOrganizationStackTemplates": + return RemoveOrganizationStackTemplates + case "CreateDashboard": + return CreateDashboard + case "GetDashboard": + return GetDashboard + case "UpdateDashboard": + return UpdateDashboard + case "GetChartsDashboard": + return GetChartsDashboard + case "GetChartDashboard": + return GetChartDashboard + case "GetStacksDashboard": + return GetStacksDashboard + case "GetResourcesDashboard": + return GetResourcesDashboard + case "GetPolicyStatusDashboard": + return GetPolicyStatusDashboard + case "GetPolicyUpdateDashboard": + return GetPolicyUpdateDashboard + case "GetPolicyEnforcementDashboard": + return GetPolicyEnforcementDashboard + case "GetPolicyViolationDashboard": + return GetPolicyViolationDashboard + case "GetPolicyViolationLogDashboard": + return GetPolicyViolationLogDashboard + case "GetPolicyStatisticsDashboard": + return GetPolicyStatisticsDashboard + case "GetWorkloadDashboard": + return GetWorkloadDashboard + case "GetPolicyViolationTop5Dashboard": + return GetPolicyViolationTop5Dashboard + case "Admin_CreateSystemNotificationTemplate": + return Admin_CreateSystemNotificationTemplate + case "Admin_UpdateSystemNotificationTemplate": + return Admin_UpdateSystemNotificationTemplate + case "Admin_DeleteSystemNotificationTemplate": + return Admin_DeleteSystemNotificationTemplate + case "Admin_GetSystemNotificationTemplates": + return Admin_GetSystemNotificationTemplates + case "Admin_GetSystemNotificationTemplate": + return Admin_GetSystemNotificationTemplate + case "Admin_CheckSystemNotificationTemplateName": + return Admin_CheckSystemNotificationTemplateName + case "GetOrganizationSystemNotificationTemplates": + return GetOrganizationSystemNotificationTemplates + case "GetOrganizationSystemNotificationTemplate": + return GetOrganizationSystemNotificationTemplate + case "AddOrganizationSystemNotificationTemplates": + return AddOrganizationSystemNotificationTemplates + case "RemoveOrganizationSystemNotificationTemplates": + return RemoveOrganizationSystemNotificationTemplates + case "CreateSystemNotificationRule": + return CreateSystemNotificationRule + case "GetSystemNotificationRules": + return GetSystemNotificationRules + case "GetSystemNotificationRule": + return GetSystemNotificationRule + case "CheckSystemNotificationRuleName": + return CheckSystemNotificationRuleName + case "DeleteSystemNotificationRule": + return DeleteSystemNotificationRule + case "UpdateSystemNotificationRule": + return UpdateSystemNotificationRule + case "MakeDefaultSystemNotificationRules": + return MakeDefaultSystemNotificationRules + case "CreateSystemNotification": + return CreateSystemNotification + case "GetSystemNotifications": + return GetSystemNotifications + case "GetSystemNotification": + return GetSystemNotification + case "DeleteSystemNotification": + return DeleteSystemNotification + case "UpdateSystemNotification": + return UpdateSystemNotification + case "CreateSystemNotificationAction": + return CreateSystemNotificationAction + case "GetPolicyNotifications": + return GetPolicyNotifications + case "GetPolicyNotification": + return GetPolicyNotification + case "GetStacks": + return GetStacks + case "CreateStack": + return CreateStack + case "CheckStackName": + return CheckStackName + case "GetStack": + return GetStack + case "UpdateStack": + return UpdateStack + case "DeleteStack": + return DeleteStack + case "GetStackKubeConfig": + return GetStackKubeConfig + case "GetStackStatus": + return GetStackStatus + case "SetFavoriteStack": + return SetFavoriteStack + case "DeleteFavoriteStack": + return DeleteFavoriteStack + case "InstallStack": + return InstallStack + case "CreateProject": + return CreateProject + case "GetProjectRoles": + return GetProjectRoles + case "GetProjectRole": + return GetProjectRole + case "GetProjects": + return GetProjects + case "GetProject": + return GetProject + case "UpdateProject": + return UpdateProject + case "DeleteProject": + return DeleteProject + case "AddProjectMember": + return AddProjectMember + case "GetProjectMember": + return GetProjectMember + case "GetProjectMembers": + return GetProjectMembers + case "RemoveProjectMember": + return RemoveProjectMember + case "UpdateProjectMemberRole": + return UpdateProjectMemberRole + case "CreateProjectNamespace": + return CreateProjectNamespace + case "GetProjectNamespaces": + return GetProjectNamespaces + case "GetProjectNamespace": + return GetProjectNamespace + case "UpdateProjectNamespace": + return UpdateProjectNamespace + case "DeleteProjectNamespace": + return DeleteProjectNamespace + case "SetFavoriteProject": + return SetFavoriteProject + case "SetFavoriteProjectNamespace": + return SetFavoriteProjectNamespace + case "UnSetFavoriteProject": + return UnSetFavoriteProject + case "UnSetFavoriteProjectNamespace": + return UnSetFavoriteProjectNamespace + case "GetProjectKubeconfig": + return GetProjectKubeconfig + case "GetProjectNamespaceK8sResources": + return GetProjectNamespaceK8sResources + case "GetProjectNamespaceKubeconfig": + return GetProjectNamespaceKubeconfig + case "GetAudits": + return GetAudits + case "GetAudit": + return GetAudit + case "DeleteAudit": + return DeleteAudit + case "CreateTksRole": + return CreateTksRole + case "ListTksRoles": + return ListTksRoles + case "GetTksRole": + return GetTksRole + case "DeleteTksRole": + return DeleteTksRole + case "UpdateTksRole": + return UpdateTksRole + case "GetPermissionsByRoleId": + return GetPermissionsByRoleId + case "UpdatePermissionsByRoleId": + return UpdatePermissionsByRoleId + case "IsRoleNameExisted": + return IsRoleNameExisted + case "AppendUsersToRole": + return AppendUsersToRole + case "GetUsersInRoleId": + return GetUsersInRoleId + case "RemoveUsersFromRole": + return RemoveUsersFromRole + case "GetPermissionTemplates": + return GetPermissionTemplates + case "Admin_CreateUser": + return Admin_CreateUser + case "Admin_ListUser": + return Admin_ListUser + case "Admin_GetUser": + return Admin_GetUser + case "Admin_DeleteUser": + return Admin_DeleteUser + case "Admin_UpdateUser": + return Admin_UpdateUser + case "Admin_ListTksRoles": + return Admin_ListTksRoles + case "Admin_GetTksRole": + return Admin_GetTksRole + case "Admin_GetProjects": + return Admin_GetProjects + case "Admin_ListPolicyTemplate": + return Admin_ListPolicyTemplate + case "Admin_CreatePolicyTemplate": + return Admin_CreatePolicyTemplate + case "Admin_DeletePolicyTemplate": + return Admin_DeletePolicyTemplate + case "Admin_GetPolicyTemplate": + return Admin_GetPolicyTemplate + case "Admin_UpdatePolicyTemplate": + return Admin_UpdatePolicyTemplate + case "Admin_GetPolicyTemplateDeploy": + return Admin_GetPolicyTemplateDeploy + case "Admin_ListPolicyTemplateStatistics": + return Admin_ListPolicyTemplateStatistics + case "Admin_ListPolicyTemplateVersions": + return Admin_ListPolicyTemplateVersions + case "Admin_CreatePolicyTemplateVersion": + return Admin_CreatePolicyTemplateVersion + case "Admin_DeletePolicyTemplateVersion": + return Admin_DeletePolicyTemplateVersion + case "Admin_GetPolicyTemplateVersion": + return Admin_GetPolicyTemplateVersion + case "Admin_ExistsPolicyTemplateKind": + return Admin_ExistsPolicyTemplateKind + case "Admin_ExistsPolicyTemplateName": + return Admin_ExistsPolicyTemplateName + case "Admin_ExtractParameters": + return Admin_ExtractParameters + case "Admin_AddPermittedPolicyTemplatesForOrganization": + return Admin_AddPermittedPolicyTemplatesForOrganization + case "Admin_DeletePermittedPolicyTemplatesForOrganization": + return Admin_DeletePermittedPolicyTemplatesForOrganization + case "ListStackPolicyStatus": + return ListStackPolicyStatus + case "GetStackPolicyTemplateStatus": + return GetStackPolicyTemplateStatus + case "UpdateStackPolicyTemplateStatus": + return UpdateStackPolicyTemplateStatus + case "GetMandatoryPolicies": + return GetMandatoryPolicies + case "SetMandatoryPolicies": + return SetMandatoryPolicies + case "GetPolicyStatistics": + return GetPolicyStatistics + case "ListPolicy": + return ListPolicy + case "CreatePolicy": + return CreatePolicy + case "DeletePolicy": + return DeletePolicy + case "GetPolicy": + return GetPolicy + case "UpdatePolicy": + return UpdatePolicy + case "UpdatePolicyTargetClusters": + return UpdatePolicyTargetClusters + case "ExistsPolicyName": + return ExistsPolicyName + case "ExistsPolicyResourceName": + return ExistsPolicyResourceName + case "GetPolicyEdit": + return GetPolicyEdit + case "AddPoliciesForStack": + return AddPoliciesForStack + case "DeletePoliciesForStack": + return DeletePoliciesForStack + case "StackPolicyStatistics": + return StackPolicyStatistics + case "ListPolicyTemplate": + return ListPolicyTemplate + case "CreatePolicyTemplate": + return CreatePolicyTemplate + case "DeletePolicyTemplate": + return DeletePolicyTemplate + case "GetPolicyTemplate": + return GetPolicyTemplate + case "UpdatePolicyTemplate": + return UpdatePolicyTemplate + case "GetPolicyTemplateDeploy": + return GetPolicyTemplateDeploy + case "ListPolicyTemplateStatistics": + return ListPolicyTemplateStatistics + case "ListPolicyTemplateVersions": + return ListPolicyTemplateVersions + case "CreatePolicyTemplateVersion": + return CreatePolicyTemplateVersion + case "DeletePolicyTemplateVersion": + return DeletePolicyTemplateVersion + case "GetPolicyTemplateVersion": + return GetPolicyTemplateVersion + case "ExistsPolicyTemplateKind": + return ExistsPolicyTemplateKind + case "ExistsPolicyTemplateName": + return ExistsPolicyTemplateName + case "ExtractParameters": + return ExtractParameters + case "ListPolicyTemplateExample": + return ListPolicyTemplateExample + case "GetPolicyTemplateExample": + return GetPolicyTemplateExample + case "UpdatePolicyTemplateExample": + return UpdatePolicyTemplateExample + case "DeletePolicyTemplateExample": + return DeletePolicyTemplateExample + case "CompileRego": + return CompileRego + default: + return -1 + } +} diff --git a/internal/delivery/http/alert.go b/internal/delivery/http/alert.go deleted file mode 100644 index 5568af51..00000000 --- a/internal/delivery/http/alert.go +++ /dev/null @@ -1,297 +0,0 @@ -package http - -import ( - "fmt" - "io" - "net/http" - - "github.com/google/uuid" - "github.com/gorilla/mux" - "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" - "github.com/openinfradev/tks-api/internal/usecase" - "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/httpErrors" - "github.com/openinfradev/tks-api/pkg/log" - "github.com/pkg/errors" -) - -type AlertHandler struct { - usecase usecase.IAlertUsecase -} - -func NewAlertHandler(h usecase.IAlertUsecase) *AlertHandler { - return &AlertHandler{ - usecase: h, - } -} - -// CreateAlert godoc -// @Tags Alerts -// @Summary Create alert. ADMIN ONLY -// @Description Create alert. ADMIN ONLY -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} nil -// @Router /system-api/organizations/{organizationId}/alerts [post] -// @Security JWT -func (h *AlertHandler) CreateAlert(w http.ResponseWriter, r *http.Request) { - - /* - INFO[2023-04-26 18:14:11] body : {"receiver":"webhook-alert","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"TestAlert1"},"annotations":{},"startsAt":"2023-04-26T09:14:01.489894015Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":"0dafe30dffce9487"}],"groupLabels":{"alertname":"TestAlert1"},"commonLabels":{"alertname":"TestAlert1"},"commonAnnotations":{},"externalURL":"http://lma-alertmanager.lma:9093","version":"4","groupKey":"{}:{alertname=\"TestAlert1\"}","truncatedAlerts":0} - INFO[2023-04-26 18:14:11] {"receiver":"webhook-alert","status":"firing","alerts":[{"status":"firing","labels":{"alertname":"TestAlert1"},"annotations":{},"startsAt":"2023-04-26T09:14:01.489894015Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":"0dafe30dffce9487"}],"groupLabels":{"alertname":"TestAlert1"},"commonLabels":{"alertname":"TestAlert1"},"commonAnnotations":{},"externalURL":"http://lma-alertmanager.lma:9093","version":"4","groupKey":"{}:{alertname=\"TestAlert1\"}","truncatedAlerts":0} - */ - - /* - // webhook 으로 부터 받은 body parse - bodyBytes, err := io.ReadAll(r.Body) - if err != nil { - log.ErrorWithContext(r.Context(),err) - } - bodyString := string(bodyBytes) - log.InfoWithContext(r.Context(),bodyString) - */ - - // 외부로부터(alert manager) 오는 데이터이므로, dto 변환없이 by-pass 처리한다. - input := domain.CreateAlertRequest{} - err := UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, err) - return - } - - err = h.usecase.Create(r.Context(), input) - if err != nil { - ErrorJSON(w, r, err) - return - } - - ResponseJSON(w, r, http.StatusOK, nil) -} - -// GetAlert godoc -// @Tags Alerts -// @Summary Get Alerts -// @Description Get Alerts -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} domain.GetAlertsResponse -// @Router /organizations/{organizationId}/alerts [get] -// @Security JWT -func (h *AlertHandler) GetAlerts(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - organizationId, ok := vars["organizationId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")) - return - } - - urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } - // convert status - for i, filter := range pg.GetFilters() { - if filter.Column == "status" { - for j, value := range filter.Values { - switch value { - case "CREATED": - pg.GetFilters()[i].Values[j] = "0" - case "INPROGRESS": - pg.GetFilters()[i].Values[j] = "1" - case "CLOSED": - pg.GetFilters()[i].Values[j] = "2" - case "ERROR": - pg.GetFilters()[i].Values[j] = "3" - } - } - } - } - - alerts, err := h.usecase.Fetch(r.Context(), organizationId, pg) - if err != nil { - ErrorJSON(w, r, err) - return - } - - var out domain.GetAlertsResponse - out.Alerts = make([]domain.AlertResponse, len(alerts)) - for i, alert := range alerts { - if err := serializer.Map(alert, &out.Alerts[i]); err != nil { - log.InfoWithContext(r.Context(), err) - } - - outAlertActions := make([]domain.AlertActionResponse, len(alert.AlertActions)) - for j, alertAction := range alert.AlertActions { - if err := serializer.Map(alertAction, &outAlertActions[j]); err != nil { - log.InfoWithContext(r.Context(), err) - } - } - out.Alerts[i].AlertActions = outAlertActions - if len(outAlertActions) > 0 { - out.Alerts[i].LastTaker = outAlertActions[0].Taker - } - } - - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) - } - /* - outFilters := make([]domain.FilterResponse, len(pg.Filters)) - for j, filter := range pg.Filters { - if err := serializer.Map(filter, &outFilters[j]); err != nil { - log.InfoWithContext(r.Context(), err) - } - } - out.Pagination.Filters = outFilters - */ - - ResponseJSON(w, r, http.StatusOK, out) -} - -// GetAlert godoc -// @Tags Alerts -// @Summary Get Alert -// @Description Get Alert -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param alertId path string true "alertId" -// @Success 200 {object} domain.GetAlertResponse -// @Router /organizations/{organizationId}/alerts/{alertId} [get] -// @Security JWT -func (h *AlertHandler) GetAlert(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - strId, ok := vars["alertId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid alertId"), "", "")) - return - } - - alertId, err := uuid.Parse(strId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) - return - } - - alert, err := h.usecase.Get(r.Context(), alertId) - if err != nil { - ErrorJSON(w, r, err) - return - } - - var out domain.GetAlertResponse - if err := serializer.Map(alert, &out.Alert); err != nil { - log.InfoWithContext(r.Context(), err) - } - outAlertActions := make([]domain.AlertActionResponse, len(alert.AlertActions)) - for j, alertAction := range alert.AlertActions { - if err := serializer.Map(alertAction, &outAlertActions[j]); err != nil { - log.InfoWithContext(r.Context(), err) - continue - } - } - out.Alert.AlertActions = outAlertActions - - ResponseJSON(w, r, http.StatusOK, out) -} - -// UpdateAlert godoc -// @Tags Alerts -// @Summary Update Alert -// @Description Update Alert -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdateAlertRequest true "Update cloud setting request" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/alerts/{alertId} [put] -// @Security JWT -func (h *AlertHandler) UpdateAlert(w http.ResponseWriter, r *http.Request) { - ErrorJSON(w, r, fmt.Errorf("Need implementation")) -} - -// DeleteAlert godoc -// @Tags Alerts -// @Summary Delete Alert -// @Description Delete Alert -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param alertId path string true "alertId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/alerts/{alertId} [delete] -// @Security JWT -func (h *AlertHandler) DeleteAlert(w http.ResponseWriter, r *http.Request) { - ErrorJSON(w, r, fmt.Errorf("Need implementation")) -} - -func (h *AlertHandler) AlertTest(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - ErrorJSON(w, r, err) - return - } - - log.InfoWithContext(r.Context(), "TEST ", body) -} - -// CreateAlertAction godoc -// @Tags Alerts -// @Summary Create alert action -// @Description Create alert action -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/alerts/{alertId}/actions [post] -// @Security JWT -func (h *AlertHandler) CreateAlertAction(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - strId, ok := vars["alertId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid alertId"), "", "")) - return - } - - alertId, err := uuid.Parse(strId) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) - return - } - - input := domain.CreateAlertActionRequest{} - err = UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, err) - return - } - - log.InfoWithContext(r.Context(), "alert : ", helper.ModelToJson(input)) - - var dto domain.AlertAction - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) - } - dto.AlertId = alertId - - alertAction, err := h.usecase.CreateAlertAction(r.Context(), dto) - if err != nil { - ErrorJSON(w, r, err) - return - } - - var out domain.CreateAlertActionResponse - out.ID = alertAction.String() - ResponseJSON(w, r, http.StatusOK, out) -} diff --git a/internal/delivery/http/app-group.go b/internal/delivery/http/app-group.go index 510e0d71..02361c39 100644 --- a/internal/delivery/http/app-group.go +++ b/internal/delivery/http/app-group.go @@ -6,6 +6,7 @@ import ( "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -18,22 +19,23 @@ type AppGroupHandler struct { usecase usecase.IAppGroupUsecase } -func NewAppGroupHandler(h usecase.IAppGroupUsecase) *AppGroupHandler { +func NewAppGroupHandler(h usecase.Usecase) *AppGroupHandler { return &AppGroupHandler{ - usecase: h, + usecase: h.AppGroup, } } // CreateAppGroup godoc -// @Tags AppGroups -// @Summary Install appGroup -// @Description Install appGroup -// @Accept json -// @Produce json -// @Param body body domain.CreateAppGroupRequest true "create appgroup request" -// @Success 200 {object} domain.CreateAppGroupResponse -// @Router /app-groups [post] -// @Security JWT +// +// @Tags AppGroups +// @Summary Install appGroup +// @Description Install appGroup +// @Accept json +// @Produce json +// @Param body body domain.CreateAppGroupRequest true "create appgroup request" +// @Success 200 {object} domain.CreateAppGroupResponse +// @Router /app-groups [post] +// @Security JWT func (h *AppGroupHandler) CreateAppGroup(w http.ResponseWriter, r *http.Request) { input := domain.CreateAppGroupRequest{} err := UnmarshalRequestInput(r, &input) @@ -42,9 +44,9 @@ func (h *AppGroupHandler) CreateAppGroup(w http.ResponseWriter, r *http.Request) return } - var dto domain.AppGroup - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.AppGroup + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } appGroupId, err := h.usecase.Create(r.Context(), dto) @@ -60,20 +62,21 @@ func (h *AppGroupHandler) CreateAppGroup(w http.ResponseWriter, r *http.Request) } // GetAppGroups godoc -// @Tags AppGroups -// @Summary Get appGroup list -// @Description Get appGroup list by giving params -// @Accept json -// @Produce json -// @Param clusterId query string false "clusterId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} domain.GetAppGroupsResponse -// @Router /app-groups [get] -// @Security JWT +// +// @Tags AppGroups +// @Summary Get appGroup list +// @Description Get appGroup list by giving params +// @Accept json +// @Produce json +// @Param clusterId query string false "clusterId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetAppGroupsResponse +// @Router /app-groups [get] +// @Security JWT func (h *AppGroupHandler) GetAppGroups(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() @@ -82,11 +85,7 @@ func (h *AppGroupHandler) GetAppGroups(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "C_INVALID_CLUSTER_ID", "")) return } - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } + pg := pagination.NewPagination(&urlParams) appGroups, err := h.usecase.Fetch(r.Context(), domain.ClusterId(clusterId), pg) if err != nil { @@ -97,29 +96,30 @@ func (h *AppGroupHandler) GetAppGroups(w http.ResponseWriter, r *http.Request) { var out domain.GetAppGroupsResponse out.AppGroups = make([]domain.AppGroupResponse, len(appGroups)) for i, appGroup := range appGroups { - if err := serializer.Map(appGroup, &out.AppGroups[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), appGroup, &out.AppGroups[i]); err != nil { + log.Info(r.Context(), err) continue } } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetAppGroup godoc -// @Tags AppGroups -// @Summary Get appGroup detail -// @Description Get appGroup detail by appGroupId -// @Accept json -// @Produce json -// @Param appGroupId path string true "appGroupId" -// @Success 200 {object} domain.GetAppGroupResponse -// @Router /app-groups/{appGroupId} [get] -// @Security JWT +// +// @Tags AppGroups +// @Summary Get appGroup detail +// @Description Get appGroup detail by appGroupId +// @Accept json +// @Produce json +// @Param appGroupId path string true "appGroupId" +// @Success 200 {object} domain.GetAppGroupResponse +// @Router /app-groups/{appGroupId} [get] +// @Security JWT func (h *AppGroupHandler) GetAppGroup(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["appGroupId"] @@ -139,23 +139,24 @@ func (h *AppGroupHandler) GetAppGroup(w http.ResponseWriter, r *http.Request) { } var out domain.GetAppGroupResponse - if err := serializer.Map(appGroup, &out.AppGroup); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), appGroup, &out.AppGroup); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // DeleteAppGroup godoc -// @Tags AppGroups -// @Summary Uninstall appGroup -// @Description Uninstall appGroup -// @Accept json -// @Produce json -// @Param object body string true "body" -// @Success 200 {object} nil -// @Router /app-groups [delete] -// @Security JWT +// +// @Tags AppGroups +// @Summary Uninstall appGroup +// @Description Uninstall appGroup +// @Accept json +// @Produce json +// @Param object body string true "body" +// @Success 200 {object} nil +// @Router /app-groups [delete] +// @Security JWT func (h *AppGroupHandler) DeleteAppGroup(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["appGroupId"] @@ -172,7 +173,7 @@ func (h *AppGroupHandler) DeleteAppGroup(w http.ResponseWriter, r *http.Request) err := h.usecase.Delete(r.Context(), appGroupId) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to delete appGroup err : ", err) + log.Error(r.Context(), "Failed to delete appGroup err : ", err) ErrorJSON(w, r, err) return } @@ -181,16 +182,17 @@ func (h *AppGroupHandler) DeleteAppGroup(w http.ResponseWriter, r *http.Request) } // GetApplications godoc -// @Tags AppGroups -// @Summary Get applications -// @Description Get applications -// @Accept json -// @Produce json -// @Param appGroupId path string true "appGroupId" -// @Param applicationType query string true "applicationType" -// @Success 200 {object} domain.GetApplicationsResponse -// @Router /app-groups/{appGroupId}/applications [get] -// @Security JWT +// +// @Tags AppGroups +// @Summary Get applications +// @Description Get applications +// @Accept json +// @Produce json +// @Param appGroupId path string true "appGroupId" +// @Param applicationType query string true "applicationType" +// @Success 200 {object} domain.GetApplicationsResponse +// @Router /app-groups/{appGroupId}/applications [get] +// @Security JWT func (h *AppGroupHandler) GetApplications(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["appGroupId"] @@ -215,7 +217,7 @@ func (h *AppGroupHandler) GetApplications(w http.ResponseWriter, r *http.Request applications, err := h.usecase.GetApplications(r.Context(), appGroupId, applicationType) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to get applications err : ", err) + log.Error(r.Context(), "Failed to get applications err : ", err) ErrorJSON(w, r, err) return } @@ -223,8 +225,8 @@ func (h *AppGroupHandler) GetApplications(w http.ResponseWriter, r *http.Request var out domain.GetApplicationsResponse out.Applications = make([]domain.ApplicationResponse, len(applications)) for i, application := range applications { - if err := serializer.Map(application, &out.Applications[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), application, &out.Applications[i]); err != nil { + log.Info(r.Context(), err) continue } } @@ -233,15 +235,16 @@ func (h *AppGroupHandler) GetApplications(w http.ResponseWriter, r *http.Request } // CreateApplication godoc -// @Tags AppGroups -// @Summary Create application -// @Description Create application -// @Accept json -// @Produce json -// @Param object body domain.CreateApplicationRequest true "body" -// @Success 200 {object} nil -// @Router /app-groups/{appGroupId}/applications [post] -// @Security JWT +// +// @Tags AppGroups +// @Summary Create application +// @Description Create application +// @Accept json +// @Produce json +// @Param object body domain.CreateApplicationRequest true "body" +// @Success 200 {object} nil +// @Router /app-groups/{appGroupId}/applications [post] +// @Security JWT func (h *AppGroupHandler) CreateApplication(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["appGroupId"] @@ -262,15 +265,15 @@ func (h *AppGroupHandler) CreateApplication(w http.ResponseWriter, r *http.Reque return } - var dto domain.Application - if err := serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.Application + if err := serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } dto.AppGroupId = appGroupId err = h.usecase.UpdateApplication(r.Context(), dto) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to update application err : ", err) + log.Error(r.Context(), "Failed to update application err : ", err) ErrorJSON(w, r, err) return } diff --git a/internal/delivery/http/app-serve-app.go b/internal/delivery/http/app-serve-app.go index 7b3df3e5..5a76487e 100644 --- a/internal/delivery/http/app-serve-app.go +++ b/internal/delivery/http/app-serve-app.go @@ -1,6 +1,7 @@ package http import ( + "context" "fmt" "math/rand" "net/http" @@ -12,6 +13,7 @@ import ( "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -68,32 +70,43 @@ var ( ) type AppServeAppHandler struct { - usecase usecase.IAppServeAppUsecase + usecase usecase.IAppServeAppUsecase + prjUsecase usecase.IProjectUsecase } -func NewAppServeAppHandler(h usecase.IAppServeAppUsecase) *AppServeAppHandler { +func NewAppServeAppHandler(u usecase.Usecase) *AppServeAppHandler { return &AppServeAppHandler{ - usecase: h, + usecase: u.AppServeApp, + prjUsecase: u.Project, } } // CreateAppServeApp godoc -// @Tags AppServeApps -// @Summary Install appServeApp -// @Description Install appServeApp -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param object body domain.CreateAppServeAppRequest true "Request body to create app" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps [post] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Install appServeApp +// @Description Install appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param object body domain.CreateAppServeAppRequest true "Request body to create app" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps [post] +// @Security JWT func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] - log.Debugf("organizationId = [%v]\n", organizationId) + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId"), "C_INVALID_PROJECT_ID", "")) return } @@ -106,23 +119,36 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re (appReq).SetDefaultValue() - var app domain.AppServeApp - if err = serializer.Map(appReq, &app); err != nil { + var app model.AppServeApp + if err = serializer.Map(r.Context(), appReq, &app); err != nil { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } - log.Infof("Processing CREATE request for app '%s'...", app.Name) + log.Infof(r.Context(), "Processing CREATE request for app '%s'...", app.Name) + + // Namespace validation + exist, err := h.prjUsecase.IsProjectNamespaceExist(r.Context(), organizationId, projectId, app.TargetClusterId, app.Namespace) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("Error while checking namespace record: %s", err), "", "")) + return + } + + if !exist { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("Namespace '%s' doesn't exist", app.Namespace), "", "")) + return + } now := time.Now() app.OrganizationId = organizationId + app.ProjectId = projectId app.EndpointUrl = "N/A" app.PreviewEndpointUrl = "N/A" app.Status = "PREPARING" app.CreatedAt = now - var task domain.AppServeAppTask - if err = serializer.Map(appReq, &task); err != nil { + var task model.AppServeAppTask + if err = serializer.Map(r.Context(), appReq, &task); err != nil { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } @@ -132,8 +158,6 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re task.Output = "" task.CreatedAt = now - app.AppServeAppTasks = append(app.AppServeAppTasks, task) - // Validate name param re, _ := regexp.Compile("^[a-z][a-z0-9-]*$") if !(re.MatchString(app.Name)) { @@ -141,7 +165,7 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re return } - exist, err := h.usecase.IsAppServeAppNameExist(organizationId, app.Name) + exist, err = h.usecase.IsAppServeAppNameExist(r.Context(), organizationId, app.Name) if err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return @@ -151,29 +175,29 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re return } - // Create namespace if it's not given by user - if len(strings.TrimSpace(app.Namespace)) == 0 { - // Check if the new namespace is already used in the target cluster - ns := "" - nsExist := true - for nsExist { - // Generate unique namespace based on name and random number - src := rand.NewSource(time.Now().UnixNano()) - r1 := rand.New(src) - ns = fmt.Sprintf("%s-%s", app.Name, strconv.Itoa(r1.Intn(10000))) - - nsExist, err = h.usecase.IsAppServeAppNamespaceExist(app.TargetClusterId, ns) - if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) - return - } - } + // Create namespace if it's not given by user + if len(strings.TrimSpace(app.Namespace)) == 0 { + // Check if the new namespace is already used in the target cluster + ns := "" + nsExist := true + for nsExist { + // Generate unique namespace based on name and random number + src := rand.NewSource(time.Now().UnixNano()) + r1 := rand.New(src) + ns = fmt.Sprintf("%s-%s", app.Name, strconv.Itoa(r1.Intn(10000))) + + nsExist, err = h.usecase.IsAppServeAppNamespaceExist(r.Context(), app.TargetClusterId, ns) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } - log.Infof("Created new namespace: %s", ns) - app.Namespace = ns - } else { - log.Infof("Using existing namespace: %s", app.Namespace) - } + log.Infof(r.Context(), "Created new namespace: %s", ns) + app.Namespace = ns + } else { + log.Infof(r.Context(), "Using existing namespace: %s", app.Namespace) + } // Validate port param for springboot app if app.AppType == "springboot" { @@ -190,14 +214,14 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re return } - _, _, err = h.usecase.CreateAppServeApp(&app) + _, _, err = h.usecase.CreateAppServeApp(r.Context(), &app, &task) if err != nil { ErrorJSON(w, r, err) return } var out domain.CreateAppServeAppResponse - if err = serializer.Map(app, &out); err != nil { + if err = serializer.Map(r.Context(), app, &out); err != nil { ErrorJSON(w, r, err) return } @@ -206,30 +230,40 @@ func (h *AppServeAppHandler) CreateAppServeApp(w http.ResponseWriter, r *http.Re } // GetAppServeApps godoc -// @Tags AppServeApps -// @Summary Get appServeApp list -// @Description Get appServeApp list by giving params -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param showAll query boolean false "Show all apps including deleted apps" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} []domain.AppServeApp -// @Router /organizations/{organizationId}/app-serve-apps [get] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Get appServeApp list +// @Description Get appServeApp list by giving params +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param showAll query boolean false "Show all apps including deleted apps" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} []model.AppServeApp +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps [get] +// @Security JWT func (h *AppServeAppHandler) GetAppServeApps(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] - fmt.Printf("organizationId = [%v]\n", organizationId) + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) if !ok { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) return } + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId"), "C_INVALID_PROJECT_ID", "")) + return + } + urlParams := r.URL.Query() showAllParam := urlParams.Get("showAll") @@ -239,176 +273,341 @@ func (h *AppServeAppHandler) GetAppServeApps(w http.ResponseWriter, r *http.Requ showAll, err := strconv.ParseBool(showAllParam) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to convert showAll params. Err: ", err) + log.Error(r.Context(), "Failed to convert showAll params. Err: ", err) ErrorJSON(w, r, err) return } - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } - apps, err := h.usecase.GetAppServeApps(organizationId, showAll, pg) + pg := pagination.NewPagination(&urlParams) + apps, err := h.usecase.GetAppServeApps(r.Context(), organizationId, projectId, showAll, pg) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to get Failed to get app-serve-apps ", err) + log.Error(r.Context(), "Failed to get Failed to get app-serve-apps ", err) ErrorJSON(w, r, err) return } var out domain.GetAppServeAppsResponse - out.AppServeApps = apps + out.AppServeApps = make([]domain.AppServeAppResponse, len(apps)) + for i, app := range apps { + if err := serializer.Map(r.Context(), app, &out.AppServeApps[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } -// GetAppServeApp godoc -// @Tags AppServeApps -// @Summary Get appServeApp -// @Description Get appServeApp by giving params -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Success 200 {object} domain.GetAppServeAppResponse -// @Router /organizations/{organizationId}/app-serve-apps/{appId} [get] -// @Security JWT -func (h *AppServeAppHandler) GetAppServeApp(w http.ResponseWriter, r *http.Request) { +// GetAppServeAppLatestTask godoc +// +// @Tags AppServeApps +// @Summary Get latest task from appServeApp +// @Description Get latest task from appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Success 200 {object} domain.GetAppServeAppTaskResponse +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/latest-task [get] +// @Security JWT +func (h *AppServeAppHandler) GetAppServeAppLatestTask(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] fmt.Printf("organizationId = [%v]\n", organizationId) if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId: [%s]", projectId), "C_INVALID_PROJECT_ID", "")) return } appId, ok := vars["appId"] fmt.Printf("appId = [%s]\n", appId) if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid appId"), "C_INVALID_ASA_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid appId"), "", "")) return } - app, err := h.usecase.GetAppServeAppById(appId) + + // Check if projectId exists + prj, err := h.prjUsecase.GetProject(r.Context(), organizationId, projectId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("Error while checking project record: %s", err), "", "")) + return + } else if prj == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("projectId not found: %s", projectId), "C_INVALID_PROJECT_ID", "")) + } + + task, err := h.usecase.GetAppServeAppLatestTask(r.Context(), appId) if err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } - if app == nil { - ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("no appId"), "D_NO_ASA", "")) + if task == nil { + ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("No task exists"), "", "")) return } + // Rollbacking to latest task should be blocked. + task.AvailableRollback = false - newTasks := make([]domain.AppServeAppTask, 0) + app, err := h.usecase.GetAppServeAppById(r.Context(), appId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if app == nil { + ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("No app exists"), "D_NO_ASA", "")) + return + } - for idx, t := range app.AppServeAppTasks { - // Rollbacking to latest task should be blocked. - if idx > 0 && strings.Contains(t.Status, "SUCCESS") && t.Status != "ABORT_SUCCESS" && - t.Status != "ROLLBACK_SUCCESS" { - t.AvailableRollback = true - } - newTasks = append(newTasks, t) + var out domain.GetAppServeAppTaskResponse + if err := serializer.Map(r.Context(), *app, &out.AppServeApp); err != nil { + log.Info(r.Context(), err) + } + if err := serializer.Map(r.Context(), *task, &out.AppServeAppTask); err != nil { + log.Info(r.Context(), err) } - app.AppServeAppTasks = newTasks - var out domain.GetAppServeAppResponse - out.AppServeApp = *app - out.Stages = makeStages(app) + out.Stages = makeStages(r.Context(), task, app) ResponseJSON(w, r, http.StatusOK, out) } -// GetAppServeAppLatestTask godoc -// @Tags AppServeApps -// @Summary Get latest task from appServeApp -// @Description Get latest task from appServeApp -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Success 200 {object} domain.GetAppServeAppTaskResponse -// @Router /organizations/{organizationId}/app-serve-apps/{appId}/latest-task [get] -// @Security JWT -func (h *AppServeAppHandler) GetAppServeAppLatestTask(w http.ResponseWriter, r *http.Request) { +// GetNumOfAppsOnStack godoc +// +// @Tags AppServeApps +// @Summary Get number of apps on given stack +// @Description Get number of apps on given stack +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param stackId query string true "Stack ID" +// @Success 200 {object} int64 +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/count [get] +// @Security JWT +func (h *AppServeAppHandler) GetNumOfAppsOnStack(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] - fmt.Printf("organizationId = [%v]\n", organizationId) + log.Debugf(r.Context(), "organizationId = [%s]\n", organizationId) if !ok { ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "", "")) return } + urlParams := r.URL.Query() + stackId := urlParams.Get("stackId") + if stackId == "" { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("StackId must be provided."), "", "")) + } + fmt.Printf("stackId = [%s]\n", stackId) + + numApps, err := h.usecase.GetNumOfAppsOnStack(r.Context(), organizationId, stackId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + ResponseJSON(w, r, http.StatusOK, numApps) +} + +// GetAppServeAppTasksByAppId godoc +// +// @Tags AppServeApps +// @Summary Get appServeAppTask list +// @Description Get appServeAppTask list by giving params +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} []model.AppServeApp +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks [get] +// @Security JWT +func (h *AppServeAppHandler) GetAppServeAppTasksByAppId(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId: %s", organizationId), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId: %s", projectId), "C_INVALID_PROJECT_ID", "")) + return + } + appId, ok := vars["appId"] - fmt.Printf("appId = [%s]\n", appId) if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid appId"), "", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid appId: %s", appId), "C_INVALID_ASA_ID", "")) return } - task, err := h.usecase.GetAppServeAppLatestTask(appId) + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + + // Check if projectId exists + prj, err := h.prjUsecase.GetProject(r.Context(), organizationId, projectId) if err != nil { - ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("Error while checking project record: %v", err), "", "")) return + } else if prj == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("projectId not found: %s", projectId), "C_INVALID_PROJECT_ID", "")) } - if task == nil { - ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("no task exists"), "", "")) + + tasks, err := h.usecase.GetAppServeAppTasks(r.Context(), appId, pg) + if err != nil { + log.Error(r.Context(), "Failed to get app-serve-app-tasks ", err) + ErrorJSON(w, r, err) return } - var out domain.GetAppServeAppTaskResponse - out.AppServeAppTask = *task + var out domain.GetAppServeAppTasksResponse + out.AppServeAppTasks = make([]domain.AppServeAppTaskResponse, len(tasks)) + for i, task := range tasks { + // Rollbacking to latest task should be blocked. + if i > 0 && strings.Contains(task.Status, "SUCCESS") && task.Status != "ABORT_SUCCESS" && + task.Status != "ROLLBACK_SUCCESS" { + task.AvailableRollback = true + } + + if err := serializer.Map(r.Context(), task, &out.AppServeAppTasks[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } ResponseJSON(w, r, http.StatusOK, out) } -// GetNumOfAppsOnStack godoc -// @Tags AppServeApps -// @Summary Get number of apps on given stack -// @Description Get number of apps on given stack -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param stackId query string true "Stack ID" -// @Success 200 {object} int64 -// @Router /organizations/{organizationId}/app-serve-apps/count [get] -// @Security JWT -func (h *AppServeAppHandler) GetNumOfAppsOnStack(w http.ResponseWriter, r *http.Request) { +// GetAppServeAppTaskDetail godoc +// +// @Tags AppServeApps +// @Summary Get task detail from appServeApp +// @Description Get task detail from appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Param taskId path string true "Task ID" +// @Success 200 {object} domain.GetAppServeAppTaskResponse +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks/{taskId} [get] +// @Security JWT +func (h *AppServeAppHandler) GetAppServeAppTaskDetail(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] - log.Debugf("organizationId = [%s]\n", organizationId) + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId: [%s]", organizationId), "C_INVALID_ORGANIZATION_ID", "")) return } - urlParams := r.URL.Query() - stackId := urlParams.Get("stackId") - if stackId == "" { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("StackId must be provided."), "", "")) + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid projectId: [%s]", projectId), "C_INVALID_PROJECT_ID", "")) + return + } + + appId, ok := vars["appId"] + log.Debugf(r.Context(), "appId = [%s]\n", appId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid appId: [%s]", appId), "C_INVALID_ASA_ID", "")) + return } - fmt.Printf("stackId = [%s]\n", stackId) - numApps, err := h.usecase.GetNumOfAppsOnStack(organizationId, stackId) + taskId, ok := vars["taskId"] + log.Debugf(r.Context(), "taskId = [%s]\n", taskId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid taskId: [%s]", taskId), "C_INVALID_ASA_TASK_ID", "")) + return + } + + // Check if projectId exists + prj, err := h.prjUsecase.GetProject(r.Context(), organizationId, projectId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("Error while checking project record: %s", err), "", "")) + return + } else if prj == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("projectId not found: %s", projectId), "C_INVALID_PROJECT_ID", "")) + } + + task, err := h.usecase.GetAppServeAppTaskById(r.Context(), taskId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if task == nil { + ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("No task exists"), "", "")) + return + } + + app, err := h.usecase.GetAppServeAppById(r.Context(), appId) if err != nil { ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } + if app == nil { + ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("No app exists"), "D_NO_ASA", "")) + return + } - ResponseJSON(w, r, http.StatusOK, numApps) + // Workaround: Compare task ID with latest task ID + // Mark latest task with additional flag later + latestTask, err := h.usecase.GetAppServeAppLatestTask(r.Context(), appId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if taskId == latestTask.ID { + task.AvailableRollback = false + } else if strings.Contains(task.Status, "SUCCESS") && task.Status != "ABORT_SUCCESS" && + task.Status != "ROLLBACK_SUCCESS" { + task.AvailableRollback = true + } + + var out domain.GetAppServeAppTaskResponse + if err := serializer.Map(r.Context(), *app, &out.AppServeApp); err != nil { + log.Info(r.Context(), err) + } + if err := serializer.Map(r.Context(), *task, &out.AppServeAppTask); err != nil { + log.Info(r.Context(), err) + } + + out.Stages = makeStages(r.Context(), task, app) + + ResponseJSON(w, r, http.StatusOK, out) } -func makeStages(app *domain.AppServeApp) []domain.StageResponse { +func makeStages(ctx context.Context, task *model.AppServeAppTask, app *model.AppServeApp) []domain.StageResponse { stages := make([]domain.StageResponse, 0) var stage domain.StageResponse var pipelines []string - taskStatus := app.AppServeAppTasks[0].Status - strategy := app.AppServeAppTasks[0].Strategy + taskStatus := task.Status + strategy := task.Strategy if taskStatus == "ROLLBACKING" || taskStatus == "ROLLBACK_SUCCESS" || @@ -431,27 +630,27 @@ func makeStages(app *domain.AppServeApp) []domain.StageResponse { pipelines = []string{"deploy", "promote"} } } else { - log.Error("Unexpected case happened while making stages!") + log.Error(ctx, "Unexpected case happened while making stages!") } fmt.Printf("Pipeline stages: %v\n", pipelines) for _, pl := range pipelines { - stage = makeStage(app, pl) + stage = makeStage(ctx, task, app, pl) stages = append(stages, stage) } return stages } -func makeStage(app *domain.AppServeApp, pl string) domain.StageResponse { - taskStatus := app.AppServeAppTasks[0].Status - strategy := app.AppServeAppTasks[0].Strategy +func makeStage(ctx context.Context, task *model.AppServeAppTask, app *model.AppServeApp, pl string) domain.StageResponse { + taskStatus := task.Status + strategy := task.Strategy stage := domain.StageResponse{ Name: pl, - Status: StatusStages[app.Status][pl], - Result: StatusResult[StatusStages[app.Status][pl]], + Status: StatusStages[taskStatus][pl], + Result: StatusResult[StatusStages[taskStatus][pl]], } var actions []domain.ActionResponse @@ -472,15 +671,13 @@ func makeStage(app *domain.AppServeApp, pl string) domain.StageResponse { } actions = append(actions, action) } - } else { - log.Error("Not supported strategy!") } } else if stage.Status == "PROMOTE_WAIT" && strategy == "blue-green" { action := domain.ActionResponse{ Name: "ABORT", Uri: fmt.Sprintf(internal.API_PREFIX+internal.API_VERSION+ - "/organizations/%v/app-serve-apps/%v", app.OrganizationId, app.ID), + "/organizations/%v/projects/%v/app-serve-apps/%v", app.OrganizationId, app.ProjectId, app.ID), Type: "API", Method: "PUT", Body: map[string]string{"strategy": "blue-green", "abort": "true"}, @@ -490,7 +687,7 @@ func makeStage(app *domain.AppServeApp, pl string) domain.StageResponse { action = domain.ActionResponse{ Name: "PROMOTE", Uri: fmt.Sprintf(internal.API_PREFIX+internal.API_VERSION+ - "/organizations/%v/app-serve-apps/%v", app.OrganizationId, app.ID), + "/organizations/%v/projects/%v/app-serve-apps/%v", app.OrganizationId, app.ProjectId, app.ID), Type: "API", Method: "PUT", Body: map[string]string{"strategy": "blue-green", "promote": "true"}, @@ -504,14 +701,17 @@ func makeStage(app *domain.AppServeApp, pl string) domain.StageResponse { } // IsAppServeAppExist godoc -// @Tags AppServeApps -// @Summary Get appServeApp -// @Description Get appServeApp by giving params -// @Accept json -// @Produce json -// @Success 200 {object} bool -// @Router /organizations/{organizationId}/app-serve-apps/{appId}/exist [get] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Get appServeApp +// @Description Get appServeApp by giving params +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} bool +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/exist [get] +// @Security JWT func (h *AppServeAppHandler) IsAppServeAppExist(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -529,7 +729,7 @@ func (h *AppServeAppHandler) IsAppServeAppExist(w http.ResponseWriter, r *http.R return } - exist, err := h.usecase.IsAppServeAppExist(appId) + exist, err := h.usecase.IsAppServeAppExist(r.Context(), appId) if err != nil { ErrorJSON(w, r, err) return @@ -543,16 +743,18 @@ func (h *AppServeAppHandler) IsAppServeAppExist(w http.ResponseWriter, r *http.R } // IsAppServeAppNameExist godoc -// @Tags AppServeApps -// @Summary Check duplicate appServeAppName -// @Description Check duplicate appServeAppName by giving params -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param name path string true "name" -// @Success 200 {object} bool -// @Router /organizations/{organizationId}/app-serve-apps/name/{name}/existence [get] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Check duplicate appServeAppName +// @Description Check duplicate appServeAppName by giving params +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param name path string true "name" +// @Success 200 {object} bool +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/name/{name}/existence [get] +// @Security JWT func (h *AppServeAppHandler) IsAppServeAppNameExist(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -568,7 +770,7 @@ func (h *AppServeAppHandler) IsAppServeAppNameExist(w http.ResponseWriter, r *ht return } - existed, err := h.usecase.IsAppServeAppNameExist(organizationId, appName) + existed, err := h.usecase.IsAppServeAppNameExist(r.Context(), organizationId, appName) if err != nil { ErrorJSON(w, r, err) return @@ -582,17 +784,19 @@ func (h *AppServeAppHandler) IsAppServeAppNameExist(w http.ResponseWriter, r *ht } // UpdateAppServeApp godoc -// @Tags AppServeApps -// @Summary Update appServeApp -// @Description Update appServeApp -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Param object body domain.UpdateAppServeAppRequest true "Request body to update app" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps/{appId} [put] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Update appServeApp +// @Description Update appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Param object body domain.UpdateAppServeAppRequest true "Request body to update app" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId} [put] +// @Security JWT func (h *AppServeAppHandler) UpdateAppServeApp(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -608,13 +812,15 @@ func (h *AppServeAppHandler) UpdateAppServeApp(w http.ResponseWriter, r *http.Re return } - app, err := h.usecase.GetAppServeAppById(appId) + // Get latest task + latestTask, err := h.usecase.GetAppServeAppLatestTask(r.Context(), appId) if err != nil { - ErrorJSON(w, r, err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) return } - if len(app.AppServeAppTasks) < 1 { - ErrorJSON(w, r, err) + if latestTask == nil { + ErrorJSON(w, r, httpErrors.NewNoContentError(fmt.Errorf("No task exists"), "", "")) + return } // unmarshal request that only contains task-specific params @@ -625,43 +831,26 @@ func (h *AppServeAppHandler) UpdateAppServeApp(w http.ResponseWriter, r *http.Re return } - // Instead of setting default value, some fields should be retrieved - // from existing app config. - //appReq.SetDefaultValue() - - var task domain.AppServeAppTask - //tasks := app.AppServeAppTasks - //sort.Slice(tasks, func(i, j int) bool { - // return tasks[i].CreatedAt.String() > tasks[j].CreatedAt.String() - //}) - //for _, t := range tasks { - // if t.Status == "DEPLOY_SUCCESS" { - // latestTask = t - // break - // } - //} - //if err = serializer.Map(latestTask, &task); err != nil { - // ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - // return - //} - - var latestTask = app.AppServeAppTasks[0] - if err = serializer.Map(latestTask, &task); err != nil { + var task model.AppServeAppTask + if err = serializer.Map(r.Context(), *latestTask, &task); err != nil { //ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } - if err = serializer.Map(appReq, &task); err != nil { + if err = serializer.Map(r.Context(), appReq, &task); err != nil { //ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } - //updateVersion, err := strconv.Atoi(latestTask.Version) - //if err != nil { - // ErrorJSON(w, r, httpErrors.NewInternalServerError(err,"")) - //} - //task.Version = strconv.Itoa(updateVersion + 1) - task.Version = strconv.Itoa(len(app.AppServeAppTasks) + 1) + // Set new version + verInt, err := strconv.Atoi(latestTask.Version) + if err != nil { + ErrorJSON(w, r, err) + return + } + newVerStr := strconv.Itoa(verInt + 1) + + task.Version = newVerStr //task.AppServeAppId = app.ID task.Status = "PREPARING" task.RollbackVersion = "" @@ -669,17 +858,15 @@ func (h *AppServeAppHandler) UpdateAppServeApp(w http.ResponseWriter, r *http.Re task.CreatedAt = time.Now() task.UpdatedAt = nil - fmt.Println("===========================") - fmt.Printf("%v\n", task) - fmt.Println("===========================") + log.Debugf(r.Context(), "New task in update request: %v\n", task) var res string if appReq.Promote { - res, err = h.usecase.PromoteAppServeApp(appId) + res, err = h.usecase.PromoteAppServeApp(r.Context(), appId) } else if appReq.Abort { - res, err = h.usecase.AbortAppServeApp(appId) + res, err = h.usecase.AbortAppServeApp(r.Context(), appId) } else { - res, err = h.usecase.UpdateAppServeApp(app, &task) + res, err = h.usecase.UpdateAppServeApp(r.Context(), appId, &task) } if err != nil { @@ -691,17 +878,19 @@ func (h *AppServeAppHandler) UpdateAppServeApp(w http.ResponseWriter, r *http.Re } // UpdateAppServeAppStatus godoc -// @Tags AppServeApps -// @Summary Update app status -// @Description Update app status -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Param body body domain.UpdateAppServeAppStatusRequest true "Request body to update app status" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps/{appId}/status [patch] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Update app status +// @Description Update app status +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Param body body domain.UpdateAppServeAppStatusRequest true "Request body to update app status" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/status [patch] +// @Security JWT func (h *AppServeAppHandler) UpdateAppServeAppStatus(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -724,7 +913,7 @@ func (h *AppServeAppHandler) UpdateAppServeAppStatus(w http.ResponseWriter, r *h return } - res, err := h.usecase.UpdateAppServeAppStatus(appId, appStatusReq.TaskID, appStatusReq.Status, appStatusReq.Output) + res, err := h.usecase.UpdateAppServeAppStatus(r.Context(), appId, appStatusReq.TaskID, appStatusReq.Status, appStatusReq.Output) if err != nil { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return @@ -734,17 +923,19 @@ func (h *AppServeAppHandler) UpdateAppServeAppStatus(w http.ResponseWriter, r *h } // UpdateAppServeAppEndpoint godoc -// @Tags AppServeApps -// @Summary Update app endpoint -// @Description Update app endpoint -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "appId" -// @Param body body domain.UpdateAppServeAppEndpointRequest true "Request body to update app endpoint" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps/{appId}/endpoint [patch] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Update app endpoint +// @Description Update app endpoint +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "appId" +// @Param body body domain.UpdateAppServeAppEndpointRequest true "Request body to update app endpoint" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/endpoint [patch] +// @Security JWT func (h *AppServeAppHandler) UpdateAppServeAppEndpoint(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -768,6 +959,7 @@ func (h *AppServeAppHandler) UpdateAppServeAppEndpoint(w http.ResponseWriter, r } res, err := h.usecase.UpdateAppServeAppEndpoint( + r.Context(), appId, appReq.TaskID, appReq.EndpointUrl, @@ -782,16 +974,18 @@ func (h *AppServeAppHandler) UpdateAppServeAppEndpoint(w http.ResponseWriter, r } // DeleteAppServeApp godoc -// @Tags AppServeApps -// @Summary Uninstall appServeApp -// @Description Uninstall appServeApp -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps/{appId} [delete] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Uninstall appServeApp +// @Description Uninstall appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId} [delete] +// @Security JWT func (h *AppServeAppHandler) DeleteAppServeApp(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -807,9 +1001,9 @@ func (h *AppServeAppHandler) DeleteAppServeApp(w http.ResponseWriter, r *http.Re return } - res, err := h.usecase.DeleteAppServeApp(appId) + res, err := h.usecase.DeleteAppServeApp(r.Context(), appId) if err != nil { - log.ErrorWithContext(r.Context(), "Failed to delete appId err : ", err) + log.Error(r.Context(), "Failed to delete appId err : ", err) ErrorJSON(w, r, err) return } @@ -818,17 +1012,19 @@ func (h *AppServeAppHandler) DeleteAppServeApp(w http.ResponseWriter, r *http.Re } // RollbackAppServeApp godoc -// @Tags AppServeApps -// @Summary Rollback appServeApp -// @Description Rollback appServeApp -// @Accept json -// @Produce json -// @Param organizationId path string true "Organization ID" -// @Param appId path string true "App ID" -// @Param object body domain.RollbackAppServeAppRequest true "Request body to rollback app" -// @Success 200 {object} string -// @Router /organizations/{organizationId}/app-serve-apps/{appId}/rollback [post] -// @Security JWT +// +// @Tags AppServeApps +// @Summary Rollback appServeApp +// @Description Rollback appServeApp +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param appId path string true "App ID" +// @Param object body domain.RollbackAppServeAppRequest true "Request body to rollback app" +// @Success 200 {object} string +// @Router /organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/rollback [post] +// @Security JWT func (h *AppServeAppHandler) RollbackAppServeApp(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -855,7 +1051,7 @@ func (h *AppServeAppHandler) RollbackAppServeApp(w http.ResponseWriter, r *http. return } - res, err := h.usecase.RollbackAppServeApp(appId, appReq.TaskId) + res, err := h.usecase.RollbackAppServeApp(r.Context(), appId, appReq.TaskId) if err != nil { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) diff --git a/internal/delivery/http/audit.go b/internal/delivery/http/audit.go new file mode 100644 index 00000000..520521c1 --- /dev/null +++ b/internal/delivery/http/audit.go @@ -0,0 +1,137 @@ +package http + +import ( + "fmt" + "net/http" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type AuditHandler struct { + usecase usecase.IAuditUsecase +} + +func NewAuditHandler(h usecase.Usecase) *AuditHandler { + return &AuditHandler{ + usecase: h.Audit, + } +} + +// CreateAudit godoc +// +// @Tags Audits +// @Summary Create Audit +// @Description Create Audit +// @Accept json +// @Produce json +// @Param body body domain.CreateAuditRequest true "create audit request" +// @Success 200 {object} domain.CreateAuditResponse +// @Router /admin/audits [post] +// @Security JWT +func (h *AuditHandler) CreateAudit(w http.ResponseWriter, r *http.Request) { + ErrorJSON(w, r, fmt.Errorf("need implementation")) +} + +// GetAudit godoc +// +// @Tags Audits +// @Summary Get Audits +// @Description Get Audits +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filter query []string false "filters" +// @Param or query []string false "filters" +// @Success 200 {object} domain.GetAuditsResponse +// @Router /admin/audits [get] +// @Security JWT +func (h *AuditHandler) GetAudits(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + audits, err := h.usecase.Fetch(r.Context(), pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetAuditsResponse + out.Audits = make([]domain.AuditResponse, len(audits)) + for i, audit := range audits { + if err := serializer.Map(r.Context(), audit, &out.Audits[i]); err != nil { + log.Info(r.Context(), err) + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetAudit godoc +// +// @Tags Audits +// @Summary Get Audit +// @Description Get Audit +// @Accept json +// @Produce json +// @Param auditId path string true "auditId" +// @Success 200 {object} domain.GetAuditResponse +// @Router /admin/audits/{auditId} [get] +// @Security JWT +func (h *AuditHandler) GetAudit(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["auditId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid auditId"), "C_INVALID_AUDIT_ID", "")) + return + } + + auditId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_AUDIT_ID", "")) + return + } + + audit, err := h.usecase.Get(r.Context(), auditId) + if err != nil { + ErrorJSON(w, r, err) + return + } + log.Info(r.Context(), audit) + + var out domain.GetAuditResponse + if err := serializer.Map(r.Context(), audit, &out.Audit); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) + +} + +// DeleteAudit godoc +// +// @Tags Audits +// @Summary Delete Audit 'NOT IMPLEMENTED' +// @Description Delete Audit +// @Accept json +// @Produce json +// @Param auditId path string true "auditId" +// @Success 200 {object} nil +// @Router /admin/audits/{auditId} [delete] +// @Security JWT +func (h *AuditHandler) DeleteAudit(w http.ResponseWriter, r *http.Request) { + ErrorJSON(w, r, fmt.Errorf("need implementation")) +} diff --git a/internal/delivery/http/auth.go b/internal/delivery/http/auth.go index 2c5fcd4d..ee74f5eb 100644 --- a/internal/delivery/http/auth.go +++ b/internal/delivery/http/auth.go @@ -5,7 +5,9 @@ import ( "net/http" "github.com/openinfradev/tks-api/internal" + "github.com/openinfradev/tks-api/internal/middleware/audit" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" @@ -16,34 +18,39 @@ import ( type IAuthHandler interface { Login(w http.ResponseWriter, r *http.Request) Logout(w http.ResponseWriter, r *http.Request) - PingToken(w http.ResponseWriter, r *http.Request) RefreshToken(w http.ResponseWriter, r *http.Request) FindId(w http.ResponseWriter, r *http.Request) FindPassword(w http.ResponseWriter, r *http.Request) VerifyIdentityForLostId(w http.ResponseWriter, r *http.Request) VerifyIdentityForLostPassword(w http.ResponseWriter, r *http.Request) + VerifyToken(w http.ResponseWriter, r *http.Request) //Authenticate(next http.Handler) http.Handler } type AuthHandler struct { - usecase usecase.IAuthUsecase + usecase usecase.IAuthUsecase + auditUsecase usecase.IAuditUsecase + projectUsecase usecase.IProjectUsecase } -func NewAuthHandler(h usecase.IAuthUsecase) IAuthHandler { +func NewAuthHandler(h usecase.Usecase) IAuthHandler { return &AuthHandler{ - usecase: h, + usecase: h.Auth, + auditUsecase: h.Audit, + projectUsecase: h.Project, } } // Login godoc -// @Tags Auth -// @Summary login -// @Description login -// @Accept json -// @Produce json -// @Param body body domain.LoginRequest true "account info" -// @Success 200 {object} domain.LoginResponse "user detail" -// @Router /auth/login [post] +// +// @Tags Auth +// @Summary login +// @Description login +// @Accept json +// @Produce json +// @Param body body domain.LoginRequest true "account info" +// @Success 200 {object} domain.LoginResponse "user detail" +// @Router /auth/login [post] func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { input := domain.LoginRequest{} err := UnmarshalRequestInput(r, &input) @@ -52,16 +59,35 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { return } - user, err := h.usecase.Login(input.AccountId, input.Password, input.OrganizationId) + user, err := h.usecase.Login(r.Context(), input.AccountId, input.Password, input.OrganizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + errorResponse, _ := httpErrors.ErrorResponse(err) + _, _ = h.auditUsecase.Create(r.Context(), model.Audit{ + OrganizationId: input.OrganizationId, + Group: "Auth", + Message: fmt.Sprintf("[%s]님이 로그인에 실패하였습니다.", input.AccountId), + Description: errorResponse.Text(), + ClientIP: audit.GetClientIpAddress(w, r), + UserId: nil, + UserAccountId: input.AccountId, + }) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return + } else { + _, _ = h.auditUsecase.Create(r.Context(), model.Audit{ + OrganizationId: input.OrganizationId, + Group: "Auth", + Message: fmt.Sprintf("[%s]님이 로그인 하였습니다.", input.AccountId), + Description: "", + ClientIP: audit.GetClientIpAddress(w, r), + UserId: &user.ID, + }) } var cookies []*http.Cookie - if targetCookies, err := h.usecase.SingleSignIn(input.OrganizationId, input.AccountId, input.Password); err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + if targetCookies, err := h.usecase.SingleSignIn(r.Context(), input.OrganizationId, input.AccountId, input.Password); err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) } else { cookies = append(cookies, targetCookies...) } @@ -73,50 +99,56 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) { } var out domain.LoginResponse - if err = serializer.Map(user, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), user, &out.User); err != nil { + log.Error(r.Context(), err) + } + for _, role := range user.Roles { + out.User.Roles = append(out.User.Roles, domain.SimpleRoleResponse{ + ID: role.ID, + Name: role.Name, + }) } ResponseJSON(w, r, http.StatusOK, out) } // Logout godoc -// @Tags Auth -// @Summary logout -// @Description logout -// @Accept json -// @Produce json -// @Success 200 {object} domain.LogoutResponse -// @Router /auth/logout [post] -// @Security JWT +// +// @Tags Auth +// @Summary logout +// @Description logout +// @Accept json +// @Produce json +// @Router /auth/logout [post] +// @Security JWT func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) { ctx := r.Context() sessionId, ok := request.SessionFrom(ctx) if !ok { - log.ErrorfWithContext(r.Context(), "session id is not found") + log.Errorf(r.Context(), "session id is not found") ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("session id is not found"), "A_NO_SESSION", "")) return } userInfo, ok := request.UserFrom(ctx) if !ok { - log.ErrorfWithContext(r.Context(), "user info is not found") + log.Errorf(r.Context(), "user info is not found") ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user info is not found"), "A_NO_SESSION", "")) return } organizationId := userInfo.GetOrganizationId() - err := h.usecase.Logout(sessionId, organizationId) + err := h.usecase.Logout(r.Context(), sessionId, organizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } var cookies []*http.Cookie - redirectUrl, targetCookies, err := h.usecase.SingleSignOut(organizationId) + redirectUrl, targetCookies, err := h.usecase.SingleSignOut(r.Context(), organizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) } cookies = append(cookies, targetCookies...) if len(cookies) > 0 { @@ -140,15 +172,16 @@ func (h *AuthHandler) RefreshToken(w http.ResponseWriter, r *http.Request) { } // FindId godoc -// @Tags Auth -// @Summary Request to find forgotten ID -// @Description This API allows users to find their account ID by submitting required information -// @Accept json -// @Produce json -// @Param body body domain.FindIdRequest true "Request body for finding the account ID including {organization ID, email, username, 6 digit code}" -// @Success 200 {object} domain.FindIdResponse -// @Failure 400 {object} httpErrors.RestError -// @Router /auth/find-id/verification [post] +// +// @Tags Auth +// @Summary Request to find forgotten ID +// @Description This API allows users to find their account ID by submitting required information +// @Accept json +// @Produce json +// @Param body body domain.FindIdRequest true "Request body for finding the account ID including {organization ID, email, username, 6 digit code}" +// @Success 200 {object} domain.FindIdResponse +// @Failure 400 {object} httpErrors.RestError +// @Router /auth/find-id/verification [post] func (h *AuthHandler) FindId(w http.ResponseWriter, r *http.Request) { input := domain.FindIdRequest{} err := UnmarshalRequestInput(r, &input) @@ -157,9 +190,9 @@ func (h *AuthHandler) FindId(w http.ResponseWriter, r *http.Request) { return } - accountId, err := h.usecase.FindId(input.Code, input.Email, input.UserName, input.OrganizationId) + accountId, err := h.usecase.FindId(r.Context(), input.Code, input.Email, input.UserName, input.OrganizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return @@ -171,15 +204,16 @@ func (h *AuthHandler) FindId(w http.ResponseWriter, r *http.Request) { } // FindPassword godoc -// @Tags Auth -// @Summary Request to find forgotten password -// @Description This API allows users to reset their forgotten password by submitting required information -// @Accept json -// @Produce json -// @Param body body domain.FindPasswordRequest true "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}" -// @Success 200 -// @Failure 400 {object} httpErrors.RestError -// @Router /auth/find-password/verification [post] +// +// @Tags Auth +// @Summary Request to find forgotten password +// @Description This API allows users to reset their forgotten password by submitting required information +// @Accept json +// @Produce json +// @Param body body domain.FindPasswordRequest true "Request body for finding the password including {organization ID, email, username, Account ID, 6 digit code}" +// @Success 200 +// @Failure 400 {object} httpErrors.RestError +// @Router /auth/find-password/verification [post] func (h *AuthHandler) FindPassword(w http.ResponseWriter, r *http.Request) { input := domain.FindPasswordRequest{} err := UnmarshalRequestInput(r, &input) @@ -188,9 +222,9 @@ func (h *AuthHandler) FindPassword(w http.ResponseWriter, r *http.Request) { return } - err = h.usecase.FindPassword(input.Code, input.AccountId, input.Email, input.UserName, input.OrganizationId) + err = h.usecase.FindPassword(r.Context(), input.Code, input.AccountId, input.Email, input.UserName, input.OrganizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } @@ -199,15 +233,16 @@ func (h *AuthHandler) FindPassword(w http.ResponseWriter, r *http.Request) { } // VerifyIdentityForLostId godoc -// @Tags Auth -// @Summary Request to verify identity for lost id -// @Description This API allows users to verify their identity for lost id by submitting required information -// @Accept json -// @Produce json -// @Param body body domain.VerifyIdentityForLostIdRequest true "Request body for verifying identity for lost id including {organization ID, email, username}" -// @Success 200 {object} domain.VerifyIdentityForLostIdResponse -// @Failure 400 {object} httpErrors.RestError -// @Router /auth/find-id/code [post] +// +// @Tags Auth +// @Summary Request to verify identity for lost id +// @Description This API allows users to verify their identity for lost id by submitting required information +// @Accept json +// @Produce json +// @Param body body domain.VerifyIdentityForLostIdRequest true "Request body for verifying identity for lost id including {organization ID, email, username}" +// @Success 200 {object} domain.VerifyIdentityForLostIdResponse +// @Failure 400 {object} httpErrors.RestError +// @Router /auth/find-id/code [post] func (h *AuthHandler) VerifyIdentityForLostId(w http.ResponseWriter, r *http.Request) { input := domain.VerifyIdentityForLostIdRequest{} err := UnmarshalRequestInput(r, &input) @@ -216,9 +251,9 @@ func (h *AuthHandler) VerifyIdentityForLostId(w http.ResponseWriter, r *http.Req return } - err = h.usecase.VerifyIdentity("", input.Email, input.UserName, input.OrganizationId) + err = h.usecase.VerifyIdentity(r.Context(), "", input.Email, input.UserName, input.OrganizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } @@ -229,15 +264,16 @@ func (h *AuthHandler) VerifyIdentityForLostId(w http.ResponseWriter, r *http.Req } // VerifyIdentityForLostPassword godoc -// @Tags Auth -// @Summary Request to verify identity for lost password -// @Description This API allows users to verify their identity for lost password by submitting required information -// @Accept json -// @Produce json -// @Param body body domain.VerifyIdentityForLostPasswordRequest true "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}" -// @Success 200 {object} domain.VerifyIdentityForLostPasswordResponse -// @Failure 400 {object} httpErrors.RestError -// @Router /auth/find-password/code [post] +// +// @Tags Auth +// @Summary Request to verify identity for lost password +// @Description This API allows users to verify their identity for lost password by submitting required information +// @Accept json +// @Produce json +// @Param body body domain.VerifyIdentityForLostPasswordRequest true "Request body for verifying identity for lost password including {organization ID, email, username, Account ID}" +// @Success 200 {object} domain.VerifyIdentityForLostPasswordResponse +// @Failure 400 {object} httpErrors.RestError +// @Router /auth/find-password/code [post] func (h *AuthHandler) VerifyIdentityForLostPassword(w http.ResponseWriter, r *http.Request) { input := domain.VerifyIdentityForLostPasswordRequest{} err := UnmarshalRequestInput(r, &input) @@ -246,9 +282,9 @@ func (h *AuthHandler) VerifyIdentityForLostPassword(w http.ResponseWriter, r *ht return } - err = h.usecase.VerifyIdentity(input.AccountId, input.Email, input.UserName, input.OrganizationId) + err = h.usecase.VerifyIdentity(r.Context(), input.AccountId, input.Email, input.UserName, input.OrganizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } @@ -258,27 +294,31 @@ func (h *AuthHandler) VerifyIdentityForLostPassword(w http.ResponseWriter, r *ht ResponseJSON(w, r, http.StatusOK, out) } -// Login godoc -// @Tags Auth -// @Summary ping with token -// @Description ping with token -// @Accept json -// @Produce json -// @Param body body domain.PingTokenRequest true "token info" -// @Success 200 {object} nil -// @Router /auth/ping [post] -func (h *AuthHandler) PingToken(w http.ResponseWriter, r *http.Request) { - input := domain.PingTokenRequest{} - err := UnmarshalRequestInput(r, &input) - if err != nil { - ErrorJSON(w, r, err) +// VerifyToken godoc +// @Tags Auth +// @Summary verify token +// @Description verify token +// @Success 200 {object} nil +// @Failure 401 {object} nil +// @Router /auth/verify-token [get] + +func (h *AuthHandler) VerifyToken(w http.ResponseWriter, r *http.Request) { + token, ok := request.TokenFrom(r.Context()) + if !ok { + log.Errorf(r.Context(), "token is not found") + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("token is not found"), "C_INTERNAL_ERROR", "")) return } - err = h.usecase.PingToken(input.Token, input.OrganizationId) + isActive, err := h.usecase.VerifyToken(r.Context(), token) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) - ErrorJSON(w, r, err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if !isActive { + ErrorJSON(w, r, httpErrors.NewUnauthorizedError(fmt.Errorf("token is not active"), "A_EXPIRED_TOKEN", "")) return } diff --git a/internal/delivery/http/cloud-account.go b/internal/delivery/http/cloud-account.go index 02809f30..2a01b9a7 100644 --- a/internal/delivery/http/cloud-account.go +++ b/internal/delivery/http/cloud-account.go @@ -3,10 +3,12 @@ package http import ( "fmt" "net/http" + "strconv" "github.com/google/uuid" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -20,23 +22,24 @@ type CloudAccountHandler struct { usecase usecase.ICloudAccountUsecase } -func NewCloudAccountHandler(h usecase.ICloudAccountUsecase) *CloudAccountHandler { +func NewCloudAccountHandler(h usecase.Usecase) *CloudAccountHandler { return &CloudAccountHandler{ - usecase: h, + usecase: h.CloudAccount, } } // CreateCloudAccount godoc -// @Tags CloudAccounts -// @Summary Create CloudAccount -// @Description Create CloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.CreateCloudAccountRequest true "create cloud setting request" -// @Success 200 {object} domain.CreateCloudAccountResponse -// @Router /organizations/{organizationId}/cloud-accounts [post] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Create CloudAccount +// @Description Create CloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.CreateCloudAccountRequest true "create cloud setting request" +// @Success 200 {object} domain.CreateCloudAccountResponse +// @Router /organizations/{organizationId}/cloud-accounts [post] +// @Security JWT func (h *CloudAccountHandler) CreateCloudAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -52,9 +55,9 @@ func (h *CloudAccountHandler) CreateCloudAccount(w http.ResponseWriter, r *http. return } - var dto domain.CloudAccount - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.CloudAccount + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } dto.OrganizationId = organizationId @@ -71,20 +74,21 @@ func (h *CloudAccountHandler) CreateCloudAccount(w http.ResponseWriter, r *http. } // GetCloudAccount godoc -// @Tags CloudAccounts -// @Summary Get CloudAccounts -// @Description Get CloudAccounts -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} domain.GetCloudAccountsResponse -// @Router /organizations/{organizationId}/cloud-accounts [get] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Get CloudAccounts +// @Description Get CloudAccounts +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetCloudAccountsResponse +// @Router /organizations/{organizationId}/cloud-accounts [get] +// @Security JWT func (h *CloudAccountHandler) GetCloudAccounts(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -94,11 +98,16 @@ func (h *CloudAccountHandler) GetCloudAccounts(w http.ResponseWriter, r *http.Re } urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return + pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.CloudAccountStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } } + cloudAccounts, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) @@ -108,30 +117,31 @@ func (h *CloudAccountHandler) GetCloudAccounts(w http.ResponseWriter, r *http.Re var out domain.GetCloudAccountsResponse out.CloudAccounts = make([]domain.CloudAccountResponse, len(cloudAccounts)) for i, cloudAccount := range cloudAccounts { - if err := serializer.Map(cloudAccount, &out.CloudAccounts[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), cloudAccount, &out.CloudAccounts[i]); err != nil { + log.Info(r.Context(), err) continue } } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetCloudAccount godoc -// @Tags CloudAccounts -// @Summary Get CloudAccount -// @Description Get CloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param cloudAccountId path string true "cloudAccountId" -// @Success 200 {object} domain.GetCloudAccountResponse -// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [get] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Get CloudAccount +// @Description Get CloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param cloudAccountId path string true "cloudAccountId" +// @Success 200 {object} domain.GetCloudAccountResponse +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [get] +// @Security JWT func (h *CloudAccountHandler) GetCloudAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["cloudAccountId"] @@ -153,24 +163,25 @@ func (h *CloudAccountHandler) GetCloudAccount(w http.ResponseWriter, r *http.Req } var out domain.GetCloudAccountResponse - if err := serializer.Map(cloudAccount, &out.CloudAccount); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), cloudAccount, &out.CloudAccount); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // UpdateCloudAccount godoc -// @Tags CloudAccounts -// @Summary Update CloudAccount -// @Description Update CloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdateCloudAccountRequest true "Update cloud setting request" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [put] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Update CloudAccount +// @Description Update CloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.UpdateCloudAccountRequest true "Update cloud setting request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [put] +// @Security JWT func (h *CloudAccountHandler) UpdateCloudAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["cloudAccountId"] @@ -183,7 +194,7 @@ func (h *CloudAccountHandler) UpdateCloudAccount(w http.ResponseWriter, r *http. ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) return } - log.DebugWithContext(r.Context(), "[TODO] organization check", organizationId) + log.Debug(r.Context(), "[TODO] organization check", organizationId) cloudAccountId, err := uuid.Parse(strId) if err != nil { @@ -198,9 +209,9 @@ func (h *CloudAccountHandler) UpdateCloudAccount(w http.ResponseWriter, r *http. return } - var dto domain.CloudAccount - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.CloudAccount + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } dto.ID = cloudAccountId dto.OrganizationId = organizationId @@ -215,17 +226,18 @@ func (h *CloudAccountHandler) UpdateCloudAccount(w http.ResponseWriter, r *http. } // DeleteCloudAccount godoc -// @Tags CloudAccounts -// @Summary Delete CloudAccount -// @Description Delete CloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.DeleteCloudAccountRequest true "Delete cloud setting request" -// @Param cloudAccountId path string true "cloudAccountId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [delete] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Delete CloudAccount +// @Description Delete CloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.DeleteCloudAccountRequest true "Delete cloud setting request" +// @Param cloudAccountId path string true "cloudAccountId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId} [delete] +// @Security JWT func (h *CloudAccountHandler) DeleteCloudAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) cloudAccountId, ok := vars["cloudAccountId"] @@ -247,32 +259,38 @@ func (h *CloudAccountHandler) DeleteCloudAccount(w http.ResponseWriter, r *http. return } - var dto domain.CloudAccount - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.CloudAccount + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } dto.ID = parsedId - err = h.usecase.Delete(r.Context(), dto) + cloudAccount, err := h.usecase.Delete(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) return } - ResponseJSON(w, r, http.StatusOK, nil) + out := domain.DeleteCloudAccountResponse{ + ID: cloudAccount.ID.String(), + Name: cloudAccount.Name, + } + + ResponseJSON(w, r, http.StatusOK, out) } // DeleteForceCloudAccount godoc -// @Tags CloudAccounts -// @Summary Delete Force CloudAccount -// @Description Delete Force CloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param cloudAccountId path string true "cloudAccountId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error [delete] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Delete Force CloudAccount +// @Description Delete Force CloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param cloudAccountId path string true "cloudAccountId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error [delete] +// @Security JWT func (h *CloudAccountHandler) DeleteForceCloudAccount(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) cloudAccountId, ok := vars["cloudAccountId"] @@ -287,26 +305,32 @@ func (h *CloudAccountHandler) DeleteForceCloudAccount(w http.ResponseWriter, r * return } - err = h.usecase.DeleteForce(r.Context(), parsedId) + cloudAccount, err := h.usecase.DeleteForce(r.Context(), parsedId) if err != nil { ErrorJSON(w, r, err) return } - ResponseJSON(w, r, http.StatusOK, nil) + out := domain.DeleteCloudAccountResponse{ + ID: cloudAccount.ID.String(), + Name: cloudAccount.Name, + } + + ResponseJSON(w, r, http.StatusOK, out) } // CheckCloudAccountName godoc -// @Tags CloudAccounts -// @Summary Check name for cloudAccount -// @Description Check name for cloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param name path string true "name" -// @Success 200 {object} domain.CheckCloudAccountNameResponse -// @Router /organizations/{organizationId}/cloud-accounts/name/{name}/existence [GET] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Check name for cloudAccount +// @Description Check name for cloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param name path string true "name" +// @Success 200 {object} domain.CheckCloudAccountNameResponse +// @Router /organizations/{organizationId}/cloud-accounts/name/{name}/existence [GET] +// @Security JWT func (h *CloudAccountHandler) CheckCloudAccountName(w http.ResponseWriter, r *http.Request) { user, ok := request.UserFrom(r.Context()) if !ok { @@ -339,16 +363,17 @@ func (h *CloudAccountHandler) CheckCloudAccountName(w http.ResponseWriter, r *ht } // CheckAwsAccountId godoc -// @Tags CloudAccounts -// @Summary Check awsAccountId for cloudAccount -// @Description Check awsAccountId for cloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param awsAccountId path string true "awsAccountId" -// @Success 200 {object} domain.CheckCloudAccountAwsAccountIdResponse -// @Router /organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence [GET] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Check awsAccountId for cloudAccount +// @Description Check awsAccountId for cloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param awsAccountId path string true "awsAccountId" +// @Success 200 {object} domain.CheckCloudAccountAwsAccountIdResponse +// @Router /organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence [GET] +// @Security JWT func (h *CloudAccountHandler) CheckAwsAccountId(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) awsAccountId, ok := vars["awsAccountId"] @@ -375,16 +400,17 @@ func (h *CloudAccountHandler) CheckAwsAccountId(w http.ResponseWriter, r *http.R } // GetResourceQuota godoc -// @Tags CloudAccounts -// @Summary Get resource quota by cloudAccount -// @Description Get resource quota by cloudAccount -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param cloudAccountId path string true "cloudAccountId" -// @Success 200 {object} domain.GetCloudAccountResourceQuotaResponse -// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota [GET] -// @Security JWT +// +// @Tags CloudAccounts +// @Summary Get resource quota by cloudAccount +// @Description Get resource quota by cloudAccount +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param cloudAccountId path string true "cloudAccountId" +// @Success 200 {object} domain.GetCloudAccountResourceQuotaResponse +// @Router /organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quota [GET] +// @Security JWT func (h *CloudAccountHandler) GetResourceQuota(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["cloudAccountId"] @@ -406,8 +432,8 @@ func (h *CloudAccountHandler) GetResourceQuota(w http.ResponseWriter, r *http.Re } var out domain.GetCloudAccountResourceQuotaResponse - if err := serializer.Map(resourceQuota, &out.ResourceQuota); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), resourceQuota, &out.ResourceQuota); err != nil { + log.Info(r.Context(), err) } out.Available = available diff --git a/internal/delivery/http/cluster.go b/internal/delivery/http/cluster.go index 7565403b..bd49b31b 100644 --- a/internal/delivery/http/cluster.go +++ b/internal/delivery/http/cluster.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -18,36 +19,33 @@ type ClusterHandler struct { usecase usecase.IClusterUsecase } -func NewClusterHandler(h usecase.IClusterUsecase) *ClusterHandler { +func NewClusterHandler(h usecase.Usecase) *ClusterHandler { return &ClusterHandler{ - usecase: h, + usecase: h.Cluster, } } // GetClusters godoc -// @Tags Clusters -// @Summary Get clusters -// @Description Get cluster list -// @Accept json -// @Produce json -// @Param organizationId query string false "organizationId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} domain.GetClustersResponse -// @Router /clusters [get] -// @Security JWT +// +// @Tags Clusters +// @Summary Get clusters +// @Description Get cluster list +// @Accept json +// @Produce json +// @Param organizationId query string false "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetClustersResponse +// @Router /clusters [get] +// @Security JWT func (h *ClusterHandler) GetClusters(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() organizationId := urlParams.Get("organizationId") - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } + pg := pagination.NewPagination(&urlParams) clusters, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) @@ -57,29 +55,37 @@ func (h *ClusterHandler) GetClusters(w http.ResponseWriter, r *http.Request) { var out domain.GetClustersResponse out.Clusters = make([]domain.ClusterResponse, len(clusters)) for i, cluster := range clusters { - if err := serializer.Map(cluster, &out.Clusters[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), cluster, &out.Clusters[i]); err != nil { + log.Info(r.Context(), err) continue } + + if cluster.Favorites != nil && len(*cluster.Favorites) > 0 { + out.Clusters[i].Favorited = true + } else { + out.Clusters[i].Favorited = false + } + } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetCluster godoc -// @Tags Clusters -// @Summary Get cluster -// @Description Get cluster detail -// @Accept json -// @Produce json -// @Param clusterId path string true "clusterId" -// @Success 200 {object} domain.Cluster -// @Router /clusters/{clusterId} [get] -// @Security JWT +// +// @Tags Clusters +// @Summary Get cluster +// @Description Get cluster detail +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} domain.GetClusterResponse +// @Router /clusters/{clusterId} [get] +// @Security JWT func (h *ClusterHandler) GetCluster(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -95,23 +101,24 @@ func (h *ClusterHandler) GetCluster(w http.ResponseWriter, r *http.Request) { } var out domain.GetClusterResponse - if err := serializer.Map(cluster, &out.Cluster); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), cluster, &out.Cluster); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetClusterSiteValues godoc -// @Tags Clusters -// @Summary Get cluster site values for creating -// @Description Get cluster site values for creating -// @Accept json -// @Produce json -// @Param clusterId path string true "clusterId" -// @Success 200 {object} domain.ClusterSiteValuesResponse -// @Router /clusters/{clusterId}/site-values [get] -// @Security JWT +// +// @Tags Clusters +// @Summary Get cluster site values for creating +// @Description Get cluster site values for creating +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} domain.ClusterSiteValuesResponse +// @Router /clusters/{clusterId}/site-values [get] +// @Security JWT func (h *ClusterHandler) GetClusterSiteValues(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -133,15 +140,16 @@ func (h *ClusterHandler) GetClusterSiteValues(w http.ResponseWriter, r *http.Req } // CreateCluster godoc -// @Tags Clusters -// @Summary Create cluster -// @Description Create cluster -// @Accept json -// @Produce json -// @Param body body domain.CreateClusterRequest true "create cluster request" -// @Success 200 {object} domain.CreateClusterResponse -// @Router /clusters [post] -// @Security JWT +// +// @Tags Clusters +// @Summary Create cluster +// @Description Create cluster +// @Accept json +// @Produce json +// @Param body body domain.CreateClusterRequest true "create cluster request" +// @Success 200 {object} domain.CreateClusterResponse +// @Router /clusters [post] +// @Security JWT func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) { input := domain.CreateClusterRequest{} err := UnmarshalRequestInput(r, &input) @@ -150,19 +158,17 @@ func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) { return } - var dto domain.Cluster - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.Cluster + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } - - if err = serializer.Map(input, &dto.Conf); err != nil { - log.InfoWithContext(r.Context(), err) + cId, err := uuid.Parse(input.CloudAccountId) + if err == nil { + dto.CloudAccountId = &cId } - // [TODO] set default value dto.ClusterType = domain.ClusterType_USER - dto.Conf.SetDefault() - log.InfoWithContext(r.Context(), dto.Conf) + dto.SetDefaultConf() //txHandle := r.Context().Value("txHandle").(*gorm.DB) clusterId := domain.ClusterId("") @@ -192,15 +198,16 @@ func (h *ClusterHandler) CreateCluster(w http.ResponseWriter, r *http.Request) { } // ImportCluster godoc -// @Tags Clusters -// @Summary Import cluster -// @Description Import cluster -// @Accept json -// @Produce json -// @Param body body domain.ImportClusterRequest true "import cluster request" -// @Success 200 {object} domain.ImportClusterResponse -// @Router /clusters/import [post] -// @Security JWT +// +// @Tags Clusters +// @Summary Import cluster +// @Description Import cluster +// @Accept json +// @Produce json +// @Param body body domain.ImportClusterRequest true "import cluster request" +// @Success 200 {object} domain.ImportClusterResponse +// @Router /clusters/import [post] +// @Security JWT func (h *ClusterHandler) ImportCluster(w http.ResponseWriter, r *http.Request) { input := domain.ImportClusterRequest{} err := UnmarshalRequestInput(r, &input) @@ -209,18 +216,13 @@ func (h *ClusterHandler) ImportCluster(w http.ResponseWriter, r *http.Request) { return } - var dto domain.Cluster - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) - } - - if err = serializer.Map(input, &dto.Conf); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.Cluster + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } - dto.Conf.SetDefault() - log.InfoWithContext(r.Context(), dto.Conf) + dto.SetDefaultConf() - dto.CloudAccountId = uuid.Nil + dto.CloudAccountId = nil clusterId, err := h.usecase.Import(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) @@ -234,15 +236,16 @@ func (h *ClusterHandler) ImportCluster(w http.ResponseWriter, r *http.Request) { } // InstallCluster godoc -// @Tags Clusters -// @Summary Install cluster on tks cluster -// @Description Install cluster on tks cluster -// @Accept json -// @Produce json -// @Param clusterId path string true "clusterId" -// @Success 200 {object} nil -// @Router /clusters/{clusterId}/install [post] -// @Security JWT +// +// @Tags Clusters +// @Summary Install cluster on tks cluster +// @Description Install cluster on tks cluster +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} nil +// @Router /clusters/{clusterId}/install [post] +// @Security JWT func (h *ClusterHandler) InstallCluster(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -261,15 +264,16 @@ func (h *ClusterHandler) InstallCluster(w http.ResponseWriter, r *http.Request) } // DeleteCluster godoc -// @Tags Clusters -// @Summary Delete cluster -// @Description Delete cluster -// @Accept json -// @Produce json -// @Param clusterId path string true "clusterId" -// @Success 200 {object} domain.Cluster -// @Router /clusters/{clusterId} [delete] -// @Security JWT +// +// @Tags Clusters +// @Summary Delete cluster +// @Description Delete cluster +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} nil +// @Router /clusters/{clusterId} [delete] +// @Security JWT func (h *ClusterHandler) DeleteCluster(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -288,14 +292,15 @@ func (h *ClusterHandler) DeleteCluster(w http.ResponseWriter, r *http.Request) { } // CreateBootstrapKubeconfig godoc -// @Tags Clusters -// @Summary Create bootstrap kubeconfig for BYOH -// @Description Create bootstrap kubeconfig for BYOH -// @Accept json -// @Produce json -// @Success 200 {object} domain.CreateBootstrapKubeconfigResponse -// @Router /clusters/{clusterId}/bootstrap-kubeconfig [post] -// @Security JWT +// +// @Tags Clusters +// @Summary Create bootstrap kubeconfig for BYOH +// @Description Create bootstrap kubeconfig for BYOH +// @Accept json +// @Produce json +// @Success 200 {object} domain.CreateBootstrapKubeconfigResponse +// @Router /clusters/{clusterId}/bootstrap-kubeconfig [post] +// @Security JWT func (h *ClusterHandler) CreateBootstrapKubeconfig(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -316,14 +321,15 @@ func (h *ClusterHandler) CreateBootstrapKubeconfig(w http.ResponseWriter, r *htt } // GetBootstrapKubeconfig godoc -// @Tags Clusters -// @Summary Get bootstrap kubeconfig for BYOH -// @Description Get bootstrap kubeconfig for BYOH -// @Accept json -// @Produce json -// @Success 200 {object} domain.GetBootstrapKubeconfigResponse -// @Router /clusters/{clusterId}/bootstrap-kubeconfig [get] -// @Security JWT +// +// @Tags Clusters +// @Summary Get bootstrap kubeconfig for BYOH +// @Description Get bootstrap kubeconfig for BYOH +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetBootstrapKubeconfigResponse +// @Router /clusters/{clusterId}/bootstrap-kubeconfig [get] +// @Security JWT func (h *ClusterHandler) GetBootstrapKubeconfig(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) clusterId, ok := vars["clusterId"] @@ -345,15 +351,16 @@ func (h *ClusterHandler) GetBootstrapKubeconfig(w http.ResponseWriter, r *http.R } // GetNodes godoc -// @Tags Clusters -// @Summary Get nodes information for BYOH -// @Description Get nodes information for BYOH -// @Accept json -// @Produce json -// @Param clusterId path string true "clusterId" -// @Success 200 {object} domain.GetClusterNodesResponse -// @Router /clusters/{clusterId}/nodes [get] -// @Security JWT +// +// @Tags Clusters +// @Summary Get nodes information for BYOH +// @Description Get nodes information for BYOH +// @Accept json +// @Produce json +// @Param clusterId path string true "clusterId" +// @Success 200 {object} domain.GetClusterNodesResponse +// @Router /clusters/{clusterId}/nodes [get] +// @Security JWT func (h *ClusterHandler) GetNodes(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["clusterId"] @@ -381,24 +388,24 @@ func (h *ClusterHandler) GetNodes(w http.ResponseWriter, r *http.Request) { func (h *ClusterHandler) GetKubernetesInfo(w http.ResponseWriter, r *http.Request) { // GetKubernetesInfo godoc - // @Tags Clusters - // @Summary Get kubernetes info - // @Description Get kubernetes info for cluster - // @Accept json - // @Produce json - // @Param clusterId path string true "clusterId" - // @Success 200 {object} ClusterKubeInfo - // @Router /clusters/{clusterId}/kubeInfo [get] + // @Tags Clusters + // @Summary Get kubernetes info + // @Description Get kubernetes info for cluster + // @Accept json + // @Produce json + // @Param clusterId path string true "clusterId" + // @Success 200 {object} ClusterKubeInfo + // @Router /clusters/{clusterId}/kubeInfo [get] /* vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") } clientset_user, err := h.GetClientFromClusterId(clusterId) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get clientset for clusterId ", clusterId) + log.Error(r.Context(),"Failed to get clientset for clusterId ", clusterId) InternalServerError(w) return } @@ -412,21 +419,21 @@ func (h *ClusterHandler) GetKubernetesInfo(w http.ResponseWriter, r *http.Reques if err == nil { out.KubeInfo.Pods = len(pods.Items) } else { - log.ErrorWithContext(r.Context(),"Failed to get pods. err : ", err) + log.Error(r.Context(),"Failed to get pods. err : ", err) } nodes, err := clientset_user.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err == nil { out.KubeInfo.Nodes = len(nodes.Items) } else { - log.ErrorWithContext(r.Context(),"Failed to get nodes. err : ", err) + log.Error(r.Context(),"Failed to get nodes. err : ", err) } services, err := clientset_user.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{}) if err == nil { out.KubeInfo.Services = len(services.Items) } else { - log.ErrorWithContext(r.Context(),"Failed to get services. err : ", err) + log.Error(r.Context(),"Failed to get services. err : ", err) } @@ -434,7 +441,7 @@ func (h *ClusterHandler) GetKubernetesInfo(w http.ResponseWriter, r *http.Reques if err == nil { out.KubeInfo.Namespaces = len(namespaces.Items) } else { - log.ErrorWithContext(r.Context(),"Failed to get namespaces. err : ", err) + log.Error(r.Context(),"Failed to get namespaces. err : ", err) } @@ -442,7 +449,7 @@ func (h *ClusterHandler) GetKubernetesInfo(w http.ResponseWriter, r *http.Reques if err == nil { out.KubeInfo.Version = version } else { - log.ErrorWithContext(r.Context(),"Failed to get kubernetes version. err : ", err) + log.Error(r.Context(),"Failed to get kubernetes version. err : ", err) } ResponseJSON(w, r, http.StatusOK, out) @@ -451,26 +458,26 @@ func (h *ClusterHandler) GetKubernetesInfo(w http.ResponseWriter, r *http.Reques func (h *ClusterHandler) GetClusterApplications(w http.ResponseWriter, r *http.Request) { // GetClusterApplications godoc - // @Tags Clusters - // @Summary Get application list - // @Description Get application list by clusterId - // @Accept json - // @Produce json - // @Param clusterId path string false "clusterId" - // @Success 200 {object} []ApplicationJson - // @Router /clusters/{clusterId}/applications [get] + // @Tags Clusters + // @Summary Get application list + // @Description Get application list by clusterId + // @Accept json + // @Produce json + // @Param clusterId path string false "clusterId" + // @Success 200 {object} []ApplicationJson + // @Router /clusters/{clusterId}/applications [get] /* vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") ErrorJSON(w, r, "invalid clusterId", http.StatusBadRequest) } var applications = []*pb.AppGroup{} res, err := appInfoClient.GetAppGroupsByClusterID(context.TODO(), &pb.IDRequest{ID: clusterId}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get appgroups err : ", err) + log.Error(r.Context(),"Failed to get appgroups err : ", err) InternalServerError(w) return } @@ -495,7 +502,7 @@ func (h *ClusterHandler) GetClusterApplicationsKubeInfo(w http.ResponseWriter, r vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") ErrorJSON(w, r, "invalid clusterId", http.StatusBadRequest) } @@ -506,7 +513,7 @@ func (h *ClusterHandler) GetClusterApplicationsKubeInfo(w http.ResponseWriter, r res, err := appInfoClient.GetAppGroupsByClusterID(context.TODO(), &pb.IDRequest{Id: clusterId}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get appgroups err : ", err) + log.Error(r.Context(),"Failed to get appgroups err : ", err) InternalServerError(w) return } @@ -523,19 +530,19 @@ func (h *ClusterHandler) GetClusterApplicationsKubeInfo(w http.ResponseWriter, r clientset, err := h.GetClientFromClusterId(appGroup.GetClusterId()) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get clientset for clusterId", appGroup.GetClusterId()) + log.Error(r.Context(),"Failed to get clientset for clusterId", appGroup.GetClusterId()) continue } pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get pods. ", err) + log.Error(r.Context(),"Failed to get pods. ", err) continue } services, err := clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get service. ", err) + log.Error(r.Context(),"Failed to get service. ", err) continue } @@ -600,19 +607,19 @@ func (h *ClusterHandler) GetClusterApplicationsKubeInfo(w http.ResponseWriter, r func (h *ClusterHandler) GetClusterKubeConfig(w http.ResponseWriter, r *http.Request) { // GetClusterKubeConfig godoc - // @Tags Clusters - // @Summary Get kubernetes kubeconfig - // @Description Get kubernetes kubeconfig for cluster - // @Accept json - // @Produce json - // @Param clusterId path string true "clusterId" - // @Success 200 {object} object - // @Router /clusters/{clusterId}/kubeconfig [get] + // @Tags Clusters + // @Summary Get kubernetes kubeconfig + // @Description Get kubernetes kubeconfig for cluster + // @Accept json + // @Produce json + // @Param clusterId path string true "clusterId" + // @Success 200 {object} object + // @Router /clusters/{clusterId}/kubeconfig [get] /* vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") ErrorJSON(w, r, "invalid clusterId", http.StatusBadRequest) return } @@ -639,60 +646,60 @@ func (h *ClusterHandler) GetClusterKubeConfig(w http.ResponseWriter, r *http.Req func (h *ClusterHandler) GetClusterKubeResources(w http.ResponseWriter, r *http.Request) { // GetClusterKubeResources godoc - // @Tags Clusters - // @Summary Get kubernetes resources - // @Description Get kubernetes resources - // @Accept json - // @Produce json - // @Param clusterId path string true "clusterId" - // @Success 200 {object} ClusterJson - // @Router /clusters/{clusterId}/kube-resources [get] + // @Tags Clusters + // @Summary Get kubernetes resources + // @Description Get kubernetes resources + // @Accept json + // @Produce json + // @Param clusterId path string true "clusterId" + // @Success 200 {object} ClusterJson + // @Router /clusters/{clusterId}/kube-resources [get] /* vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") ErrorJSON(w, r, "Invalid clusterId", http.StatusBadRequest) } clientset, err := h.GetClientFromClusterId(clusterId) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get clientset for clusterId", clusterId) + log.Error(r.Context(),"Failed to get clientset for clusterId", clusterId) InternalServerError(w) return } pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get pods", err) + log.Error(r.Context(),"Failed to get pods", err) InternalServerError(w) return } services, err := clientset.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get services", err) + log.Error(r.Context(),"Failed to get services", err) InternalServerError(w) return } namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get namespaces", err) + log.Error(r.Context(),"Failed to get namespaces", err) InternalServerError(w) return } events, err := clientset.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get events", err) + log.Error(r.Context(),"Failed to get events", err) InternalServerError(w) return } nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get events", err) + log.Error(r.Context(),"Failed to get events", err) InternalServerError(w) return } @@ -832,25 +839,25 @@ func (h *ClusterHandler) GetClusterKubeResources(w http.ResponseWriter, r *http. func (h *ClusterHandler) SetIstioLabel(w http.ResponseWriter, r *http.Request) { // SetIstioLabel godoc - // @Tags Clusters - // @Summary Set Istio label to namespace - // @Description Set istio label to namespace on kubernetes - // @Accept json - // @Produce json - // @Param clusterId path string true "clusterId" - // @Success 200 {object} object - // @Router /clusters/{clusterId}/kube-resources/{namespace}/istio-label [post] + // @Tags Clusters + // @Summary Set Istio label to namespace + // @Description Set istio label to namespace on kubernetes + // @Accept json + // @Produce json + // @Param clusterId path string true "clusterId" + // @Success 200 {object} object + // @Router /clusters/{clusterId}/kube-resources/{namespace}/istio-label [post] /* vars := mux.Vars(r) clusterId, ok := vars["clusterId"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get clusterId") + log.Error(r.Context(),"Failed to get clusterId") ErrorJSON(w, r, "invalid clusterId", http.StatusBadRequest) return } namespace, ok := vars["namespace"] if !ok { - log.ErrorWithContext(r.Context(),"Failed to get namespace") + log.Error(r.Context(),"Failed to get namespace") ErrorJSON(w, r, "invalid namespace", http.StatusBadRequest) return } @@ -860,17 +867,17 @@ func (h *ClusterHandler) SetIstioLabel(w http.ResponseWriter, r *http.Request) { } body, err := io.ReadAll(r.Body) if err != nil { - log.ErrorWithContext(r.Context(),err) + log.Error(r.Context(),err) return } err = json.Unmarshal(body, &input) if err != nil { - log.ErrorWithContext(r.Context(),err) + log.Error(r.Context(),err) ErrorJSON(w, r, "invalid json", http.StatusBadRequest) return } - log.InfoWithContext(r.Context(),input) + log.Info(r.Context(),input) if input.Value != "enabled" && input.Value != "disabled" { ErrorJSON(w, r, "invalid value", http.StatusBadRequest) @@ -881,7 +888,7 @@ func (h *ClusterHandler) SetIstioLabel(w http.ResponseWriter, r *http.Request) { clientset, err := h.GetClientFromClusterId(clusterId) if err != nil { - log.ErrorWithContext(r.Context(),"Failed to get clientset for clusterId", clusterId) + log.Error(r.Context(),"Failed to get clientset for clusterId", clusterId) InternalServerError(w) return } diff --git a/internal/delivery/http/dashboard.go b/internal/delivery/http/dashboard.go index 8a5ae87b..99e3f04b 100644 --- a/internal/delivery/http/dashboard.go +++ b/internal/delivery/http/dashboard.go @@ -4,8 +4,13 @@ import ( "fmt" "net/http" "strings" + "time" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + policytemplate "github.com/openinfradev/tks-api/internal/policy-template" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" "github.com/openinfradev/tks-api/pkg/domain" @@ -13,29 +18,235 @@ import ( "github.com/openinfradev/tks-api/pkg/log" ) +type IDashboardHandler interface { + CreateDashboard(w http.ResponseWriter, r *http.Request) + GetDashboard(w http.ResponseWriter, r *http.Request) + UpdateDashboard(w http.ResponseWriter, r *http.Request) + GetCharts(w http.ResponseWriter, r *http.Request) + GetChart(w http.ResponseWriter, r *http.Request) + GetStacks(w http.ResponseWriter, r *http.Request) + GetResources(w http.ResponseWriter, r *http.Request) + GetPolicyStatus(w http.ResponseWriter, r *http.Request) + GetPolicyUpdate(w http.ResponseWriter, r *http.Request) + GetPolicyEnforcement(w http.ResponseWriter, r *http.Request) + GetPolicyViolation(w http.ResponseWriter, r *http.Request) + GetPolicyViolationLog(w http.ResponseWriter, r *http.Request) + GetPolicyStatistics(w http.ResponseWriter, r *http.Request) + GetWorkload(w http.ResponseWriter, r *http.Request) + GetPolicyViolationTop5(w http.ResponseWriter, r *http.Request) +} + type DashboardHandler struct { - usecase usecase.IDashboardUsecase + usecase usecase.IDashboardUsecase + organizationUsecase usecase.IOrganizationUsecase + policyUsecase usecase.IPolicyUsecase + systemNotificationUsecase usecase.ISystemNotificationUsecase } -func NewDashboardHandler(h usecase.IDashboardUsecase) *DashboardHandler { +func NewDashboardHandler(h usecase.Usecase) IDashboardHandler { return &DashboardHandler{ - usecase: h, + usecase: h.Dashboard, + organizationUsecase: h.Organization, + policyUsecase: h.Policy, + systemNotificationUsecase: h.SystemNotification, + } +} + +// CreateDashboard godoc +// +// @Tags Dashboards +// @Summary Create new dashboard +// @Description Create new dashboard +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param request body domain.CreateDashboardRequest true "Request body to create dashboard" +// @Success 200 {object} domain.CreateDashboardResponse +// @Router /organizations/{organizationId}/dashboards [post] +// @Security JWT +func (h *DashboardHandler) CreateDashboard(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + var dashboardReq domain.CreateDashboardRequest + if err := UnmarshalRequestInput(r, &dashboardReq); err != nil { + ErrorJSON(w, r, err) + return + } + content, err := MarshalToString(r.Context(), dashboardReq.Contents) + if err != nil { + ErrorJSON(w, r, err) + return + } + + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + log.Error(r.Context(), "Failed to retrieve user info from request") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve user info from request")) + } + userId := requestUserInfo.GetUserId() + + dashboard, err := h.usecase.GetDashboard(r.Context(), organizationId, userId.String(), dashboardReq.DashboardKey) + if err == nil && dashboard != nil { + log.Error(r.Context(), "Dashboard already exists") + ResponseJSON(w, r, http.StatusInternalServerError, "Dashboard already exists") + return + } + + dashboard = &model.Dashboard{ + OrganizationId: organizationId, + UserId: userId, + Key: dashboardReq.DashboardKey, + Content: content, + IsAdmin: false, + } + log.Info(r.Context(), "Processing CREATE request for dashboard") + + dashboardId, err := h.usecase.CreateDashboard(r.Context(), dashboard) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + out := domain.CreateDashboardResponse{DashboardId: dashboardId} + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetDashboard godoc +// +// @Tags Dashboards +// @Summary Get dashboard +// @Description Get dashboard +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param dashboardKey path string true "Dashboard Key" +// @Success 200 {array} domain.GetDashboardResponse +// @Router /organizations/{organizationId}/dashboards/{dashboardKey} [get] +// @Security JWT +func (h *DashboardHandler) GetDashboard(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + dashboardKey, ok := vars["dashboardKey"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid dashboardKey", dashboardKey), + "", "")) + return + } + + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + log.Error(r.Context(), "Failed to retrieve user info from request") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve user info from request")) + } + userId := requestUserInfo.GetUserId().String() + + dashboard, err := h.usecase.GetDashboard(r.Context(), organizationId, userId, dashboardKey) + if err != nil { + log.Error(r.Context(), "Failed to retrieve dashboard", err) + ErrorJSON(w, r, err) + return + } + if dashboard == nil { + ResponseJSON(w, r, http.StatusOK, nil) + return + } + if len(dashboard.Content) == 0 { + ResponseJSON(w, r, http.StatusOK, []domain.GetDashboardResponse{}) + return + } + + var dashboardRes []domain.GetDashboardResponse + if err := UnmarshalFromString(r.Context(), dashboard.Content, &dashboardRes); err != nil { + ErrorJSON(w, r, err) + } + ResponseJSON(w, r, http.StatusOK, dashboardRes) +} + +// UpdateDashboard godoc +// +// @Tags Dashboards +// @Summary Update dashboard +// @Description Update dashboard +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param request body domain.UpdateDashboardRequest true "Request body to update dashboard" +// @Success 200 {object} domain.CommonDashboardResponse +// @Router /organizations/{organizationId}/dashboards/{dashboardKey} [put] +// @Security JWT +func (h *DashboardHandler) UpdateDashboard(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return } + dashboardKey, ok := vars["dashboardKey"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid dashboardKey", dashboardKey), + "", "")) + return + } + + var dashboardReq []domain.UpdateDashboardRequest + if err := UnmarshalRequestInput(r, &dashboardReq); err != nil { + ErrorJSON(w, r, err) + return + } + content, err := MarshalToString(r.Context(), dashboardReq) + if err != nil { + ErrorJSON(w, r, err) + return + } + + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + log.Error(r.Context(), "Failed to retrieve user info from request") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve user info from request")) + } + userId := requestUserInfo.GetUserId().String() + + dashboard, err := h.usecase.GetDashboard(r.Context(), organizationId, userId, dashboardKey) + if err != nil || dashboard == nil { + log.Error(r.Context(), "Failed to retrieve dashboard", err) + ErrorJSON(w, r, err) + return + } + + dashboard.Content = content + if err := h.usecase.UpdateDashboard(r.Context(), dashboard); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + ResponseJSON(w, r, http.StatusOK, domain.CommonDashboardResponse{Result: "OK"}) } // GetCharts godoc -// @Tags Dashboards -// @Summary Get charts data -// @Description Get charts data -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param chartType query string false "chartType" -// @Param duration query string true "duration" -// @Param interval query string true "interval" -// @Success 200 {object} domain.GetDashboardChartsResponse -// @Router /organizations/{organizationId}/dashboard/charts [get] -// @Security JWT +// +// @Tags Dashboard Widgets +// @Summary Get charts data +// @Description Get charts data +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param chartType query string false "chartType" +// @Param duration query string true "duration" +// @Param interval query string true "interval" +// @Success 200 {object} domain.GetDashboardChartsResponse +// @Router /organizations/{organizationId}/dashboards/widgets/charts [get] +// @Security JWT func (h *DashboardHandler) GetCharts(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -73,8 +284,8 @@ func (h *DashboardHandler) GetCharts(w http.ResponseWriter, r *http.Request) { var out domain.GetDashboardChartsResponse out.Charts = make([]domain.DashboardChartResponse, len(charts)) for i, chart := range charts { - if err := serializer.Map(chart, &out.Charts[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), chart, &out.Charts[i]); err != nil { + log.Info(r.Context(), err) continue } } @@ -82,19 +293,20 @@ func (h *DashboardHandler) GetCharts(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, out) } -// GetCharts godoc -// @Tags Dashboards -// @Summary Get chart data -// @Description Get chart data -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param chartType path string true "chartType" -// @Param duration query string true "duration" -// @Param interval query string true "interval" -// @Success 200 {object} domain.GetDashboardChartResponse -// @Router /organizations/{organizationId}/dashboard/charts/{chartType} [get] -// @Security JWT +// GetChart godoc +// +// @Tags Dashboard Widgets +// @Summary Get chart data +// @Description Get chart data +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param chartType path string true "chartType" +// @Param duration query string true "duration" +// @Param interval query string true "interval" +// @Success 200 {object} domain.GetDashboardChartResponse +// @Router /organizations/{organizationId}/dashboards/widgets/charts/{chartType} [get] +// @Security JWT func (h *DashboardHandler) GetChart(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -151,23 +363,24 @@ func (h *DashboardHandler) GetChart(w http.ResponseWriter, r *http.Request) { } var out domain.DashboardChartResponse - if err := serializer.Map(charts[0], &out); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), charts[0], &out); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetStacks godoc -// @Tags Dashboards -// @Summary Get stacks -// @Description Get stacks -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} domain.GetDashboardStacksResponse -// @Router /organizations/{organizationId}/dashboard/stacks [get] -// @Security JWT +// +// @Tags Dashboard Widgets +// @Summary Get stacks +// @Description Get stacks +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.GetDashboardStacksResponse +// @Router /organizations/{organizationId}/dashboards/widgets/stacks [get] +// @Security JWT func (h *DashboardHandler) GetStacks(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -190,8 +403,8 @@ func (h *DashboardHandler) GetStacks(w http.ResponseWriter, r *http.Request) { var out domain.GetDashboardStacksResponse out.Stacks = make([]domain.DashboardStackResponse, len(stacks)) for i, stack := range stacks { - if err := serializer.Map(stack, &out.Stacks[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), stack, &out.Stacks[i]); err != nil { + log.Info(r.Context(), err) continue } } @@ -200,15 +413,16 @@ func (h *DashboardHandler) GetStacks(w http.ResponseWriter, r *http.Request) { } // GetResources godoc -// @Tags Dashboards -// @Summary Get resources -// @Description Get resources -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} domain.GetDashboardResourcesResponse -// @Router /organizations/{organizationId}/dashboard/resources [get] -// @Security JWT +// +// @Tags Dashboard Widgets +// @Summary Get resources +// @Description Get resources +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.GetDashboardResourcesResponse +// @Router /organizations/{organizationId}/dashboards/widgets/resources [get] +// @Security JWT func (h *DashboardHandler) GetResources(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -227,9 +441,373 @@ func (h *DashboardHandler) GetResources(w http.ResponseWriter, r *http.Request) return } var out domain.GetDashboardResourcesResponse - if err := serializer.Map(resources, &out.Resources); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), resources, &out.Resources); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyStatus godoc +// +// @Tags Dashboard Widgets +// @Summary Get policy status +// @Description Get policy status +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetDashboardPolicyStatusResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-status [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyStatus(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + organization, err := h.organizationUsecase.Get(r.Context(), organizationId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve organization") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve organization")) + return + } + + tksClusters, err := policytemplate.GetTksClusterCRs(r.Context(), organization.PrimaryClusterId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve tkscluster list", err) + ErrorJSON(w, r, err) + return + } + + var policyStatus domain.DashboardPolicyStatus + for _, c := range tksClusters { + switch status := c.Status.TKSProxy.Status; status { + case "ready": + policyStatus.Normal++ + case "warn": + policyStatus.Warning++ + case "error": + policyStatus.Error++ + default: + continue + } + } + + var out domain.GetDashboardPolicyStatusResponse + out.PolicyStatus = policyStatus + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyUpdate godoc +// +// @Tags Dashboard Widgets +// @Summary Get the number of policytemplates that need to be updated +// @Description Get the number of policytemplates that need to be updated +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetDashboardPolicyUpdateResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-update [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyUpdate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + organization, err := h.organizationUsecase.Get(r.Context(), organizationId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve organization") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve organization")) + return + } + + policyTemplates, err := policytemplate.GetTksPolicyTemplateCRs(r.Context(), organization.PrimaryClusterId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve policytemplate list", err) + ErrorJSON(w, r, err) + return + } + policies, err := policytemplate.GetTksPolicyCRs(r.Context(), organization.PrimaryClusterId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve policy list", err) + ErrorJSON(w, r, err) + return + } + + dpu, err := h.usecase.GetPolicyUpdate(r.Context(), policyTemplates, policies) + if err != nil { + log.Error(r.Context(), "Failed to make policy update status", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetDashboardPolicyUpdateResponse + out.PolicyUpdate = dpu + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyEnforcement godoc +// +// @Tags Dashboard Widgets +// @Summary Get the number of policy enforcement +// @Description Get the number of policy enforcement +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetDashboardPolicyEnforcementResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-enforcement [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyEnforcement(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + organization, err := h.organizationUsecase.Get(r.Context(), organizationId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve organization") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve organization")) + return + } + + bcd, err := h.usecase.GetPolicyEnforcement(r.Context(), organizationId, organization.PrimaryClusterId) + if err != nil { + log.Error(r.Context(), "Failed to make policy bar chart data", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetDashboardPolicyEnforcementResponse + out.ChartType = "PolicyEnforcement" + out.OrganizationId = organizationId + out.Name = "정책 적용 현황" + out.Description = "정책 적용 현황 통계 데이터" + out.ChartData = *bcd + out.UpdatedAt = time.Now() + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyViolation godoc +// +// @Tags Dashboard Widgets +// @Summary Get the number of policy violation +// @Description Get the number of policy violation +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param duration query string true "duration" +// @Param interval query string true "interval" +// @Success 200 {object} domain.GetDashboardPolicyViolationResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-violation [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyViolation(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + query := r.URL.Query() + duration := query.Get("duration") + if duration == "" { + duration = "1d" // default + } + + interval := query.Get("interval") + if interval == "" { + interval = "1d" // default + } + + bcd, err := h.usecase.GetPolicyViolation(r.Context(), organizationId, duration, interval) + if err != nil { + log.Error(r.Context(), "Failed to make policy bar chart data", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetDashboardPolicyViolationResponse + out.ChartType = "PolicyViolation" + out.OrganizationId = organizationId + out.Name = "정책 위반 현황" + out.Description = "정책 위반 현황 통계 데이터" + out.ChartData = *bcd + out.UpdatedAt = time.Now() + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyViolationLog godoc +// +// @Tags Dashboard Widgets +// @Summary Get policy violation log +// @Description Get policy violation log +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetPolicyNotificationsResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-violation-log [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyViolationLog(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + + policyNotifications, err := h.systemNotificationUsecase.FetchPolicyNotifications(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetPolicyNotificationsResponse + out.PolicyNotifications = make([]domain.PolicyNotificationResponse, len(policyNotifications)) + for i, policyNotification := range policyNotifications { + if err := serializer.Map(r.Context(), policyNotification, &out.PolicyNotifications[i]); err != nil { + log.Info(r.Context(), err) + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyStatistics godoc +// +// @Tags Dashboard Widgets +// @Summary Get policy violation log +// @Description Get policy violation log +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetDashboardPolicyStatisticsResponse +// @Router /organizations/{organizationId}/dashboards/widgets/policy-statistics [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyStatistics(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + psr, err := h.policyUsecase.GetPolicyStatistics(r.Context(), organizationId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + out := domain.GetDashboardPolicyStatisticsResponse{PolicyStatisticsResponse: *psr} + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetWorkload godoc +// +// @Tags Dashboard Widgets +// @Summary Get workloads +// @Description Get workloads +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.GetDashboardWorkloadResponse +// @Router /organizations/{organizationId}/dashboards/widgets/workload [get] +// @Security JWT +func (h *DashboardHandler) GetWorkload(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + dwr, err := h.usecase.GetWorkload(r.Context(), organizationId) + if err != nil { + log.Error(r.Context(), "Failed to make workload", err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, dwr) +} + +// GetPolicyViolationTop5 godoc +// +// @Tags Dashboard Widgets +// @Summary Get policy violation top5 +// @Description Get policy violation top5 +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param duration query string true "duration" +// @Param interval query string true "interval" +// @Success 200 {object} domain.GetDashboardPolicyViolationTop5Response +// @Router /organizations/{organizationId}/dashboards/widgets/policy-violation-top5 [get] +// @Security JWT +func (h *DashboardHandler) GetPolicyViolationTop5(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("%s: invalid organizationId", organizationId), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + query := r.URL.Query() + duration := query.Get("duration") + if duration == "" { + duration = "1d" // default + } + + interval := query.Get("interval") + if interval == "" { + interval = "1d" // default + } + + bcd, err := h.usecase.GetPolicyViolationTop5(r.Context(), organizationId, duration, interval) + if err != nil { + log.Error(r.Context(), "Failed to make policy bar chart data", err) + ErrorJSON(w, r, err) + return } + var out domain.GetDashboardPolicyViolationTop5Response + out.ChartType = "PolicyViolationTop5" + out.OrganizationId = organizationId + out.Name = "정책 위반 Top5" + out.Description = "정책 위반 Top5 데이터" + out.ChartData = *bcd + out.UpdatedAt = time.Now() ResponseJSON(w, r, http.StatusOK, out) } diff --git a/internal/delivery/http/handler.go b/internal/delivery/http/handler.go index 46cdf4db..ffb133b5 100644 --- a/internal/delivery/http/handler.go +++ b/internal/delivery/http/handler.go @@ -1,15 +1,15 @@ package http import ( + "bytes" + "context" "encoding/json" "errors" - "fmt" "io" "net/http" ut "github.com/go-playground/universal-translator" validator_ "github.com/go-playground/validator/v10" - "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/validator" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" @@ -28,7 +28,7 @@ func init() { } func ErrorJSON(w http.ResponseWriter, r *http.Request, err error) { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) errorResponse, status := httpErrors.ErrorResponse(err) ResponseJSON(w, r, status, errorResponse) } @@ -41,15 +41,8 @@ func ResponseJSON(w http.ResponseWriter, r *http.Request, httpStatus int, data i w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(httpStatus) - responseStr := helper.ModelToJson(out) - if len(responseStr) > MAX_LOG_LEN { - log.InfoWithContext(r.Context(), fmt.Sprintf("[API_RESPONSE] [%s]", responseStr[:MAX_LOG_LEN-1])) - } else { - log.InfoWithContext(r.Context(), fmt.Sprintf("[API_RESPONSE] [%s]", responseStr)) - } - log.DebugWithContext(r.Context(), fmt.Sprintf("[API_RESPONSE] [%s]", responseStr)) if err := json.NewEncoder(w).Encode(out); err != nil { - log.ErrorWithContext(r.Context(), err) + log.Error(r.Context(), err) } } @@ -58,6 +51,7 @@ func UnmarshalRequestInput(r *http.Request, in any) error { if err != nil { return err } + r.Body = io.NopCloser(bytes.NewBuffer(body)) err = json.Unmarshal(body, &in) if err != nil { @@ -77,6 +71,22 @@ func UnmarshalRequestInput(r *http.Request, in any) error { return nil } +// Http Request가 아닌 경우에도 domain 객체 validate가 필요한 경우 호출 +// 예를 들어 정책의 match를 RawYaml로 전달받았을 경우 이를 domain.Match 객체로 unmarshalling 한 후 domain.Match를 이용해서 validate 가능 +func ValidateDomainObject(in any) error { + err := validate.Struct(in) + if err != nil { + var valErrs validator_.ValidationErrors + if errors.As(err, &valErrs) { + for _, e := range valErrs { + return httpErrors.NewBadRequestError(err, "", e.Translate(trans)) + } + } + } + + return nil +} + /* func (h *APIHandler) GetClientFromClusterId(clusterId string) (*kubernetes.Clientset, error) { const prefix = "CACHE_KEY_KUBE_CLIENT_" @@ -117,10 +127,28 @@ func (h *APIHandler) AddHistory(r *http.Request, projectId string, historyType s err := h.Repository.AddHistory(userId, projectId, historyType, description) if err != nil { - log.ErrorWithContext(r.Context(),err) + log.Error(r.Context(),err) return err } return nil } */ + +func UnmarshalFromString(ctx context.Context, content string, in any) error { + err := json.Unmarshal([]byte(content), &in) + if err != nil { + log.Fatalf(ctx, "Unable to unmarshal JSON due to %s", err) + return err + } + return nil +} + +func MarshalToString(ctx context.Context, in any) (string, error) { + b, err := json.Marshal(in) + if err != nil { + log.Fatalf(ctx, "Unable to marshal JSON due to %s", err) + return "", nil + } + return string(b), nil +} diff --git a/internal/delivery/http/organization.go b/internal/delivery/http/organization.go index 4489fa9c..292d73ba 100644 --- a/internal/delivery/http/organization.go +++ b/internal/delivery/http/organization.go @@ -1,11 +1,13 @@ package http import ( + "encoding/json" "fmt" "net/http" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -15,124 +17,206 @@ import ( ) type OrganizationHandler struct { - usecase usecase.IOrganizationUsecase - userUsecase usecase.IUserUsecase + usecase usecase.IOrganizationUsecase + userUsecase usecase.IUserUsecase + roleUsecase usecase.IRoleUsecase + permissionUsecase usecase.IPermissionUsecase + systemNotificationRuleUsecase usecase.ISystemNotificationRuleUsecase } -func NewOrganizationHandler(o usecase.IOrganizationUsecase, u usecase.IUserUsecase) *OrganizationHandler { +func NewOrganizationHandler(u usecase.Usecase) *OrganizationHandler { return &OrganizationHandler{ - usecase: o, - userUsecase: u, + usecase: u.Organization, + userUsecase: u.User, + roleUsecase: u.Role, + permissionUsecase: u.Permission, + systemNotificationRuleUsecase: u.SystemNotificationRule, } } // CreateOrganization godoc -// @Tags Organizations -// @Summary Create organization -// @Description Create organization -// @Accept json -// @Produce json -// @Param body body domain.CreateOrganizationRequest true "create organization request" -// @Success 200 {object} object -// @Router /organizations [post] -// @Security JWT -func (h *OrganizationHandler) CreateOrganization(w http.ResponseWriter, r *http.Request) { +// +// @Tags Organizations +// @Summary Create organization in Admin portal +// @Description Create organization in Admin portal +// @Accept json +// @Produce json +// @Param body body domain.CreateOrganizationRequest true "create organization request" +// @Success 200 {object} object +// @Router /admin/organizations [post] +// @Security JWT +func (h *OrganizationHandler) Admin_CreateOrganization(w http.ResponseWriter, r *http.Request) { input := domain.CreateOrganizationRequest{} err := UnmarshalRequestInput(r, &input) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } ctx := r.Context() - var organization domain.Organization - if err = serializer.Map(input, &organization); err != nil { - log.ErrorWithContext(r.Context(), err) + var organization model.Organization + if err = serializer.Map(r.Context(), input, &organization); err != nil { + log.Error(r.Context(), err) } organizationId, err := h.usecase.Create(ctx, &organization) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } organization.ID = organizationId + + // Role 생성 + adminRole := model.Role{ + OrganizationID: organizationId, + Name: "admin", + Description: "admin", + Type: string(domain.RoleTypeTks), + } + adminRoleId, err := h.roleUsecase.CreateTksRole(r.Context(), &adminRole) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + userRole := model.Role{ + OrganizationID: organizationId, + Name: "user", + Description: "user", + Type: string(domain.RoleTypeTks), + } + userRoleId, err := h.roleUsecase.CreateTksRole(r.Context(), &userRole) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + // Permission 생성 + adminPermissionSet := h.permissionUsecase.GetAllowedPermissionSet(r.Context()) + h.permissionUsecase.SetRoleIdToPermissionSet(r.Context(), adminRoleId, adminPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(r.Context(), adminPermissionSet) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + userPermissionSet := h.permissionUsecase.GetUserPermissionSet(r.Context()) + h.permissionUsecase.SetRoleIdToPermissionSet(r.Context(), userRoleId, userPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(r.Context(), userPermissionSet) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + role, err := h.roleUsecase.GetTksRole(r.Context(), organizationId, adminRoleId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + user := model.User{ + Organization: model.Organization{ + ID: organizationId, + }, + AccountId: input.AdminAccountId, + Name: input.AdminName, + Email: input.AdminEmail, + Roles: []model.Role{ + *role, + }, + } // Admin user 생성 - _, err = h.userUsecase.CreateAdmin(organizationId, input.Email) + admin, err := h.userUsecase.CreateAdmin(r.Context(), &user) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + err = h.usecase.ChangeAdminId(r.Context(), organizationId, admin.ID) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + organization.AdminId = &admin.ID + + // Default systemNotificationRules 생성 + err = h.systemNotificationRuleUsecase.MakeDefaultSystemNotificationRules(r.Context(), organizationId, &organization) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } var out domain.CreateOrganizationResponse - if err = serializer.Map(organization, &out); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), organization, &out); err != nil { + log.Error(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetOrganizations godoc -// @Tags Organizations -// @Summary Get organization list -// @Description Get organization list -// @Accept json -// @Produce json -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} []domain.ListOrganizationBody -// @Router /organizations [get] -// @Security JWT +// +// @Tags Organizations +// @Summary Get organization list +// @Description Get organization list +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} []domain.ListOrganizationResponse +// @Router /organizations [get] +// @Security JWT func (h *OrganizationHandler) GetOrganizations(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) + pg := pagination.NewPagination(&urlParams) + organizations, err := h.usecase.Fetch(r.Context(), pg) if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } - - organizations, err := h.usecase.Fetch(pg) - if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } var out domain.ListOrganizationResponse - out.Organizations = make([]domain.ListOrganizationBody, len(*organizations)) + out.Organizations = make([]domain.OrganizationResponse, len(*organizations)) for i, organization := range *organizations { - if err = serializer.Map(organization, &out.Organizations[i]); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), organization, &out.Organizations[i]); err != nil { + log.Error(r.Context(), err) } - - log.InfoWithContext(r.Context(), organization) } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetOrganization godoc -// @Tags Organizations -// @Summary Get organization detail -// @Description Get organization detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} domain.GetOrganizationResponse -// @Router /organizations/{organizationId} [get] -// @Security JWT +// +// @Tags Organizations +// @Summary Get organization detail +// @Description Get organization detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.GetOrganizationResponse +// @Router /organizations/{organizationId} [get] +// @Security JWT func (h *OrganizationHandler) GetOrganization(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -141,9 +225,9 @@ func (h *OrganizationHandler) GetOrganization(w http.ResponseWriter, r *http.Req return } - organization, err := h.usecase.Get(organizationId) + organization, err := h.usecase.Get(r.Context(), organizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return @@ -153,24 +237,48 @@ func (h *OrganizationHandler) GetOrganization(w http.ResponseWriter, r *http.Req return } var out domain.GetOrganizationResponse - if err = serializer.Map(organization, &out.Organization); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), organization, &out.Organization); err != nil { + log.Error(r.Context(), err) + } + + out.Organization.StackTemplates = make([]domain.SimpleStackTemplateResponse, len(organization.StackTemplates)) + for i, stackTemplate := range organization.StackTemplates { + if err = serializer.Map(r.Context(), stackTemplate, &out.Organization.StackTemplates[i]); err != nil { + log.Error(r.Context(), err) + } + err := json.Unmarshal(stackTemplate.Services, &out.Organization.StackTemplates[i].Services) + if err != nil { + log.Error(r.Context(), err) + } + } + out.Organization.PolicyTemplates = make([]domain.SimplePolicyTemplateResponse, len(organization.PolicyTemplates)) + for i, policyTemplate := range organization.PolicyTemplates { + if err = serializer.Map(r.Context(), policyTemplate, &out.Organization.PolicyTemplates[i]); err != nil { + log.Error(r.Context(), err) + } + } + out.Organization.SystemNotificationTemplates = make([]domain.SimpleSystemNotificationTemplateResponse, len(organization.SystemNotificationTemplates)) + for i, notificationTemplate := range organization.SystemNotificationTemplates { + if err = serializer.Map(r.Context(), notificationTemplate, &out.Organization.SystemNotificationTemplates[i]); err != nil { + log.Error(r.Context(), err) + } } ResponseJSON(w, r, http.StatusOK, out) } // DeleteOrganization godoc -// @Tags Organizations -// @Summary Delete organization -// @Description Delete organization -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} domain.Organization -// @Router /organizations/{organizationId} [delete] -// @Security JWT -func (h *OrganizationHandler) DeleteOrganization(w http.ResponseWriter, r *http.Request) { +// +// @Tags Organizations +// @Summary Delete organization +// @Description Delete organization +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.DeleteOrganizationResponse +// @Router /organizations/{organizationId} [delete] +// @Security JWT +func (h *OrganizationHandler) Admin_DeleteOrganization(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] if !ok { @@ -186,16 +294,16 @@ func (h *OrganizationHandler) DeleteOrganization(w http.ResponseWriter, r *http. err := h.userUsecase.DeleteAll(r.Context(), organizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } // organization 삭제 - err = h.usecase.Delete(organizationId, token) + err = h.usecase.Delete(r.Context(), organizationId, token) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return @@ -204,20 +312,24 @@ func (h *OrganizationHandler) DeleteOrganization(w http.ResponseWriter, r *http. return } - ResponseJSON(w, r, http.StatusOK, nil) + out := domain.DeleteOrganizationResponse{ + ID: organizationId, + } + ResponseJSON(w, r, http.StatusOK, out) } // UpdateOrganization godoc -// @Tags Organizations -// @Summary Update organization detail -// @Description Update organization detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdateOrganizationRequest true "update organization request" -// @Success 200 {object} domain.UpdateOrganizationResponse -// @Router /organizations/{organizationId} [put] -// @Security JWT +// +// @Tags Organizations +// @Summary Update organization detail +// @Description Update organization detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.UpdateOrganizationRequest true "update organization request" +// @Success 200 {object} domain.UpdateOrganizationResponse +// @Router /organizations/{organizationId} [put] +// @Security JWT func (h *OrganizationHandler) UpdateOrganization(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -233,36 +345,49 @@ func (h *OrganizationHandler) UpdateOrganization(w http.ResponseWriter, r *http. return } - organization, err := h.usecase.Update(organizationId, input) + var dto model.Organization + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + res, err := h.usecase.Update(r.Context(), organizationId, dto) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) - if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } ErrorJSON(w, r, err) return } - var out domain.UpdateOrganizationResponse - if err = serializer.Map(organization, &out); err != nil { - log.ErrorWithContext(r.Context(), err) + user, err := h.userUsecase.GetByAccountId(r.Context(), input.AdminAccountId, organizationId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return } + err = h.usecase.ChangeAdminId(r.Context(), organizationId, user.ID) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + var out domain.UpdateOrganizationResponse + out.ID = res.ID + ResponseJSON(w, r, http.StatusOK, out) } // UpdatePrimaryCluster godoc -// @Tags Organizations -// @Summary Update primary cluster -// @Description Update primary cluster -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdatePrimaryClusterRequest true "update primary cluster request" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/primary-cluster [patch] -// @Security JWT +// +// @Tags Organizations +// @Summary Update primary cluster +// @Description Update primary cluster +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.UpdatePrimaryClusterRequest true "update primary cluster request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/primary-cluster [patch] +// @Security JWT func (h *OrganizationHandler) UpdatePrimaryCluster(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -278,7 +403,7 @@ func (h *OrganizationHandler) UpdatePrimaryCluster(w http.ResponseWriter, r *htt return } - err = h.usecase.UpdatePrimaryClusterId(organizationId, input.PrimaryClusterId) + err = h.usecase.UpdatePrimaryClusterId(r.Context(), organizationId, input.PrimaryClusterId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) @@ -290,3 +415,41 @@ func (h *OrganizationHandler) UpdatePrimaryCluster(w http.ResponseWriter, r *htt ResponseJSON(w, r, http.StatusOK, nil) } + +// CheckOrganizationName godoc +// +// @Tags Organizations +// @Summary Check name for organization +// @Description Check name for organization +// @Accept json +// @Produce json +// @Param name path string true "name" +// @Success 200 {object} nil +// @Router /organizations/name/{name}/existence [GET] +// @Security JWT +func (h *OrganizationHandler) CheckOrganizationName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid name"), "O_INVALID_ORGANIZATION_NAME", "")) + return + } + + exist := false + pg := pagination.NewPaginationWithFilter("name", "", "$eq", []string{name}) + organizations, err := h.usecase.Fetch(r.Context(), pg) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + if organizations != nil && len(*organizations) > 0 { + exist = true + } + + var out domain.CheckStackNameResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/permission.go b/internal/delivery/http/permission.go new file mode 100644 index 00000000..3ad7798f --- /dev/null +++ b/internal/delivery/http/permission.go @@ -0,0 +1,68 @@ +package http + +import ( + "context" + "net/http" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" +) + +type IPermissionHandler interface { + GetPermissionTemplates(w http.ResponseWriter, r *http.Request) +} + +type PermissionHandler struct { + permissionUsecase usecase.IPermissionUsecase + userUsecase usecase.IUserUsecase +} + +func NewPermissionHandler(usecase usecase.Usecase) IPermissionHandler { + return &PermissionHandler{ + permissionUsecase: usecase.Permission, + userUsecase: usecase.User, + } +} + +// GetPermissionTemplates godoc +// +// @Tags Permission +// @Summary Get Permission Templates +// @Description Get Permission Templates +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetPermissionTemplatesResponse +// @Router /permissions/templates [get] +// @Security JWT +func (h PermissionHandler) GetPermissionTemplates(w http.ResponseWriter, r *http.Request) { + permissionSet := model.NewDefaultPermissionSet() + + var out domain.GetPermissionTemplatesResponse + out.Permissions = make([]*domain.TemplateResponse, 0) + + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Dashboard)) + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Stack)) + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Policy)) + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.ProjectManagement)) + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Notification)) + out.Permissions = append(out.Permissions, convertModelToPermissionTemplateResponse(r.Context(), permissionSet.Configuration)) + + ResponseJSON(w, r, http.StatusOK, out) +} + +func convertModelToPermissionTemplateResponse(ctx context.Context, permission *model.Permission) *domain.TemplateResponse { + var permissionResponse domain.TemplateResponse + + permissionResponse.Key = permission.Key + permissionResponse.Name = permission.Name + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionTemplateResponse(ctx, child)) + } + + return &permissionResponse +} diff --git a/internal/delivery/http/policy-notification.go b/internal/delivery/http/policy-notification.go new file mode 100644 index 00000000..12e87db4 --- /dev/null +++ b/internal/delivery/http/policy-notification.go @@ -0,0 +1,133 @@ +package http + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type PolicyNotificationHandler struct { + usecase usecase.ISystemNotificationUsecase +} + +func NewPolicyNotificationHandler(h usecase.Usecase) *PolicyNotificationHandler { + return &PolicyNotificationHandler{ + usecase: h.SystemNotification, + } +} + +// GetPolicyNotification godoc +// +// @Tags PolicyNotifications +// @Summary Get PolicyNotifications +// @Description Get PolicyNotifications +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetPolicyNotificationsResponse +// @Router /organizations/{organizationId}/policy-notifications [get] +// @Security JWT +func (h *PolicyNotificationHandler) GetPolicyNotifications(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.SystemNotificationRuleStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } else if filter.Column == "message_action_proposal" { + for j, value := range filter.Values { + val := "" + if value == "dryrun" { + val = "감사" + } else { + val = "거부" + } + pg.GetFilters()[i].Values[j] = val + } + } + } + + policyNotifications, err := h.usecase.FetchPolicyNotifications(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetPolicyNotificationsResponse + out.PolicyNotifications = make([]domain.PolicyNotificationResponse, len(policyNotifications)) + for i, policyNotification := range policyNotifications { + if err := serializer.Map(r.Context(), policyNotification, &out.PolicyNotifications[i]); err != nil { + log.Info(r.Context(), err) + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyNotification godoc +// +// @Tags PolicyNotifications +// @Summary Get PolicyNotification +// @Description Get PolicyNotification +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param policyNotificationId path string true "policyNotificationId" +// @Success 200 {object} domain.GetPolicyNotificationResponse +// @Router /organizations/{organizationId}/policy-notifications/{policyNotificationId} [get] +// @Security JWT +func (h *PolicyNotificationHandler) GetPolicyNotification(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["policyNotificationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid policyNotificationId"), "", "")) + return + } + + policyNotificationId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) + return + } + + policyNotification, err := h.usecase.Get(r.Context(), policyNotificationId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetPolicyNotificationResponse + if err := serializer.Map(r.Context(), policyNotification, &out.PolicyNotification); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/policy-template.go b/internal/delivery/http/policy-template.go new file mode 100644 index 00000000..4fba4a3a --- /dev/null +++ b/internal/delivery/http/policy-template.go @@ -0,0 +1,1771 @@ +package http + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/openinfradev/tks-api/pkg/domain" + admin_domain "github.com/openinfradev/tks-api/pkg/domain/admin" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + + "github.com/Masterminds/semver/v3" +) + +type PolicyTemplateHandler struct { + usecase usecase.IPolicyTemplateUsecase +} + +type IPolicyTemplateHandler interface { + Admin_CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplate(w http.ResponseWriter, r *http.Request) + Admin_ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) + Admin_ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) + Admin_CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + Admin_ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) + Admin_ExtractParameters(w http.ResponseWriter, r *http.Request) + Admin_AddPermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) + Admin_UpdatePermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) + Admin_DeletePermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) + + CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) + UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) + DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) + GetPolicyTemplate(w http.ResponseWriter, r *http.Request) + ListPolicyTemplate(w http.ResponseWriter, r *http.Request) + ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) + ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) + ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) + GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) + CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) + ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) + ExtractParameters(w http.ResponseWriter, r *http.Request) + + RegoCompile(w http.ResponseWriter, r *http.Request) +} + +func NewPolicyTemplateHandler(u usecase.Usecase) IPolicyTemplateHandler { + return &PolicyTemplateHandler{ + usecase: u.PolicyTemplate, + } +} + +// Admin_CreatePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_CreatePolicyTemplate] 정책 템플릿 신규 생성 +// @Description 정책 템플릿을 신규 생성(v1.0.0을 생성)한다. +// @Accept json +// @Produce json +// @Param body body admin_domain.CreatePolicyTemplateRequest true "create policy template request" +// @Success 200 {object} admin_domain.CreatePolicyTemplateReponse +// @Router /admin/policy-templates [post] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { + input := admin_domain.CreatePolicyTemplateRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.PolicyTemplate + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + dto.Type = "tks" + + policyTemplateId, err := h.usecase.Create(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out admin_domain.CreatePolicyTemplateReponse + out.ID = policyTemplateId.String() + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_UpdatePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_UpdatePolicyTemplate] 정책 템플릿 업데이트 +// @Description 정책 템플릿의 업데이트 가능한 필드들을 업데이트한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param body body admin_domain.UpdatePolicyTemplateRequest true "update policy template request" +// @Success 200 {object} nil +// @Router /admin/policy-templates/{policyTemplateId} [patch] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + input := admin_domain.UpdatePolicyTemplateRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + err = h.usecase.Update(r.Context(), nil, id, input.TemplateName, input.Description, input.Severity, input.Deprecated, input.PermittedOrganizationIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// Admin_DeletePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_DeletePolicyTemplate] 정책 템플릿 삭제 +// @Description 정책 템플릿을 삭제한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} nil +// @Router /admin/policy-templates/{policyTemplateId} [delete] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + err = h.usecase.Delete(r.Context(), nil, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// Admin_GetPolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_GetPolicyTemplate] 정책 템플릿 조회(최신 버전) +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} admin_domain.GetPolicyTemplateResponse +// @Router /admin/policy-templates/{policyTemplateId} [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + policyTemplate, err := h.usecase.Get(r.Context(), nil, id) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policyTemplate == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out admin_domain.GetPolicyTemplateResponse + if err = serializer.Map(r.Context(), *policyTemplate, &out.PolicyTemplate); err != nil { + log.Error(r.Context(), err) + } + + out.PolicyTemplate.PermittedOrganizations = make([]domain.SimpleOrganizationResponse, len(policyTemplate.PermittedOrganizations)) + for i, organization := range policyTemplate.PermittedOrganizations { + if err := serializer.Map(r.Context(), organization, &out.PolicyTemplate.PermittedOrganizations[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + // if err = h.usecase.FillPermittedOrganizations(r.Context(), policyTemplate, &out.PolicyTemplate); err != nil { + // log.Error(r.Context(), err) + // } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_ListPolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ListPolicyTemplate] 정책 템플릿 목록 조회 +// @Description 정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다. +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} admin_domain.ListPolicyTemplateResponse +// @Router /admin/policy-templates [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + + pg := pagination.NewPagination(&urlParams) + + policyTemplates, err := h.usecase.Fetch(r.Context(), nil, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out admin_domain.ListPolicyTemplateResponse + out.PolicyTemplates = make([]admin_domain.PolicyTemplateResponse, len(policyTemplates)) + for i, policyTemplate := range policyTemplates { + if err := serializer.Map(r.Context(), policyTemplate, &out.PolicyTemplates[i]); err != nil { + log.Info(r.Context(), err) + continue + } + + out.PolicyTemplates[i].PermittedOrganizations = make([]domain.SimpleOrganizationResponse, len(policyTemplate.PermittedOrganizations)) + for j, organization := range policyTemplate.PermittedOrganizations { + if err := serializer.Map(r.Context(), organization, &out.PolicyTemplates[i].PermittedOrganizations[j]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + } + + // if err = h.usecase.FillPermittedOrganizationsForList(r.Context(), &policyTemplates, &out.PolicyTemplates); err != nil { + // log.Error(r.Context(), err) + // } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_ListPolicyTemplateVersions godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} admin_domain.ListPolicyTemplateVersionsResponse +// @Router /admin/policy-templates/{policyTemplateId}/versions [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + policyTemplateVersions, err := h.usecase.ListPolicyTemplateVersions(r.Context(), nil, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + var out admin_domain.ListPolicyTemplateVersionsResponse + if err = serializer.Map(r.Context(), *policyTemplateVersions, &out); err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_ExtractParameters godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ExtractParameters] 정책 템플릿 파라미터 추출 +// @Description 정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "버전(v0.0.0 형식)" +// @Param body body admin_domain.ExtractParametersRequest true "Rego 코드" +// @Success 200 {object} admin_domain.ExtractParametersResponse +// @Router /admin/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters [post] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ExtractParameters(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + input := admin_domain.ExtractParametersRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + response, err := h.usecase.ExtractPolicyParameters(r.Context(), nil, id, version, input.Rego, input.Libs) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusCreated, response) +} + +// Admin_ListPolicyTemplateStatistics godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} admin_domain.ListPolicyTemplateStatisticsResponse +// @Router /admin/policy-templates/{policyTemplateId}/statistics [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + usageCounts, err := h.usecase.ListPolicyTemplateStatistics(r.Context(), nil, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + var out admin_domain.ListPolicyTemplateStatisticsResponse + out.PolicyTemplateStatistics = make([]admin_domain.PolicyTemplateStatistics, len(usageCounts)) + for i, usageCount := range usageCounts { + if err := serializer.Map(r.Context(), usageCount, &out.PolicyTemplateStatistics[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_GetPolicyTemplateDeploy godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} admin_domain.GetPolicyTemplateDeployResponse +// @Router /admin/policy-templates/{policyTemplateId}/deploy [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + out, err := h.usecase.GetPolicyTemplateDeploy(r.Context(), nil, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_GetPolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "조회할 버전(v0.0.0 형식)" +// @Success 200 {object} admin_domain.GetPolicyTemplateVersionResponse +// @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + policyTemplate, err := h.usecase.GetPolicyTemplateVersion(r.Context(), nil, id, version) + if err != nil { + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + } + + if policyTemplate == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out admin_domain.GetPolicyTemplateVersionResponse + if err = serializer.Map(r.Context(), *policyTemplate, &out.PolicyTemplate); err != nil { + log.Error(r.Context(), err) + } + + out.PolicyTemplate.PermittedOrganizations = make([]domain.SimpleOrganizationResponse, len(policyTemplate.PermittedOrganizations)) + + for i, organization := range policyTemplate.PermittedOrganizations { + if err := serializer.Map(r.Context(), organization, &out.PolicyTemplate.PermittedOrganizations[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + // if err = h.usecase.FillPermittedOrganizations(r.Context(), policyTemplate, &out.PolicyTemplate); err != nil { + // log.Error(r.Context(), err) + // } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_CreatePolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param body body admin_domain.CreatePolicyTemplateVersionRequest true "create policy template version request" +// @Success 200 {object} admin_domain.CreatePolicyTemplateVersionResponse +// @Router /admin/policy-templates/{policyTemplateId}/versions [post] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + input := admin_domain.CreatePolicyTemplateVersionRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + currentVer, err := semver.NewVersion(input.CurrentVersion) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid currentVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("invalid version format '%s'", input.CurrentVersion))) + return + } + + versionUpType := strings.ToLower(input.VersionUpType) + + var expectedVersion string + + switch versionUpType { + case "major": + newVersion := currentVer.IncMajor() + expectedVersion = newVersion.Original() + case "minor": + newVersion := currentVer.IncMinor() + expectedVersion = newVersion.Original() + case "patch": + newVersion := currentVer.IncPatch() + expectedVersion = newVersion.Original() + } + + if expectedVersion != input.ExpectedVersion { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid expectedVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("expected version mismatch '%s' != '%s'", + input.ExpectedVersion, expectedVersion))) + } + + createdVersion, err := h.usecase.CreatePolicyTemplateVersion(r.Context(), nil, id, expectedVersion, input.ParametersSchema, + input.Rego, input.Libs, input.SyncKinds, input.SyncJson) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out admin_domain.CreatePolicyTemplateVersionResponse + out.Version = createdVersion + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_DeletePolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다. +// @Accept json +// @Produce json +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "삭제할 버전(v0.0.0 형식)" +// @Success 200 {object} nil +// @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [delete] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + err = h.usecase.DeletePolicyTemplateVersion(r.Context(), nil, id, version) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// Admin_ExistsPolicyTemplateName godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인 +// @Description 해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param policyTemplateName path string true "정책 템플릿 이름" +// @Success 200 {object} admin_domain.ExistsPolicyTemplateNameResponse +// @Router /admin/policy-templates/name/{policyTemplateName}/existence [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateName, ok := vars["policyTemplateName"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateName not found in path"), + "PT_INVALID_POLICY_TEMPLATE_NAME", "")) + return + } + + exist, err := h.usecase.IsPolicyTemplateNameExist(r.Context(), nil, policyTemplateName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out admin_domain.ExistsPolicyTemplateNameResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_ExistsPolicyTemplateKind godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인 +// @Description 해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param policyTemplateKind path string true "정책 템플릿 이름" +// @Success 200 {object} admin_domain.ExistsPolicyTemplateKindResponse +// @Router /admin/policy-templates/kind/{policyTemplateKind}/existence [get] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + policyTemplateKind, ok := vars["policyTemplateKind"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateKind not found in path"), + "PT_INVALID_POLICY_TEMPLATE_KIND", "")) + return + } + + exist, err := h.usecase.IsPolicyTemplateKindExist(r.Context(), nil, policyTemplateKind) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out admin_domain.ExistsPolicyTemplateKindResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_AddPermittedPolicyTemplatesForOrganization godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_AddPermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 추가 +// @Description 특정 조직에 대해 허용된 tks 템플릿 목록을 추가한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body admin_domain.AddPermittedPolicyTemplatesForOrganizationRequest true "update pemitted policy template request" +// @Success 200 {object} nil +// @Router /admin/organizations/{organizationId}/policyTemplates [post] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_AddPermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + input := admin_domain.AddPermittedPolicyTemplatesForOrganizationRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyTemplateIds)) + + for i, policyTemplateId := range input.PolicyTemplateIds { + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.AddPermittedPolicyTemplatesForOrganization(r.Context(), organizationId, ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +func (h *PolicyTemplateHandler) Admin_UpdatePermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + input := admin_domain.UpdatePermittedPolicyTemplatesForOrganizationRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyTemplateIds)) + + for i, policyTemplateId := range input.PolicyTemplateIds { + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.UpdatePermittedPolicyTemplatesForOrganization(r.Context(), organizationId, ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// Admin_DeletePermittedPolicyTemplatesForOrganization godoc +// +// @Tags PolicyTemplate +// @Summary [Admin_DeletePermittedPolicyTemplatesForOrganization] 특정 조직에 대해 허용된 tks 템플릿 목록 제거 +// @Description 특정 조직에 대해 허용된 tks 템플릿의 허용 상태를 해제(제거)한다. tks 우형 템플릿에 대해서만 동작하고 organization 유형 템플릿에 대해서는 거부된다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body admin_domain.DeletePermittedPolicyTemplatesForOrganizationRequest true "delete pemitted policy template request" +// @Success 200 {object} nil +// @Router /admin/organizations/{organizationId}/policyTemplates [put] +// @Security JWT +func (h *PolicyTemplateHandler) Admin_DeletePermittedPolicyTemplatesForOrganization(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + input := admin_domain.DeletePermittedPolicyTemplatesForOrganizationRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyTemplateIds)) + + for i, policyTemplateId := range input.PolicyTemplateIds { + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.DeletePermittedPolicyTemplatesForOrganization(r.Context(), organizationId, ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// CompileRego godoc +// +// @Tags PolicyTemplate +// @Summary [CompileRego] Rego 코드 컴파일 및 파라미터 파싱 +// @Description Rego 코드 컴파일 및 파라미터 파싱을 수행한다. 파라미터 파싱을 위해서는 먼저 컴파일이 성공해야 하며, parseParameter를 false로 하면 컴파일만 수행할 수 있다. +// @Accept json +// @Produce json +// @Param parseParameter query bool true "파라미터 파싱 여부" +// @Param body body domain.RegoCompileRequest true "Rego 코드" +// @Success 200 {object} domain.RegoCompileResponse +// @Router /policy-templates/rego-compile [post] +// @Security JWT +func (h *PolicyTemplateHandler) RegoCompile(w http.ResponseWriter, r *http.Request) { + parseParameter := false + + urlParams := r.URL.Query() + + parse := urlParams.Get("parseParameter") + if len(parse) > 0 { + parsedBool, err := strconv.ParseBool(parse) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid parseParameter: '%s'", parse), "PT_INVALID_REGO_PARSEPARAMETER", "")) + return + } + parseParameter = parsedBool + } + + input := domain.RegoCompileRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + response, err := h.usecase.RegoCompile(&input, parseParameter) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusCreated, response) +} + +// CreatePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [CreatePolicyTemplate] 정책 템플릿 신규 생성 +// @Description 정책 템플릿을 신규 생성(v1.0.0을 생성)한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body domain.CreatePolicyTemplateRequest true "create policy template request" +// @Success 200 {object} domain.CreatePolicyTemplateReponse +// @Router /organizations/{organizationId}/policy-templates [post] +// @Security JWT +func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.CreatePolicyTemplateRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.PolicyTemplate + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + // // no converter found for *[]domain.ParameterDef -> []*domain.ParameterDef + // dto.ParametersSchema = input.ParametersSchema + + dto.Type = "organization" + dto.OrganizationId = &organizationId + + policyTemplateId, err := h.usecase.Create(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CreatePolicyTemplateReponse + out.ID = policyTemplateId.String() + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [UpdatePolicyTemplate] 정책 템플릿 업데이트 +// @Description 정책 템플릿의 업데이트 가능한 필드들을 업데이트한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param body body domain.UpdatePolicyTemplateRequest true "update policy template request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId} [patch] +// @Security JWT +func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + input := domain.UpdatePolicyTemplateRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + // Organization 템플릿은 PermittedOrganizations가 없음 + err = h.usecase.Update(r.Context(), &organizationId, id, input.TemplateName, input.Description, input.Severity, input.Deprecated, nil) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeletePolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [DeletePolicyTemplate] 정책 템플릿 삭제 +// @Description 정책 템플릿을 삭제한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId} [delete] +// @Security JWT +func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + err = h.usecase.Delete(r.Context(), &organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// GetPolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [GetPolicyTemplate] 정책 템플릿 조회(최신 버전) +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} domain.GetPolicyTemplateResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId} [get] +// @Security JWT +func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + policyTemplate, err := h.usecase.Get(r.Context(), &organizationId, id) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policyTemplate == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out domain.GetPolicyTemplateResponse + if err = serializer.Map(r.Context(), *policyTemplate, &out.PolicyTemplate); err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicyTemplate godoc +// +// @Tags PolicyTemplate +// @Summary [ListPolicyTemplate] 정책 템플릿 목록 조회 +// @Description 정책 템플릿 목록을 조회한다. 정책 템플릿 목록 조회 결과는 최신 템플릿 버전 목록만 조회된다. +// @Accept json +// @Produce json +// @Param limit query string false "pageSize" +// @Param page query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Success 200 {object} domain.ListPolicyTemplateResponse +// @Router /organizations/{organizationId}/policy-templates [get] +// @Security JWT +func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + + pg := pagination.NewPagination(&urlParams) + + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplates, err := h.usecase.Fetch(r.Context(), &organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListPolicyTemplateResponse + out.PolicyTemplates = make([]domain.PolicyTemplateTwoVersionResponse, len(policyTemplates)) + for i, policyTemplate := range policyTemplates { + if err := serializer.Map(r.Context(), policyTemplate, &out.PolicyTemplates[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicyTemplateVersions godoc +// +// @Tags PolicyTemplate +// @Summary [ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} domain.ListPolicyTemplateVersionsResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions [get] +// @Security JWT +func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + policyTemplateVersions, err := h.usecase.ListPolicyTemplateVersions(r.Context(), &organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + var out domain.ListPolicyTemplateVersionsResponse + if err = serializer.Map(r.Context(), *policyTemplateVersions, &out); err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicyTemplateStatistics godoc +// +// @Tags PolicyTemplate +// @Summary [ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 최신 버전을 조회한다. 전체 조직의 통계를 조회하려면 organizationId를 tks로 설정한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} domain.ListPolicyTemplateStatisticsResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/statistics [get] +// @Security JWT +func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + usageCounts, err := h.usecase.ListPolicyTemplateStatistics(r.Context(), &organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + var out domain.ListPolicyTemplateStatisticsResponse + out.PolicyTemplateStatistics = make([]domain.PolicyTemplateStatistics, len(usageCounts)) + for i, usageCount := range usageCounts { + if err := serializer.Map(r.Context(), usageCount, &out.PolicyTemplateStatistics[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyTemplateDeploy godoc +// +// @Tags PolicyTemplate +// @Summary [GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 정책 템플릿 클러스터 별 설치 버전을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} domain.GetPolicyTemplateDeployResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/deploy [get] +// @Security JWT +func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + out, err := h.usecase.GetPolicyTemplateDeploy(r.Context(), &organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "조회할 버전(v0.0.0 형식)" +// @Success 200 {object} domain.GetPolicyTemplateVersionResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version} [get] +// @Security JWT +func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + policyTemplate, err := h.usecase.GetPolicyTemplateVersion(r.Context(), &organizationId, id, version) + if err != nil { + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + } + + if policyTemplate == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out domain.GetPolicyTemplateVersionResponse + if err = serializer.Map(r.Context(), *policyTemplate, &out.PolicyTemplate); err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// CreatePolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 저장한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param body body domain.CreatePolicyTemplateVersionRequest true "create policy template version request" +// @Success 200 {object} domain.CreatePolicyTemplateVersionResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions [post] +// @Security JWT +func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + input := domain.CreatePolicyTemplateVersionRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + currentVer, err := semver.NewVersion(input.CurrentVersion) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid currentVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("invalid version format '%s'", input.CurrentVersion))) + return + } + + versionUpType := strings.ToLower(input.VersionUpType) + + var expectedVersion string + + switch versionUpType { + case "major": + newVersion := currentVer.IncMajor() + expectedVersion = newVersion.Original() + case "minor": + newVersion := currentVer.IncMinor() + expectedVersion = newVersion.Original() + case "patch": + newVersion := currentVer.IncPatch() + expectedVersion = newVersion.Original() + } + + if expectedVersion != input.ExpectedVersion { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid expectedVersion"), "PT_INVALID_POLICY_TEMPLATE_VERSION", fmt.Sprintf("expected version mismatch '%s' != '%s'", + input.ExpectedVersion, expectedVersion))) + } + + createdVersion, err := h.usecase.CreatePolicyTemplateVersion(r.Context(), &organizationId, id, expectedVersion, input.ParametersSchema, + input.Rego, input.Libs, input.SyncKinds, input.SyncJson) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CreatePolicyTemplateVersionResponse + out.Version = createdVersion + + ResponseJSON(w, r, http.StatusOK, out) +} + +// DeletePolicyTemplateVersion godoc +// +// @Tags PolicyTemplate +// @Summary [DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제 +// @Description 해당 식별자를 가진 정책 템플릿의 특정 버전을 삭제한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "삭제할 버전(v0.0.0 형식)" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version} [delete] +// @Security JWT +func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + err = h.usecase.DeletePolicyTemplateVersion(r.Context(), &organizationId, id, version) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// ExistsPolicyTemplateName godoc +// +// @Tags PolicyTemplate +// @Summary [ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인 +// @Description 해당 이름을 가진 정책 템플릿이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateName path string true "정책 템플릿 이름" +// @Success 200 {object} domain.ExistsPolicyTemplateNameResponse +// @Router /organizations/{organizationId}/policy-templates/name/{policyTemplateName}/existence [get] +// @Security JWT +func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateName, ok := vars["policyTemplateName"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateName not found in path"), + "PT_INVALID_POLICY_TEMPLATE_NAME", "")) + return + } + + exist, err := h.usecase.IsPolicyTemplateNameExist(r.Context(), &organizationId, policyTemplateName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ExistsPolicyTemplateNameResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ExistsPolicyTemplateKind godoc +// +// @Tags PolicyTemplate +// @Summary [ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인 +// @Description 해당 유형을 가진 정책 템플릿이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateKind path string true "정책 템플릿 이름" +// @Success 200 {object} domain.ExistsPolicyTemplateKindResponse +// @Router /organizations/{organizationId}/policy-templates/kind/{policyTemplateKind}/existence [get] +// @Security JWT +func (h *PolicyTemplateHandler) ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateKind, ok := vars["policyTemplateKind"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyTemplateKind not found in path"), + "PT_INVALID_POLICY_TEMPLATE_KIND", "")) + return + } + + exist, err := h.usecase.IsPolicyTemplateKindExist(r.Context(), &organizationId, policyTemplateKind) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ExistsPolicyTemplateKindResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ExtractParameters godoc +// +// @Tags PolicyTemplate +// @Summary [ExtractParameters] 정책 템플릿 파라미터 추출 +// @Description 정책 템플릿 파라미터를 기존 버전의 수정불가능한 파라미터를 포함해서 추출한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param version path string true "버전(v0.0.0 형식)" +// @Param body body domain.ExtractParametersRequest true "Rego 코드" +// @Success 200 {object} domain.ExtractParametersResponse +// @Router /organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters [post] +// @Security JWT +func (h *PolicyTemplateHandler) ExtractParameters(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + version, ok := vars["version"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid version"), "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "PT_INVALID_POLICY_TEMPLATE_VERSION", "")) + return + } + + input := domain.ExtractParametersRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ExtractParametersResponse + + response, err := h.usecase.ExtractPolicyParameters(r.Context(), &organizationId, id, version, input.Rego, input.Libs) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + if err := serializer.Map(r.Context(), *response, &out); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusCreated, response) +} diff --git a/internal/delivery/http/policy.go b/internal/delivery/http/policy.go new file mode 100644 index 00000000..2555ac1a --- /dev/null +++ b/internal/delivery/http/policy.go @@ -0,0 +1,1189 @@ +package http + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + policytemplate "github.com/openinfradev/tks-api/internal/policy-template" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "gopkg.in/yaml.v3" + "k8s.io/apimachinery/pkg/util/validation" +) + +type PolicyHandler struct { + usecase usecase.IPolicyUsecase +} + +type IPolicyHandler interface { + CreatePolicy(w http.ResponseWriter, r *http.Request) + UpdatePolicy(w http.ResponseWriter, r *http.Request) + DeletePolicy(w http.ResponseWriter, r *http.Request) + GetPolicy(w http.ResponseWriter, r *http.Request) + ListPolicy(w http.ResponseWriter, r *http.Request) + UpdatePolicyTargetClusters(w http.ResponseWriter, r *http.Request) + GetMandatoryPolicies(w http.ResponseWriter, r *http.Request) + SetMandatoryPolicies(w http.ResponseWriter, r *http.Request) + ExistsPolicyName(w http.ResponseWriter, r *http.Request) + ExistsPolicyResourceName(w http.ResponseWriter, r *http.Request) + ListStackPolicyStatus(w http.ResponseWriter, r *http.Request) + GetStackPolicyTemplateStatus(w http.ResponseWriter, r *http.Request) + UpdateStackPolicyTemplateStatus(w http.ResponseWriter, r *http.Request) + GetPolicyEdit(w http.ResponseWriter, r *http.Request) + GetPolicyStatistics(w http.ResponseWriter, r *http.Request) + AddPoliciesForStack(w http.ResponseWriter, r *http.Request) + UpdatePoliciesForStack(w http.ResponseWriter, r *http.Request) + DeletePoliciesForStack(w http.ResponseWriter, r *http.Request) + StackPolicyStatistics(w http.ResponseWriter, r *http.Request) +} + +func NewPolicyHandler(u usecase.Usecase) IPolicyHandler { + return &PolicyHandler{ + usecase: u.Policy, + } +} + +// CreatePolicy godoc +// +// @Tags Policy +// @Summary [CreatePolicy] 정책 생성 +// @Description 새로운 정책을 생성한다. targetClusterIds가 명시되지 않으면 정책은 활성화되지 않은 상태로 생성된다. 다른 클러스터에 동일한 정책이 존재한다면 정책 생성이 아닌 정책 업데이트를 통해 targetClusterIds를 수정해야 한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body domain.CreatePolicyRequest true "create policy request" +// @Success 200 {object} domain.CreatePolicyResponse +// @Router /organizations/{organizationId}/policies [post] +// @Security JWT +func (h *PolicyHandler) CreatePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.CreatePolicyRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + if input.Match != nil && input.MatchYaml != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("both match and match yaml specified"), "P_INVALID_MATCH", "")) + return + } + + if input.MatchYaml != nil { + var match domain.Match + + err := yaml.Unmarshal([]byte(*input.MatchYaml), &match) + + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("match yaml error: %s", err), "P_INVALID_MATCH", "")) + return + } + + if err := ValidateDomainObject(match); err != nil { + ErrorJSON(w, r, err) + return + } + } else if input.Match != nil { + normaized, err := policytemplate.CheckAndNormalizeKinds(input.Match.Kinds) + + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("match error: %s", err), "P_INVALID_MATCH", "")) + return + } + + input.Match.Kinds = normaized + } + + if len(input.PolicyResourceName) > 0 { + errMsgs := validation.IsDNS1123Subdomain(input.PolicyResourceName) + + if len(errMsgs) > 0 { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid k8s resource name for policy: %v", errMsgs), "P_INVALID_RESURCE_NAME", "")) + return + } + } + + var dto model.Policy + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + policyId, err := h.usecase.Create(r.Context(), organizationId, dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CreatePolicyResponse + out.ID = policyId.String() + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePolicy godoc +// +// @Tags Policy +// @Summary [UpdatePolicy] 정책을 업데이트 +// @Description 정책의 내용을 업데이트 한다. 업데이트할 필드만 명시하면 된다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Param body body domain.UpdatePolicyRequest true "update policy set request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId} [patch] +// @Security JWT +func (h *PolicyHandler) UpdatePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + input := domain.UpdatePolicyRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + if input.Match != nil && input.MatchYaml != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("both match and match yaml specified"), "P_INVALID_MATCH", "")) + return + } + + if input.MatchYaml != nil { + var match domain.Match + + err := yaml.Unmarshal([]byte(*input.MatchYaml), &match) + + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("match yaml error: %s", err), "P_INVALID_MATCH", "")) + return + } + + if err := ValidateDomainObject(match); err != nil { + ErrorJSON(w, r, err) + return + } + } else if input.Match != nil { + normaized, err := policytemplate.CheckAndNormalizeKinds(input.Match.Kinds) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("match error: %s", err), "P_INVALID_MATCH", "")) + return + } + + input.Match.Kinds = normaized + } + + var templateId *uuid.UUID = nil + + if input.TemplateId != nil { + tuuid, err := uuid.Parse(*input.TemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + templateId = &tuuid + } + + err = h.usecase.Update(r.Context(), organizationId, id, + input.Mandatory, input.PolicyName, input.Description, templateId, input.EnforcementAction, + input.Parameters, input.Match, input.MatchYaml, input.TargetClusterIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeletePolicy godoc +// +// @Tags Policy +// @Summary [DeletePolicy] 정책 삭제 +// @Description 정첵을 삭제한다. 정책이 적용된 클러스터가 있으면 삭제되지 않으므로 삭제 전 적용된 클러스터가 비어있어야 한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId} [delete] +// @Security JWT +func (h *PolicyHandler) DeletePolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + err = h.usecase.Delete(r.Context(), organizationId, id) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, "") +} + +// GetPolicy godoc +// +// @Tags Policy +// @Summary [GetPolicy] 정책 조회 +// @Description 정책 정보를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Success 200 {object} domain.GetPolicyResponse +// @Router /organizations/{organizationId}/policies/{policyId} [get] +// @Security JWT +func (h *PolicyHandler) GetPolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + policy, err := h.usecase.Get(r.Context(), organizationId, id) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "P_NOT_FOUND_POLICY", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policy == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out domain.GetPolicyResponse + if err = serializer.Map(r.Context(), *policy, &out.Policy); err != nil { + log.Error(r.Context(), err) + } + + out.Policy.TargetClusters = make([]domain.SimpleClusterResponse, len(policy.TargetClusters)) + + for i, targetCluster := range policy.TargetClusters { + if err = serializer.Map(r.Context(), targetCluster, &out.Policy.TargetClusters[i]); err != nil { + log.Error(r.Context(), err) + } + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicy godoc +// +// @Tags Policy +// @Summary [ListPolicy] 정책 목록 조회 +// @Description 정책 목록을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param filledParameter query string false "filledParameter" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.ListPolicyResponse +// @Router /organizations/{organizationId}/policies [get] +// @Security JWT +func (h *PolicyHandler) ListPolicy(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + + pg := pagination.NewPagination(&urlParams) + + filledParameter := false + + parse := urlParams.Get("filledParameter") + if len(parse) > 0 { + parsedBool, err := strconv.ParseBool(parse) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid fillParameter: '%s'", parse), "PT_INVALID_FILLPARAMETER", "")) + return + } + filledParameter = parsedBool + } + + policies, err := h.usecase.Fetch(r.Context(), organizationId, pg, filledParameter) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListPolicyResponse + out.Policies = make([]domain.PolicyResponse, len(*policies)) + for i, policy := range *policies { + if err := serializer.Map(r.Context(), policy, &out.Policies[i]); err != nil { + log.Info(r.Context(), err) + continue + } + + out.Policies[i].TargetClusters = make([]domain.SimpleClusterResponse, len(policy.TargetClusters)) + + for j, targetCluster := range policy.TargetClusters { + if err = serializer.Map(r.Context(), targetCluster, &out.Policies[i].TargetClusters[j]); err != nil { + log.Error(r.Context(), err) + } + } + + if filledParameter { + parameterSchema := policy.PolicyTemplate.ParametersSchema + parameters := policy.Parameters + + err = policytemplate.FillParamDefFromJsonStr(parameterSchema, parameters) + if err != nil { + log.Error(r.Context(), err) + } else { + out.Policies[i].FilledParameters = parameterSchema + } + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdatePolicyTargetClusters godoc +// +// @Tags Policy +// @Summary [UpdatePolicyTargetClusters] 정책 적용 대상 클러스터 수정 +// @Description 정책 적용 대상 클러스터를 수정한다. 추가할 클러스터 목록과 제거할 클러스터 목록 중 하나만 명시되어야 한다. 현재 정책이 배포된 클러스터를 확인하지 않고도 특정 클러스터를 추가하거나 제거할 수 있는 편의 API이다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Param body body domain.UpdatePolicyClustersRequest true "update policy set request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/policies/{policyId}/clusters [patch] +// @Security JWT +func (h *PolicyHandler) UpdatePolicyTargetClusters(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + input := domain.UpdatePolicyClustersRequest{} + + err = UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + err = h.usecase.UpdatePolicyTargetClusters(r.Context(), organizationId, id, + input.CurrentTargetClusterIds, input.NewTargetClusterIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetMandatoryPolicies godoc +// +// @Tags Policy +// @Summary [GetMandatoryPolicies] 필수 정책 템플릿, 정책을 조회 +// @Description 템플릿, 정책이 필수 인지 여부를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Success 200 {object} domain.GetMandatoryPoliciesResponse +// @Router /organizations/{organizationId}/mandatory-policies [get] +// @Security JWT +func (h *PolicyHandler) GetMandatoryPolicies(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + out, err := h.usecase.GetMandatoryPolicies(r.Context(), organizationId) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// SetMandatoryPolicies godoc +// +// @Tags Policy +// @Summary [SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정 +// @Description 템플릿, 정책이 필수 인지 여부를 설정한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param body body domain.SetMandatoryPoliciesRequest true "update mandatory policy/policy template request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/mandatory-policies [patch] +// @Security JWT +func (h *PolicyHandler) SetMandatoryPolicies(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.SetMandatoryPoliciesRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + mandatoryPolicyIds := []uuid.UUID{} + nonMandatoryPolicyIds := []uuid.UUID{} + for _, policy := range input.Policies { + policyId, err := uuid.Parse(policy.PolicyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policy.Mandatory { + mandatoryPolicyIds = append(mandatoryPolicyIds, policyId) + } else { + nonMandatoryPolicyIds = append(nonMandatoryPolicyIds, policyId) + } + } + + err = h.usecase.SetMandatoryPolicies(r.Context(), organizationId, mandatoryPolicyIds, nonMandatoryPolicyIds) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } +} + +// ExistsPolicyName godoc +// +// @Tags Policy +// @Summary [ExistsPolicyName] 정책 이름 존재 여부 확인 +// @Description 해당 이름을 가진 정책이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyName path string true "정책 이름" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/policies/name/{policyName}/existence [get] +// @Security JWT +func (h *PolicyHandler) ExistsPolicyName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyName, ok := vars["policyName"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyName not found in path"), + "P_INVALID_POLICY_NAME", "")) + return + } + + exist, err := h.usecase.IsPolicyNameExist(r.Context(), organizationId, policyName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckExistedResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ExistsPolicyResourceName godoc +// +// @Tags Policy +// @Summary [ExistsPolicyResourceName] 정책 자원 이름 존재 여부 확인 +// @Description 해당 자원 이름을 가진 정책이 이미 존재하는지 확인한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyResourceName path string true "정책 자원 이름(쿠버네티스 배포 시 자원 이름)" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/policies/resource-name/{policyResourceName}/existence [get] +// @Security JWT +func (h *PolicyHandler) ExistsPolicyResourceName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyResourceName, ok := vars["policyResourceName"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("policyResourceName not found in path"), + "P_INVALID_POLICY_RESOURCE_NAME", "")) + return + } + + exist, err := h.usecase.IsPolicyResourceNameExist(r.Context(), organizationId, policyResourceName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckExistedResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListStackPolicyStatus godoc +// +// @Tags StackPolicyStatus +// @Summary [ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회 +// @Description 클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.ListStackPolicyStatusResponse +// @Router /organizations/{organizationId}/stacks/{stackId}/policy-status [get] +// @Security JWT +func (h *PolicyHandler) ListStackPolicyStatus(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), + "C_INVALID_STACK_ID", "")) + return + } + + urlParams := r.URL.Query() + + pg := pagination.NewPagination(&urlParams) + + policyStatuses, err := h.usecase.ListStackPolicyStatus(r.Context(), stackId, pg) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + out := domain.ListStackPolicyStatusResponse{ + Polices: policyStatuses, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// StackPolicyStatistics godoc +// +// @Tags StackPolicyStatus +// @Summary [ListStackPolicyStatus] 클러스터의 정책과 정책 템플릿, 버전 조회 +// @Description 클러스터의 정책과 정책 템플릿, 버전 등을 포함한 상태 목록을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param sortColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.StackPolicyStatistics +// @Router /organizations/{organizationId}/stacks/{stackId}/statistics [get] +// @Security JWT +func (h *PolicyHandler) StackPolicyStatistics(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), + "C_INVALID_STACK_ID", "")) + return + } + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), + "C_INVALID_STACK_ID", "")) + return + } + + stackPolicyStatistics, err := h.usecase.GetStackPolicyStatistics(r.Context(), + organizationId, domain.ClusterId(stackId)) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, stackPolicyStatistics) +} + +// GetStackPolicyTemplateStatus godoc +// +// @Tags StackPolicyStatus +// @Summary [GetStackPolicyTemplateStatus] 클러스터 템플릿 상태 상세 조회 +// @Description 템플릿의 클러스터 버전 등 상태를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Success 200 {object} domain.GetStackPolicyTemplateStatusResponse +// @Router /organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId} [get] +// @Security JWT +func (h *PolicyHandler) GetStackPolicyTemplateStatus(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), + "C_INVALID_STACK_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + out, err := h.usecase.GetStackPolicyTemplateStatus(r.Context(), stackId, id) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateStackPolicyTemplateStatus godoc +// +// @Tags StackPolicyStatus +// @Summary [UpdateStackPolicyTemplateStatus] 템플릿 버전 업데이트 +// @Description 해당 템플릿의 버전 업데이트 및 연관된 정책의 새 기본값을 설정한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param policyTemplateId path string true "정책 템플릿 식별자(uuid)" +// @Param body body domain.UpdateStackPolicyTemplateStatusRequest true "update stack policy template status request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId} [patch] +// @Security JWT +func (h *PolicyHandler) UpdateStackPolicyTemplateStatus(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), + "C_INVALID_STACK_ID", "")) + return + } + + policyTemplateId, ok := vars["policyTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyTemplateId"), "C_INVALID_POLICY_TEMPLATE_ID", "")) + return + } + + input := domain.UpdateStackPolicyTemplateStatusRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + ErrorJSON(w, r, err) + return + } + + id, err := uuid.Parse(policyTemplateId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + err = h.usecase.UpdateStackPolicyTemplateStatus(r.Context(), stackId, id, + input.TemplateCurrentVersion, input.TemplateTargetVerson) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetPolicyEdit godoc +// +// @Tags Policy +// @Summary [GetPolicy] 정책 조회 +// @Description 정책 정보를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param policyId path string true "정책 식별자(uuid)" +// @Success 200 {object} domain.GetPolicyResponse +// @Router /organizations/{organizationId}/policies/{policyId}/edit [get] +// @Security JWT +func (h *PolicyHandler) GetPolicyEdit(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + policyId, ok := vars["policyId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "")) + return + } + + id, err := uuid.Parse(policyId) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + policy, err := h.usecase.GetForEdit(r.Context(), organizationId, id) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "P_NOT_FOUND_POLICY", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + if policy == nil { + ResponseJSON(w, r, http.StatusNotFound, nil) + return + } + + var out domain.GetPolicyResponse + if err = serializer.Map(r.Context(), *policy, &out.Policy); err != nil { + log.Error(r.Context(), err) + } + + out.Policy.TargetClusters = make([]domain.SimpleClusterResponse, len(policy.TargetClusters)) + + for i, targetCluster := range policy.TargetClusters { + if err = serializer.Map(r.Context(), targetCluster, &out.Policy.TargetClusters[i]); err != nil { + log.Error(r.Context(), err) + } + } + + parameterSchema := policy.PolicyTemplate.ParametersSchema + parameters := policy.Parameters + + err = policytemplate.FillParamDefFromJsonStr(parameterSchema, parameters) + if err != nil { + log.Error(r.Context(), err) + } else { + out.Policy.FilledParameters = parameterSchema + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetPolicyStatistics godoc +// +// @Tags Policy +// @Summary [GetPolicyStatistics] 정책 템플릿, 정책 통계 조회 +// @Description 템플릿, 정책의 통계를 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Success 200 {object} domain.PolicyStatisticsResponse +// @Router /organizations/{organizationId}/policy-statistics [get] +// @Security JWT +func (h *PolicyHandler) GetPolicyStatistics(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + out, err := h.usecase.GetPolicyStatistics(r.Context(), organizationId) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// AddPoliciesForStack godoc +// +// @Tags Policy +// @Summary [AddPoliciesForStack] 특정 스택의 정책 목록 추가 +// @Description 특정 스택의 정책 목록을 정책 식별자 리스트로 지정해서 추가한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param body body domain.AddPoliciesForStackRequest true "add policies for stack request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId}/policies [post] +// @Security JWT +func (h *PolicyHandler) AddPoliciesForStack(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("stackId not found in path"), + "C_INVALID_STACK_ID", "")) + return + } + + input := domain.AddPoliciesForStackRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyIds)) + + for i, policyId := range input.PolicyIds { + id, err := uuid.Parse(policyId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.AddPoliciesForClusterID(r.Context(), organizationId, domain.ClusterId(stackId), ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +func (h *PolicyHandler) UpdatePoliciesForStack(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("stackId not found in path"), + "C_INVALID_STACK_ID", "")) + return + } + + input := domain.UpdatePoliciesForStackRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyIds)) + + for i, policyId := range input.PolicyIds { + id, err := uuid.Parse(policyId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.UpdatePoliciesForClusterID(r.Context(), organizationId, domain.ClusterId(stackId), ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeletePoliciesForStack godoc +// +// @Tags Policy +// @Summary [DeletePoliciesForStack] 특정 스택의 정책 제거 +// @Description 특정 스택에서 정책 식별자로 지정된 정책을 제거한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @Param stackId path string true "스택 식별자" +// @Param body body domain.DeletePoliciesForStackRequest true "delete policies for stack request" +// @Router /organizations/{organizationId}/stacks/{stackId}/policies [put] +// @Security JWT +func (h *PolicyHandler) DeletePoliciesForStack(w http.ResponseWriter, r *http.Request) { + // TODO: API 형상 추후 반드시 검토 + + 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 + } + + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("stackId not found in path"), + "C_INVALID_STACK_ID", "")) + return + } + + input := domain.DeletePoliciesForStackRequest{} + + err := UnmarshalRequestInput(r, &input) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ids := make([]uuid.UUID, len(input.PolicyIds)) + + for i, policyId := range input.PolicyIds { + id, err := uuid.Parse(policyId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "C_INVALID_POLICY_ID", "")) + return + } + ids[i] = id + } + + err = h.usecase.DeletePoliciesForClusterID(r.Context(), organizationId, domain.ClusterId(stackId), ids) + + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} diff --git a/internal/delivery/http/project.go b/internal/delivery/http/project.go new file mode 100644 index 00000000..0e5df939 --- /dev/null +++ b/internal/delivery/http/project.go @@ -0,0 +1,1906 @@ +package http + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/openinfradev/tks-api/internal/keycloak" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" +) + +type IProjectHandler interface { + CreateProject(w http.ResponseWriter, r *http.Request) + GetProjectRole(w http.ResponseWriter, r *http.Request) + GetProjectRoles(w http.ResponseWriter, r *http.Request) + UpdateProject(w http.ResponseWriter, r *http.Request) + DeleteProject(w http.ResponseWriter, r *http.Request) + GetProject(w http.ResponseWriter, r *http.Request) + IsProjectNameExist(w http.ResponseWriter, r *http.Request) + GetProjects(w http.ResponseWriter, r *http.Request) + Admin_GetProjects(w http.ResponseWriter, r *http.Request) + + AddProjectMember(w http.ResponseWriter, r *http.Request) + GetProjectMember(w http.ResponseWriter, r *http.Request) + GetProjectMembers(w http.ResponseWriter, r *http.Request) + GetProjectMemberCount(w http.ResponseWriter, r *http.Request) + RemoveProjectMember(w http.ResponseWriter, r *http.Request) + RemoveProjectMembers(w http.ResponseWriter, r *http.Request) + UpdateProjectMemberRole(w http.ResponseWriter, r *http.Request) + UpdateProjectMembersRole(w http.ResponseWriter, r *http.Request) + + CreateProjectNamespace(w http.ResponseWriter, r *http.Request) + IsProjectNamespaceExist(w http.ResponseWriter, r *http.Request) + GetProjectNamespaces(w http.ResponseWriter, r *http.Request) + GetProjectNamespace(w http.ResponseWriter, r *http.Request) + UpdateProjectNamespace(w http.ResponseWriter, r *http.Request) + DeleteProjectNamespace(w http.ResponseWriter, r *http.Request) + + SetFavoriteProject(w http.ResponseWriter, r *http.Request) + SetFavoriteProjectNamespace(w http.ResponseWriter, r *http.Request) + UnSetFavoriteProject(w http.ResponseWriter, r *http.Request) + UnSetFavoriteProjectNamespace(w http.ResponseWriter, r *http.Request) + + GetProjectKubeconfig(w http.ResponseWriter, r *http.Request) + GetProjectNamespaceK8sResources(w http.ResponseWriter, r *http.Request) + GetProjectNamespaceResourcesUsage(w http.ResponseWriter, r *http.Request) + GetProjectNamespaceKubeconfig(w http.ResponseWriter, r *http.Request) +} + +type ProjectHandler struct { + usecase usecase.IProjectUsecase + authUsecase usecase.IAuthUsecase + dashboardUsecase usecase.IDashboardUsecase +} + +func NewProjectHandler(u usecase.Usecase) IProjectHandler { + return &ProjectHandler{ + usecase: u.Project, + authUsecase: u.Auth, + dashboardUsecase: u.Dashboard, + } +} + +// CreateProject godoc +// +// @Tags Projects +// @Summary Create new project +// @Description Create new project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param request body domain.CreateProjectRequest true "Request body to create project" +// @Success 200 {object} domain.CreateProjectResponse +// @Router /organizations/{organizationId}/projects [post] +// @Security JWT +func (p ProjectHandler) CreateProject(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + var projectReq domain.CreateProjectRequest + if err := UnmarshalRequestInput(r, &projectReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + project := &model.Project{ + OrganizationId: organizationId, + Name: projectReq.Name, + Description: projectReq.Description, + CreatedAt: now, + } + log.Infof(r.Context(), "Processing CREATE request for project '%s'...", project.Name) + + projectId, err := p.usecase.CreateProject(r.Context(), project) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + project.ID = projectId + ProjectLeaderId, err := uuid.Parse(projectReq.ProjectLeaderId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "Failed to parse uuid to string")) + return + } + + prs, err := p.usecase.GetProjectRoles(r.Context(), usecase.ProjectLeader) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "Failed to retrieve project-leader id")) + return + } + + //Don't add ProjectUser Object because of Cascading + pm := &model.ProjectMember{ + ProjectId: projectId, + //ProjectUser: &model.ProjectUser{ID: ProjectLeaderId}, + //ProjectRole: &model.ProjectRole{ID: projectReq.ProjectRoleId}, + ProjectUserId: ProjectLeaderId, + ProjectRoleId: prs[0].ID, + IsProjectLeader: true, + CreatedAt: now, + } + + projectMemberId, err := p.usecase.AddProjectMember(r.Context(), organizationId, pm) + if err != nil { + log.Errorf(r.Context(), "projectMemberId: %v", projectMemberId) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + err = p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, projectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + out := domain.CreateProjectResponse{ProjectId: projectId} + ResponseJSON(w, r, http.StatusOK, out) + +} + +// GetProjects godoc +// +// @Tags Projects +// @Summary Get projects +// @Description Get projects +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param query query string false "(all | only)" +// @Param projectName query string false "Project Name" +// @Success 200 {object} domain.GetProjectsResponse +// @Router /organizations/{organizationId}/projects [get] +// @Security JWT +func (p ProjectHandler) GetProjects(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + queryName := urlParams.Get("query") + onlyMyProject := false + if queryName == "only" { + onlyMyProject = true + } + projectName := urlParams.Get("projectName") + + pg := pagination.NewPagination(&urlParams) + + // get myUserId from login component + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + log.Error(r.Context(), "Failed to retrieve user info from request") + ErrorJSON(w, r, fmt.Errorf("failed to retrieve user info from request")) + } + myUserId := requestUserInfo.GetUserId().String() + pr, err := p.usecase.GetProjects(r.Context(), organizationId, myUserId, onlyMyProject, projectName, pg) + if err != nil { + log.Error(r.Context(), "Failed to retrieve projects ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectsResponse + out.Projects = pr + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + if pr == nil { + out.Projects = make([]domain.ProjectResponse, 0) + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_GetProjects godoc +// +// @Tags Projects +// @Summary Get projects as admin +// @Description Get projects as admin +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectName query string false "Project Name" +// @Success 200 {object} domain.GetProjectsResponse +// @Router /admin/organizations/{organizationId}/projects [get] +// @Security JWT +func (p ProjectHandler) Admin_GetProjects(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + projectName := urlParams.Get("projectName") + pg := pagination.NewPagination(&urlParams) + pr, err := p.usecase.GetProjects(r.Context(), organizationId, "", false, projectName, pg) + if err != nil { + log.Error(r.Context(), "Failed to retrieve projects ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectsResponse + out.Projects = pr + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + if pr == nil { + ResponseJSON(w, r, http.StatusNotFound, domain.GetProjectsResponse{}) + } else { + ResponseJSON(w, r, http.StatusOK, out) + } +} + +// GetProject godoc +// +// @Tags Projects +// @Summary Get projects +// @Description Get projects +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.GetProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId} [get] +// @Security JWT +func (p ProjectHandler) GetProject(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + project, err := p.usecase.GetProjectWithLeader(r.Context(), organizationId, projectId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve project", err) + ErrorJSON(w, r, err) + return + } + if project == nil { + project, err := p.usecase.GetProject(r.Context(), organizationId, projectId) + if err != nil { + log.Error(r.Context(), "Failed to retrieve project", err) + ErrorJSON(w, r, err) + return + } + if project == nil { + ResponseJSON(w, r, http.StatusOK, domain.GetProjectResponse{}) + } + } + + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, r, fmt.Errorf("failed to retrieve user info from request")) + } + myUserId := requestUserInfo.GetUserId().String() + + var projectRoleId, projectRoleName string + //userProjectRole := requestUserInfo.GetRoleProjectMapping() + //if userProjectRole != nil { + // projectRoleName = userProjectRole[project.ID] + //} + //projectRoles, err := p.usecase.GetProjectRoles(r.Context(), usecase.ProjectAll) + //if err != nil { + // ErrorJSON(w, r, fmt.Errorf("failed to retrieve project role")) + //} + //for _, projectRole := range projectRoles { + // if projectRoleName == projectRole.Name { + // projectRoleId = projectRole.ID + // break + // } + //} + + pm, err := p.usecase.GetProjectMemberByUserId(r.Context(), project.ID, myUserId) + if err != nil { + log.Warnf(r.Context(), "failed to retrieve project member %+v", err) + } + if pm != nil { + projectRoleId = pm.ProjectRole.ID + projectRoleName = pm.ProjectRole.Name + } + + //appCount, err := p.usecase.GetAppCount(organizationId, projectId) + //if err != nil { + // log.Error(r.Context(), "Failed to retrieve app count", err) + // ErrorJSON(w, r, err) + // return + //} + + var out domain.GetProjectResponse + var projectLeaderId, projectLeaderName, projectLeaderAccountId, projectLeaderDepartment string + for _, pu := range project.ProjectMembers { + projectLeaderId = pu.ProjectUser.ID.String() + projectLeaderName = pu.ProjectUser.Name + projectLeaderAccountId = pu.ProjectUser.AccountId + projectLeaderDepartment = pu.ProjectUser.Department + } + + var pdr domain.ProjectDetailResponse + if err = serializer.Map(r.Context(), *project, &pdr); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, err) + return + } + pdr.ProjectLeaderId = projectLeaderId + pdr.ProjectLeaderName = projectLeaderName + pdr.ProjectLeaderAccountId = projectLeaderAccountId + pdr.ProjectLeaderDepartment = projectLeaderDepartment + pdr.ProjectRoleId = projectRoleId + pdr.ProjectRoleName = projectRoleName + //pdr.AppCount = appCount + + out.Project = &pdr + ResponseJSON(w, r, http.StatusOK, out) +} + +// IsProjectNameExist godoc +// +// @Tags Projects +// @Summary Check project name exist +// @Description Check project name exist +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param type query string false "type (name)" +// @Param value query string true "value (project name)" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/projects/existence [get] +// @Security JWT +func (p ProjectHandler) IsProjectNameExist(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 + } + + urlParams := r.URL.Query() + projectName := urlParams.Get("value") + + exist, err := p.usecase.IsProjectNameExist(r.Context(), organizationId, projectName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckExistedResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateProject godoc +// +// @Tags Projects +// @Summary Update project +// @Description Update project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.UpdateProjectRequest true "Request body to update project" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId} [put] +// @Security JWT +func (p ProjectHandler) UpdateProject(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + var projectReq domain.UpdateProjectRequest + if err := UnmarshalRequestInput(r, &projectReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + project, err := p.usecase.GetProjectWithLeader(r.Context(), organizationId, projectId) + if err != nil { + ErrorJSON(w, r, err) + return + } + if project == nil { + project, err = p.usecase.GetProject(r.Context(), organizationId, projectId) + if err != nil { + ErrorJSON(w, r, err) + return + } + } + + project.Name = projectReq.Name + project.Description = projectReq.Description + project.UpdatedAt = &now + + if err := p.usecase.UpdateProject(r.Context(), project, projectReq.ProjectLeaderId); err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +func (p ProjectHandler) DeleteProject(w http.ResponseWriter, r *http.Request) { + //ToDo: to donggyu. implement cleanup logic for k8s & keycloak + + //TODO implement me +} + +// GetProjectRole godoc +// +// @Tags Projects +// @Summary Get project role +// @Description Get project role by id +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectRoleId path string true "Project Role ID" +// @Success 200 {object} domain.GetProjectRoleResponse +// @Router /organizations/{organizationId}/projects/project-roles/{projectRoleId} [get] +// @Security JWT +func (p ProjectHandler) GetProjectRole(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectRoleId, ok := vars["projectRoleId"] + log.Debugf(r.Context(), "projectRoleId = [%v]\n", projectRoleId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectRoleId"), + "C_INVALID_PROJECT_ROLE_ID", "")) + return + } + + pr, err := p.usecase.GetProjectRole(r.Context(), projectRoleId) + if err != nil { + log.Error(r.Context(), "Failed to get project roles ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectRoleResponse + out.ProjectRole = domain.ProjectRoleResponse(*pr) + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectRoles godoc +// +// @Tags Projects +// @Summary Get project roles +// @Description Get project roles by giving params +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param query query string false "project role search by query (query=all), (query=leader), (query=member), (query=viewer)" +// @Success 200 {object} domain.GetProjectRolesResponse +// @Router /organizations/{organizationId}/projects/project-roles [get] +// @Security JWT +func (p ProjectHandler) GetProjectRoles(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + + queryParam := urlParams.Get("query") + var query int + if queryParam == "" || strings.EqualFold(queryParam, "all") { + query = usecase.ProjectAll + } else if strings.EqualFold(queryParam, "leader") { + query = usecase.ProjectLeader + } else if strings.EqualFold(queryParam, "member") { + query = usecase.ProjectMember + } else if strings.EqualFold(queryParam, "viewer") { + query = usecase.ProjectViewer + } else { + log.Error(r.Context(), "Invalid query params. Err: ") + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid query params"), + "C_INVALID_QUERY_PARAM", "")) + return + } + + prs, err := p.usecase.GetProjectRoles(r.Context(), query) + if err != nil { + log.Error(r.Context(), "Failed to get project roles ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectRolesResponse + for _, pr := range prs { + out.ProjectRoles = append(out.ProjectRoles, domain.ProjectRoleResponse(pr)) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// AddProjectMember godoc +// +// @Tags Projects +// @Summary Add project member to project +// @Description Add project member to project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.AddProjectMemberRequest true "Request body to add project member" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members [post] +// @Security JWT +func (p ProjectHandler) AddProjectMember(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + var projectMemberReq domain.AddProjectMemberRequest + if err := UnmarshalRequestInput(r, &projectMemberReq); err != nil { + ErrorJSON(w, r, err) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, pg) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + stackIds := make(map[string]struct{}) + for _, pn := range pns { + stackIds[pn.StackId] = struct{}{} + } + + now := time.Now() + for _, pmr := range projectMemberReq.ProjectMemberRequests { + pu, err := p.usecase.GetProjectUser(r.Context(), pmr.ProjectUserId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectUserId"), + "C_INVALID_PROJECT_USER_ID", "")) + return + } + + pr, err := p.usecase.GetProjectRole(r.Context(), pmr.ProjectRoleId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if pr == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectRoleId"), + "C_INVALID_PROJECT_ROLE_ID", "")) + return + } + + pm := &model.ProjectMember{ + ProjectId: projectId, + ProjectUserId: pu.ID, + ProjectUser: nil, + ProjectRoleId: pr.ID, + ProjectRole: nil, + CreatedAt: now, + } + pmId, err := p.usecase.AddProjectMember(r.Context(), organizationId, pm) + if err != nil { + log.Errorf(r.Context(), "projectMemberId: %s", pmId) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + err = p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pmId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + // tasks for keycloak & k8s + for stackId := range stackIds { + if err := p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pmId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pmr.ProjectUserId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + out := domain.CommonProjectResponse{Result: "OK"} + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectMember godoc +// +// @Tags Projects +// @Summary Get project member +// @Description Get project member +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param projectMemberId path string true "Project Member ID" +// @Success 200 {object} domain.GetProjectMemberResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId} [get] +// @Security JWT +func (p ProjectHandler) GetProjectMember(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + projectMemberId, ok := vars["projectMemberId"] + log.Debugf(r.Context(), "projectMemberId = [%v]\n", projectMemberId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + pm, err := p.usecase.GetProjectMember(r.Context(), projectMemberId) + if err != nil { + log.Error(r.Context(), "Failed to get project member ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectMemberResponse + if pm == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + + pmr := &domain.ProjectMemberResponse{ + ID: pm.ID, + ProjectId: pm.ProjectId, + ProjectUserId: pm.ProjectUser.ID.String(), + ProjectUserName: pm.ProjectUser.Name, + ProjectUserAccountId: pm.ProjectUser.AccountId, + ProjectUserEmail: pm.ProjectUser.Email, + ProjectUserDepartment: pm.ProjectUser.Department, + ProjectRoleId: pm.ProjectRole.ID, + ProjectRoleName: pm.ProjectRole.Name, + CreatedAt: pm.CreatedAt, + UpdatedAt: pm.UpdatedAt, + } + + out.ProjectMember = pmr + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectMembers godoc +// +// @Tags Projects +// @Summary Get project members +// @Description Get project members +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param query query string false "project member search by query (query=all), (query=leader), (query=member), (query=viewer)" +// @Success 200 {object} domain.GetProjectMembersResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members [get] +// @Security JWT +func (p ProjectHandler) GetProjectMembers(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + urlParams := r.URL.Query() + + queryParam := urlParams.Get("query") + var query int + if queryParam == "" || strings.EqualFold(queryParam, "all") { + query = usecase.ProjectAll + } else if strings.EqualFold(queryParam, "leader") { + query = usecase.ProjectLeader + } else if strings.EqualFold(queryParam, "member") { + query = usecase.ProjectMember + } else if strings.EqualFold(queryParam, "viewer") { + query = usecase.ProjectViewer + } else { + log.Error(r.Context(), "Invalid query params. Err: ") + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid query params"), + "C_INVALID_QUERY_PARAM", "")) + return + } + + pg := pagination.NewPagination(&urlParams) + pms, err := p.usecase.GetProjectMembers(r.Context(), projectId, query, pg) + if err != nil { + log.Error(r.Context(), "Failed to get project members ", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectMembersResponse + if pms == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + + pmrs := make([]domain.ProjectMemberResponse, 0) + for _, pm := range pms { + pmr := domain.ProjectMemberResponse{ + ID: pm.ID, + ProjectId: pm.ProjectId, + ProjectUserId: pm.ProjectUser.ID.String(), + ProjectUserName: pm.ProjectUser.Name, + ProjectUserAccountId: pm.ProjectUser.AccountId, + ProjectUserEmail: pm.ProjectUser.Email, + ProjectUserDepartment: pm.ProjectUser.Department, + ProjectRoleId: pm.ProjectRole.ID, + ProjectRoleName: pm.ProjectRole.Name, + CreatedAt: pm.CreatedAt, + UpdatedAt: pm.UpdatedAt, + } + pmrs = append(pmrs, pmr) + } + + out = domain.GetProjectMembersResponse{ProjectMembers: pmrs} + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectMemberCount godoc +// +// @Tags Projects +// @Summary Get project member count group by project role +// @Description Get project member count group by project role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.GetProjectMemberCountResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members/count [get] +// @Security JWT +func (p ProjectHandler) GetProjectMemberCount(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + pmcr, err := p.usecase.GetProjectMemberCount(r.Context(), projectId) + if err != nil { + log.Error(r.Context(), "Failed to get project member count", err) + ErrorJSON(w, r, err) + return + } + + if pmcr == nil { + ResponseJSON(w, r, http.StatusNotFound, domain.GetProjectMembersResponse{}) + return + } + ResponseJSON(w, r, http.StatusOK, pmcr) +} + +// RemoveProjectMember godoc +// +// @Tags Projects +// @Summary Remove project members to project +// @Description Remove project members to project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param projectMemberId path string true "Project Member ID" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId} [delete] +// @Security JWT +func (p ProjectHandler) RemoveProjectMember(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + projectMemberId, ok := vars["projectMemberId"] + log.Debugf(r.Context(), "projectMemberId = [%v]\n", projectMemberId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + // tasks for keycloak & k8s + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, nil) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + stackIds := make(map[string]struct{}) + for _, pn := range pns { + stackIds[pn.StackId] = struct{}{} + } + + err = p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, projectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + for stackId := range stackIds { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", projectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + pm, err := p.usecase.GetProjectMember(r.Context(), projectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, projectMemberId); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + + } + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// RemoveProjectMembers godoc +// +// @Tags Projects +// @Summary Remove project members to project +// @Description Remove project members to project +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.RemoveProjectMemberRequest true "Request body to remove project member" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members [delete] +// @Security JWT +func (p ProjectHandler) RemoveProjectMembers(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + var projectMemberReq domain.RemoveProjectMemberRequest + if err := UnmarshalRequestInput(r, &projectMemberReq); err != nil { + ErrorJSON(w, r, err) + return + } + + // tasks for keycloak & k8s + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, nil) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + stackIds := make(map[string]struct{}) + for _, pn := range pns { + stackIds[pn.StackId] = struct{}{} + } + + // TODO: change multi row delete + for _, pmId := range projectMemberReq.ProjectMember { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pmId.ProjectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + // tasks for keycloak & k8s + for stackId := range stackIds { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pmId.ProjectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + pm, err := p.usecase.GetProjectMember(r.Context(), pmId.ProjectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if err := p.usecase.RemoveProjectMember(r.Context(), organizationId, pmId.ProjectMemberId); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// UpdateProjectMemberRole godoc +// +// @Tags Projects +// @Summary Update project member Role +// @Description Update project member Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param projectMemberId path string true "Project Member ID" +// @Param request body domain.UpdateProjectMemberRoleRequest true "Request body to update project member role" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}/role [put] +// @Security JWT +func (p ProjectHandler) UpdateProjectMemberRole(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + projectMemberId, ok := vars["projectMemberId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + var pmrReq domain.UpdateProjectMemberRoleRequest + if err := UnmarshalRequestInput(r, &pmrReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + pm, err := p.usecase.GetProjectMember(r.Context(), projectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if pm == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + if err = p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, projectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, nil) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + stackIds := make(map[string]struct{}) + for _, pn := range pns { + stackIds[pn.StackId] = struct{}{} + } + // tasks for keycloak & k8s. Unassign old role + for stackId := range stackIds { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", projectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + pm.ProjectRoleId = pmrReq.ProjectRoleId + pm.ProjectUser = nil + pm.ProjectRole = nil + pm.UpdatedAt = &now + + if err := p.usecase.UpdateProjectMemberRole(r.Context(), pm); err != nil { + ErrorJSON(w, r, err) + return + } + + if err := p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, projectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + // tasks for keycloak & k8s. Assign new role + for stackId := range stackIds { + if err := p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", projectMemberId); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + // update token expired time + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// UpdateProjectMembersRole godoc +// +// @Tags Projects +// @Summary Update project member Role +// @Description Update project member Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.UpdateProjectMembersRoleRequest true "Request body to update project member role" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/members [put] +// @Security JWT +func (p ProjectHandler) UpdateProjectMembersRole(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + + projectId, ok := vars["projectId"] + log.Debugf(r.Context(), "projectId = [%v]\n", projectId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + now := time.Now() + var projectMemberReq domain.UpdateProjectMembersRoleRequest + if err := UnmarshalRequestInput(r, &projectMemberReq); err != nil { + ErrorJSON(w, r, err) + return + } + + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, nil) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + stackIds := make(map[string]struct{}) + for _, pn := range pns { + stackIds[pn.StackId] = struct{}{} + } + + for _, pmr := range projectMemberReq.ProjectMemberRoleRequests { + pm, err := p.usecase.GetProjectMember(r.Context(), pmr.ProjectMemberId) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if pm == nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectMemberId"), + "C_INVALID_PROJECT_MEMBER_ID", "")) + return + } + + if err = p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pm.ID); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + for stackId := range stackIds { + if err := p.usecase.UnassignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pm.ID); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + pm.ProjectRoleId = pmr.ProjectRoleId + pm.ProjectUser = nil + pm.ProjectRole = nil + pm.UpdatedAt = &now + + if err := p.usecase.UpdateProjectMemberRole(r.Context(), pm); err != nil { + ErrorJSON(w, r, err) + return + } + + if err := p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, keycloak.DefaultClientID, pm.ID); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + for stackId := range stackIds { + if err := p.usecase.AssignKeycloakClientRoleToMember(r.Context(), organizationId, projectId, stackId+"-k8s-api", pm.ID); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + // update token expired time + err = p.authUsecase.UpdateExpiredTimeOnToken(r.Context(), organizationId, pm.ProjectUserId.String()) + if err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// CreateProjectNamespace godoc +// +// @Tags Projects +// @Summary Create project namespace +// @Description Create project namespace +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param request body domain.CreateProjectNamespaceRequest true "Request body to create project namespace" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces [post] +// @Security JWT +func (p ProjectHandler) CreateProjectNamespace(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + var projectNamespaceReq domain.CreateProjectNamespaceRequest + if err := UnmarshalRequestInput(r, &projectNamespaceReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + pn := &model.ProjectNamespace{ + StackId: projectNamespaceReq.StackId, + Namespace: projectNamespaceReq.Namespace, + ProjectId: projectId, + Stack: nil, + Description: projectNamespaceReq.Description, + Status: "RUNNING", + CreatedAt: now, + } + + // tasks for keycloak & k8s + // ToDo: Check if the namespace is already created + if err := p.usecase.EnsureNamespaceForCluster(r.Context(), organizationId, projectNamespaceReq.StackId, projectNamespaceReq.Namespace); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if err := p.usecase.EnsureRequiredSetupForCluster(r.Context(), organizationId, projectId, projectNamespaceReq.StackId); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if err := p.usecase.CreateK8SNSRoleBinding(r.Context(), organizationId, projectId, projectNamespaceReq.StackId, projectNamespaceReq.Namespace); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + if err := p.usecase.CreateProjectNamespace(r.Context(), organizationId, pn); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + out := domain.CommonProjectResponse{Result: "OK"} + ResponseJSON(w, r, http.StatusOK, out) +} + +// IsProjectNamespaceExist godoc +// +// @Tags Projects +// @Summary Check project namespace exist +// @Description Check project namespace exist +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param stackId path string true "Project Stack ID" +// @Param projectNamespace path string true "Project Namespace" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/existence [get] +// @Security JWT +func (p ProjectHandler) IsProjectNamespaceExist(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("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + stackId, ok := vars["stackId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackId"), + "C_INVALID_STACK_ID", "")) + return + } + projectNamespace, ok := vars["projectNamespace"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("projectNamespace not found in path"), + "C_INVALID_PROJECT_NAMESPACE", "")) + return + } + + exist, err := p.usecase.IsProjectNamespaceExist(r.Context(), organizationId, projectId, stackId, projectNamespace) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckExistedResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectNamespaces godoc +// +// @Tags Projects +// @Summary Get project namespaces +// @Description Get project namespaces +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.GetProjectNamespacesResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces [get] +// @Security JWT +func (p ProjectHandler) GetProjectNamespaces(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("invalid projectId"), + "C_INVALID_PROJECT_ID", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + pns, err := p.usecase.GetProjectNamespaces(r.Context(), organizationId, projectId, pg) + if err != nil { + log.Error(r.Context(), "Failed to get project namespaces.", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectNamespacesResponse + if pns == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + pnrs := make([]domain.ProjectNamespaceResponse, 0) + for _, pn := range pns { + var pnr domain.ProjectNamespaceResponse + if err = serializer.Map(r.Context(), pn, &pnr); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, err) + return + } + appCount, err := p.usecase.GetAppCount(r.Context(), organizationId, projectId, pn.Namespace) + if err != nil { + log.Error(r.Context(), "Failed to retrieve app count", err) + ErrorJSON(w, r, err) + return + } + + pnr.StackName = pn.Stack.Name + pnr.AppCount = appCount + pnrs = append(pnrs, pnr) + } + + out.ProjectNamespaces = pnrs + ResponseJSON(w, r, http.StatusOK, out) + +} + +// GetProjectNamespace godoc +// +// @Tags Projects +// @Summary Get project namespace +// @Description Get project namespace +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param projectNamespace path string true "Project Namespace" +// @Param stackId path string true "Project Stack ID" +// @Success 200 {object} domain.GetProjectNamespaceResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId} [get] +// @Security JWT +func (p ProjectHandler) GetProjectNamespace(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("invalid projectId"), + "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 + } + + pn, err := p.usecase.GetProjectNamespace(r.Context(), organizationId, projectId, projectNamespace, stackId) + if err != nil { + log.Error(r.Context(), "Failed to get project namespace.", err) + ErrorJSON(w, r, err) + return + } + + appCount, err := p.usecase.GetAppCount(r.Context(), organizationId, projectId, projectNamespace) + if err != nil { + log.Error(r.Context(), "Failed to retrieve app count", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectNamespaceResponse + if pn == nil { + ResponseJSON(w, r, http.StatusNotFound, out) + return + } + + var pnr domain.ProjectNamespaceResponse + if err = serializer.Map(r.Context(), *pn, &pnr); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, err) + return + } + pnr.StackName = pn.Stack.Name + pnr.AppCount = appCount + + out.ProjectNamespace = &pnr + ResponseJSON(w, r, http.StatusOK, out) + +} + +// UpdateProjectNamespace godoc +// +// @Tags Projects +// @Summary Update project namespace +// @Description Update project namespace +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Param projectNamespace path string true "Project Namespace" +// @Param stackId path string true "Project Stack ID" +// @Param request body domain.UpdateProjectNamespaceRequest true "Request body to update project namespace" +// @Success 200 {object} domain.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId} [put] +// @Security JWT +func (p ProjectHandler) UpdateProjectNamespace(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + log.Debugf(r.Context(), "organizationId = [%v]\n", organizationId) + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), + "C_INVALID_ORGANIZATION_ID", "")) + return + } + projectId, ok := vars["projectId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid projectId"), + "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 + } + + var projectNamespaceReq domain.UpdateProjectNamespaceRequest + if err := UnmarshalRequestInput(r, &projectNamespaceReq); err != nil { + ErrorJSON(w, r, err) + return + } + + now := time.Now() + pn, err := p.usecase.GetProjectNamespace(r.Context(), organizationId, projectId, projectNamespace, stackId) + if err != nil { + log.Error(r.Context(), "Failed to get project namespace.", err) + ErrorJSON(w, r, err) + return + } + + pn.Description = projectNamespaceReq.Description + pn.UpdatedAt = &now + + if err := p.usecase.UpdateProjectNamespace(r.Context(), pn); err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, domain.CommonProjectResponse{Result: "OK"}) +} + +// DeleteProjectNamespace godoc +// +// @Tags Projects +// @Summary Delete project namespace +// @Description Delete 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.CommonProjectResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId} [delete] +// @Security JWT +func (p ProjectHandler) DeleteProjectNamespace(w http.ResponseWriter, r *http.Request) { + + //ToDo: from donggyu. uncomment lines below after implementing usecase.DeleteProjectNamespace. + // tasks for keycloak & k8s + /*if err := p.usecase.DeleteK8SNSRoleBinding(organizationId, projectId, stackId, projectNamespace); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + if err := p.usecase.MayRemoveRequiredSetupForCluster(organizationId, projectId, stackId); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + }*/ + + //TODO implement me + + //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("invalid projectId"), + // "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 + //} + // + //if err := p.usecase.DeleteProjectNamespace(organizationId, projectId, projectNamespace, stackId); err != nil { + // ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + // return + // + //} + //ResponseJSON(w, r, http.StatusOK, model.CommonProjectResponse{Result: "OK"}) +} + +func (p ProjectHandler) SetFavoriteProject(w http.ResponseWriter, r *http.Request) { + //TODO implement me +} + +func (p ProjectHandler) SetFavoriteProjectNamespace(w http.ResponseWriter, r *http.Request) { + //TODO implement me +} + +func (p ProjectHandler) UnSetFavoriteProject(w http.ResponseWriter, r *http.Request) { + //TODO implement me +} + +func (p ProjectHandler) UnSetFavoriteProjectNamespace(w http.ResponseWriter, r *http.Request) { + //TODO implement me +} + +// GetProjectKubeconfig godoc +// +// @Tags Projects +// @Summary Get project kubeconfig +// @Description Get project kubeconfig +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param projectId path string true "Project ID" +// @Success 200 {object} domain.GetProjectKubeconfigResponse +// @Router /organizations/{organizationId}/projects/{projectId}/kube-config [get] +// @Security JWT +func (p ProjectHandler) GetProjectKubeconfig(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 + } + + kubeconfig, err := p.usecase.GetProjectKubeconfig(r.Context(), organizationId, projectId) + if err != nil { + log.Error(r.Context(), "Failed to get project kubeconfig.", err) + ErrorJSON(w, r, err) + return + } + + out := domain.GetProjectKubeconfigResponse{ + Kubeconfig: kubeconfig, + } + + 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, domain.StackId(stackId)) + if err != nil { + log.Error(r.Context(), "Failed to get project resources.", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectNamespaceK8sResourcesResponse + if err = serializer.Map(r.Context(), k8sResources, &out.K8sResources); err != nil { + log.Error(r.Context(), err) + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectNamespaceResourcesUsage godoc +// +// @Tags Projects +// @Summary Get resources usage for project namespace +// @Description Get resources usage 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.GetProjectNamespaceResourcesUsageResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/resources-usage [get] +// @Security JWT +func (p ProjectHandler) GetProjectNamespaceResourcesUsage(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 + } + + thanosClient, err := p.dashboardUsecase.GetThanosClient(r.Context(), organizationId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Failed to get thanos client"), "", "")) + return + } + + resourcesUsage, err := p.usecase.GetResourcesUsage(r.Context(), thanosClient, organizationId, projectId, projectNamespace, domain.StackId(stackId)) + if err != nil { + log.Error(r.Context(), "Failed to get project resources.", err) + ErrorJSON(w, r, err) + return + } + + var out domain.GetProjectNamespaceResourcesUsageResponse + if err = serializer.Map(r.Context(), resourcesUsage, &out.ResourcesUsage); err != nil { + log.Error(r.Context(), err) + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetProjectNamespaceKubeconfig godoc +// +// @Tags Projects +// @Summary Get project namespace kubeconfig +// @Description Get project namespace kubeconfig +// @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.GetProjectNamespaceKubeConfigResponse +// @Router /organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/kube-config [get] +// @Security JWT +func (p ProjectHandler) GetProjectNamespaceKubeconfig(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 + } + + kubeconfig, err := p.usecase.GetProjectNamespaceKubeconfig(r.Context(), organizationId, projectId, projectNamespace, domain.StackId(stackId)) + if err != nil { + log.Error(r.Context(), "Failed to get project kubeconfig.", err) + ErrorJSON(w, r, err) + return + } + + out := domain.GetProjectNamespaceKubeConfigResponse{ + KubeConfig: kubeconfig, + } + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/role.go b/internal/delivery/http/role.go new file mode 100644 index 00000000..1ff872be --- /dev/null +++ b/internal/delivery/http/role.go @@ -0,0 +1,742 @@ +package http + +import ( + "context" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" +) + +type IRoleHandler interface { + CreateTksRole(w http.ResponseWriter, r *http.Request) + ListTksRoles(w http.ResponseWriter, r *http.Request) + GetTksRole(w http.ResponseWriter, r *http.Request) + DeleteTksRole(w http.ResponseWriter, r *http.Request) + UpdateTksRole(w http.ResponseWriter, r *http.Request) + GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) + UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) + IsRoleNameExisted(w http.ResponseWriter, r *http.Request) + + GetUsersInRoleId(w http.ResponseWriter, r *http.Request) + AppendUsersToRole(w http.ResponseWriter, r *http.Request) + RemoveUsersFromRole(w http.ResponseWriter, r *http.Request) + + Admin_ListTksRoles(w http.ResponseWriter, r *http.Request) + Admin_GetTksRole(w http.ResponseWriter, r *http.Request) +} + +type RoleHandler struct { + roleUsecase usecase.IRoleUsecase + userUsecase usecase.IUserUsecase + permissionUsecase usecase.IPermissionUsecase +} + +func NewRoleHandler(usecase usecase.Usecase) *RoleHandler { + return &RoleHandler{ + roleUsecase: usecase.Role, + permissionUsecase: usecase.Permission, + userUsecase: usecase.User, + } +} + +// CreateTksRole godoc +// +// @Tags Roles +// @Summary Create Tks Role +// @Description Create Tks Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param body body domain.CreateTksRoleRequest true "Create Tks Role Request" +// @Success 200 {object} domain.CreateTksRoleResponse +// @Router /organizations/{organizationId}/roles [post] +// @Security JWT +func (h RoleHandler) CreateTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + var organizationId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // request body + input := domain.CreateTksRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + dto := model.Role{ + OrganizationID: organizationId, + Name: input.Name, + Description: input.Description, + Type: string(domain.RoleTypeTks), + } + + // create role + var roleId string + if roleId, err = h.roleUsecase.CreateTksRole(r.Context(), &dto); err != nil { + ErrorJSON(w, r, err) + return + } + + // create permission + defaultPermissionSet := model.NewDefaultPermissionSet() + h.permissionUsecase.SetRoleIdToPermissionSet(r.Context(), roleId, defaultPermissionSet) + err = h.permissionUsecase.CreatePermissionSet(r.Context(), defaultPermissionSet) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, domain.CreateTksRoleResponse{ID: roleId}) +} + +// ListTksRoles godoc +// +// @Tags Roles +// @Summary List Tks Roles +// @Description List Tks Roles +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.ListTksRoleResponse +// @Router /organizations/{organizationId}/roles [get] +// @Security JWT +func (h RoleHandler) ListTksRoles(w http.ResponseWriter, r *http.Request) { + // path parameter + var organizationId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // query parameter + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + + // list roles + roles, err := h.roleUsecase.ListTksRoles(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListTksRoleResponse + out.Roles = make([]domain.GetTksRoleResponse, len(roles)) + for i, role := range roles { + out.Roles[i] = domain.GetTksRoleResponse{ + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + } + + if err := serializer.Map(r.Context(), *pg, &out.Pagination); err != nil { + log.Info(r.Context(), err) + } + + // response + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetTksRole godoc +// +// @Tags Roles +// @Summary Get Tks Role +// @Description Get Tks Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.GetTksRoleResponse +// @Router /organizations/{organizationId}/roles/{roleId} [get] +// @Security JWT +func (h RoleHandler) GetTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var organizationId, roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + } else { + roleId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + } else { + organizationId = v + } + + // get role + role, err := h.roleUsecase.GetTksRole(r.Context(), organizationId, roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + out := domain.GetTksRoleResponse{ + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// DeleteTksRole godoc +// +// @Tags Roles +// @Summary Delete Tks Role +// @Description Delete Tks Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId} [delete] +// @Security JWT +func (h RoleHandler) DeleteTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + var organizationId string + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // delete role + if err := h.roleUsecase.DeleteTksRole(r.Context(), organizationId, roleId); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// UpdateTksRole godoc +// +// @Tags Roles +// @Summary Update Tks Role +// @Description Update Tks Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdateTksRoleRequest true "Update Tks Role Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId} [put] +// @Security JWT +func (h RoleHandler) UpdateTksRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request body + input := domain.UpdateTksRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // input to dto + dto := model.Role{ + ID: roleId, + Name: input.Name, + Description: input.Description, + } + + // update role + if err := h.roleUsecase.UpdateTksRole(r.Context(), &dto); err != nil { + ErrorJSON(w, r, err) + return + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetPermissionsByRoleId godoc +// +// @Tags Roles +// @Summary Get Permissions By Role ID +// @Description Get Permissions By Role ID +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.GetPermissionsByRoleIdResponse +// @Router /organizations/{organizationId}/roles/{roleId}/permissions [get] +// @Security JWT +func (h RoleHandler) GetPermissionsByRoleId(w http.ResponseWriter, r *http.Request) { + // path parameter + var roleId string + + vars := mux.Vars(r) + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + permissionSet, err := h.permissionUsecase.GetPermissionSetByRoleId(r.Context(), roleId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + var out domain.GetPermissionsByRoleIdResponse + out.Permissions = make([]*domain.PermissionResponse, 0) + + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.Dashboard)) + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.Stack)) + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.Policy)) + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.ProjectManagement)) + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.Notification)) + out.Permissions = append(out.Permissions, convertModelToPermissionResponse(r.Context(), permissionSet.Configuration)) + + ResponseJSON(w, r, http.StatusOK, out) +} + +func convertModelToPermissionResponse(ctx context.Context, permission *model.Permission) *domain.PermissionResponse { + var permissionResponse domain.PermissionResponse + + permissionResponse.Key = permission.Key + permissionResponse.Name = permission.Name + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + permissionResponse.ID = &permission.ID + } + + for _, endpoint := range permission.Endpoints { + permissionResponse.Endpoints = append(permissionResponse.Endpoints, convertModelToEndpointResponse(ctx, endpoint)) + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToPermissionResponse(ctx, child)) + } + + return &permissionResponse +} + +func convertModelToEndpointResponse(_ context.Context, endpoint *model.Endpoint) *domain.EndpointResponse { + var endpointResponse domain.EndpointResponse + + endpointResponse.Name = endpoint.Name + endpointResponse.Group = endpoint.Group + + return &endpointResponse +} + +// UpdatePermissionsByRoleId godoc +// +// @Tags Roles +// @Summary Update Permissions By Role ID +// @Description Update Permissions By Role ID +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.UpdatePermissionsByRoleIdRequest true "Update Permissions By Role ID Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId}/permissions [put] +// @Security JWT +func (h RoleHandler) UpdatePermissionsByRoleId(w http.ResponseWriter, r *http.Request) { + // request + input := domain.UpdatePermissionsByRoleIdRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + for _, permissionResponse := range input.Permissions { + var permission model.Permission + permission.ID = permissionResponse.ID + permission.IsAllowed = permissionResponse.IsAllowed + + if err := h.permissionUsecase.UpdatePermission(r.Context(), &permission); err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// Admin_ListTksRoles godoc +// +// @Tags Roles +// @Summary Admin List Tks Roles +// @Description Admin List Tks Roles +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Success 200 {object} domain.ListTksRoleResponse +// @Router /admin/organizations/{organizationId}/roles [get] +// @Security JWT +func (h RoleHandler) Admin_ListTksRoles(w http.ResponseWriter, r *http.Request) { + // Same as ListTksRoles + + var organizationId string + + vars := mux.Vars(r) + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // query parameter + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + + // list roles + roles, err := h.roleUsecase.ListTksRoles(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.ListTksRoleResponse + out.Roles = make([]domain.GetTksRoleResponse, len(roles)) + for i, role := range roles { + out.Roles[i] = domain.GetTksRoleResponse{ + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + } + + if err := serializer.Map(r.Context(), *pg, &out.Pagination); err != nil { + log.Info(r.Context(), err) + } + + // response + ResponseJSON(w, r, http.StatusOK, out) +} + +// Admin_GetTksRole godoc +// +// @Tags Roles +// @Summary Admin Get Tks Role +// @Description Admin Get Tks Role +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Success 200 {object} domain.GetTksRoleResponse +// @Router /admin/organizations/{organizationId}/roles/{roleId} [get] +// @Security JWT +func (h RoleHandler) Admin_GetTksRole(w http.ResponseWriter, r *http.Request) { + // Same as GetTksRole + + vars := mux.Vars(r) + var organizationId, roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + } else { + roleId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + } else { + organizationId = v + } + + // get role + role, err := h.roleUsecase.GetTksRole(r.Context(), organizationId, roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + // response + out := domain.GetTksRoleResponse{ + ID: role.ID, + Name: role.Name, + OrganizationID: role.OrganizationID, + Description: role.Description, + Creator: role.Creator.String(), + CreatedAt: role.CreatedAt, + UpdatedAt: role.UpdatedAt, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// IsRoleNameExisted godoc +// +// @Tags Roles +// @Summary Check whether the role name exists +// @Description Check whether the role name exists +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleName path string true "Role Name" +// @Success 200 {object} domain.CheckRoleNameResponse +// @Router /organizations/{organizationId}/roles/{roleName}/existence [get] +// @Security JWT +func (h RoleHandler) IsRoleNameExisted(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var organizationId, roleName string + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + if v, ok := vars["roleName"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleName = v + } + + // check role name exist + isExist, err := h.roleUsecase.IsRoleNameExisted(r.Context(), organizationId, roleName) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CheckRoleNameResponse + out.IsExist = isExist + + // response + ResponseJSON(w, r, http.StatusOK, out) +} + +// AppendUsersToRole godoc +// +// @Tags Roles +// @Summary Append Users To Role +// @Description Append Users To Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.AppendUsersToRoleRequest true "Append Users To Role Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId}/users [post] +// @Security JWT +func (h RoleHandler) AppendUsersToRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var organizationId, roleId string + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request body + input := domain.AppendUsersToRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + for _, user := range input.Users { + originUser, err := h.userUsecase.Get(r.Context(), user) + if err != nil { + ErrorJSON(w, r, err) + return + } + + role, err := h.roleUsecase.GetTksRole(r.Context(), organizationId, roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + originUser.Roles = append(originUser.Roles, *role) + + if _, err := h.userUsecase.UpdateByAccountIdByAdmin(r.Context(), originUser); err != nil { + ErrorJSON(w, r, err) + return + } + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// RemoveUsersFromRole godoc +// +// @Tags Roles +// @Summary Remove Users From Role +// @Description Remove Users From Role +// @Accept json +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param body body domain.RemoveUsersFromRoleRequest true "Remove Users From Role Request" +// @Success 200 +// @Router /organizations/{organizationId}/roles/{roleId}/users [delete] +// @Security JWT +func (h RoleHandler) RemoveUsersFromRole(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var organizationId, roleId string + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + + // request body + input := domain.RemoveUsersFromRoleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + for _, user := range input.Users { + originUser, err := h.userUsecase.Get(r.Context(), user) + if err != nil { + ErrorJSON(w, r, err) + return + } + + role, err := h.roleUsecase.GetTksRole(r.Context(), organizationId, roleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + for i, r := range originUser.Roles { + if r.ID == role.ID { + originUser.Roles = append(originUser.Roles[:i], originUser.Roles[i+1:]...) + break + } + } + + if _, err := h.userUsecase.UpdateByAccountIdByAdmin(r.Context(), originUser); err != nil { + ErrorJSON(w, r, err) + return + } + } + + // response + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetUsersInRoleId godoc +// +// @Tags Roles +// @Summary Get Users By Role ID +// @Description Get Users By Role ID +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param roleId path string true "Role ID" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetUsersInRoleIdResponse +// @Router /organizations/{organizationId}/roles/{roleId}/users [get] +// @Security JWT +func (h RoleHandler) GetUsersInRoleId(w http.ResponseWriter, r *http.Request) { + // path parameter + vars := mux.Vars(r) + var organizationId, roleId string + if v, ok := vars["roleId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + roleId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + users, err := h.userUsecase.ListUsersByRole(r.Context(), organizationId, roleId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetUsersInRoleIdResponse + out.Users = make([]domain.SimpleUserResponse, len(*users)) + for i, user := range *users { + out.Users[i] = domain.SimpleUserResponse{ + ID: user.ID.String(), + AccountId: user.AccountId, + Name: user.Name, + Email: user.Email, + Department: user.Department, + } + } + + if err := serializer.Map(r.Context(), *pg, &out.Pagination); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/stack-template.go b/internal/delivery/http/stack-template.go index 29b4af18..78faf378 100644 --- a/internal/delivery/http/stack-template.go +++ b/internal/delivery/http/stack-template.go @@ -7,6 +7,8 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -20,48 +22,66 @@ type StackTemplateHandler struct { usecase usecase.IStackTemplateUsecase } -func NewStackTemplateHandler(h usecase.IStackTemplateUsecase) *StackTemplateHandler { +func NewStackTemplateHandler(h usecase.Usecase) *StackTemplateHandler { return &StackTemplateHandler{ - usecase: h, + usecase: h.StackTemplate, } } // CreateStackTemplate godoc -// @Tags StackTemplates -// @Summary Create StackTemplate 'NOT IMPLEMENTED' -// @Description Create StackTemplate -// @Accept json -// @Produce json -// @Param body body domain.CreateStackTemplateRequest true "create stack template request" -// @Success 200 {object} domain.CreateStackTemplateResponse -// @Router /stack-templates [post] -// @Security JWT +// +// @Tags StackTemplates +// @Summary Create StackTemplate +// @Description Create StackTemplate +// @Accept json +// @Produce json +// @Param body body domain.CreateStackTemplateRequest true "create stack template request" +// @Success 200 {object} domain.CreateStackTemplateResponse +// @Router /admin/stack-templates [post] +// @Security JWT func (h *StackTemplateHandler) CreateStackTemplate(w http.ResponseWriter, r *http.Request) { - ErrorJSON(w, r, fmt.Errorf("need implementation")) -} + input := domain.CreateStackTemplateRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } -// GetStackTemplate godoc -// @Tags StackTemplates -// @Summary Get StackTemplates -// @Description Get StackTemplates -// @Accept json -// @Produce json -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} domain.GetStackTemplatesResponse -// @Router /stack-templates [get] -// @Security JWT -func (h *StackTemplateHandler) GetStackTemplates(w http.ResponseWriter, r *http.Request) { - urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) + var dto model.StackTemplate + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + + id, err := h.usecase.Create(r.Context(), dto) if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + ErrorJSON(w, r, err) return } + out := domain.CreateStackTemplateResponse{ + ID: id.String(), + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetStackTemplate godoc +// +// @Tags StackTemplates +// @Summary Get StackTemplates +// @Description Get StackTemplates +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetStackTemplatesResponse +// @Router /admin/stack-templates [get] +// @Security JWT +func (h *StackTemplateHandler) GetStackTemplates(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) stackTemplates, err := h.usecase.Fetch(r.Context(), pg) if err != nil { ErrorJSON(w, r, err) @@ -71,33 +91,42 @@ func (h *StackTemplateHandler) GetStackTemplates(w http.ResponseWriter, r *http. var out domain.GetStackTemplatesResponse out.StackTemplates = make([]domain.StackTemplateResponse, len(stackTemplates)) for i, stackTemplate := range stackTemplates { - if err := serializer.Map(stackTemplate, &out.StackTemplates[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), stackTemplate, &out.StackTemplates[i]); err != nil { + log.Info(r.Context(), err) + } + + out.StackTemplates[i].Organizations = make([]domain.SimpleOrganizationResponse, len(stackTemplate.Organizations)) + for j, organization := range stackTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.StackTemplates[i].Organizations[j]); err != nil { + log.Info(r.Context(), err) + continue + } } err := json.Unmarshal(stackTemplate.Services, &out.StackTemplates[i].Services) if err != nil { - log.ErrorWithContext(r.Context(), err) + log.Error(r.Context(), err) } } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetStackTemplate godoc -// @Tags StackTemplates -// @Summary Get StackTemplate -// @Description Get StackTemplate -// @Accept json -// @Produce json -// @Param stackTemplateId path string true "stackTemplateId" -// @Success 200 {object} domain.GetStackTemplateResponse -// @Router /stack-templates/{stackTemplateId} [get] -// @Security JWT +// +// @Tags StackTemplates +// @Summary Get StackTemplate +// @Description Get StackTemplate +// @Accept json +// @Produce json +// @Param stackTemplateId path string true "stackTemplateId" +// @Success 200 {object} domain.GetStackTemplateResponse +// @Router /admin/stack-templates/{stackTemplateId} [get] +// @Security JWT func (h *StackTemplateHandler) GetStackTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["stackTemplateId"] @@ -119,76 +148,415 @@ func (h *StackTemplateHandler) GetStackTemplate(w http.ResponseWriter, r *http.R } var out domain.GetStackTemplateResponse - if err := serializer.Map(stackTemplate, &out.StackTemplate); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), stackTemplate, &out.StackTemplate); err != nil { + log.Info(r.Context(), err) + } + + out.StackTemplate.Organizations = make([]domain.SimpleOrganizationResponse, len(stackTemplate.Organizations)) + for i, organization := range stackTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.StackTemplate.Organizations[i]); err != nil { + log.Info(r.Context(), err) + continue + } } err = json.Unmarshal(stackTemplate.Services, &out.StackTemplate.Services) if err != nil { - log.ErrorWithContext(r.Context(), err) + log.Error(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // UpdateStackTemplate godoc -// @Tags StackTemplates -// @Summary Update StackTemplate 'NOT IMPLEMENTED' -// @Description Update StackTemplate -// @Accept json -// @Produce json -// @Param body body domain.UpdateStackTemplateRequest true "Update stack template request" -// @Success 200 {object} nil -// @Router /stack-templates/{stackTemplateId} [put] -// @Security JWT +// +// @Tags StackTemplates +// @Summary Update StackTemplate +// @Description Update StackTemplate +// @Accept json +// @Produce json +// @Param body body domain.UpdateStackTemplateRequest true "Update stack template request" +// @Success 200 {object} nil +// @Router /admin/stack-templates/{stackTemplateId} [put] +// @Security JWT func (h *StackTemplateHandler) UpdateStackTemplate(w http.ResponseWriter, r *http.Request) { - /* - vars := mux.Vars(r) - strId, ok := vars["stackTemplateId"] - if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackTemplateId"))) - return + vars := mux.Vars(r) + strId, ok := vars["stackTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + stackTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + input := domain.UpdateStackTemplateRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.StackTemplate + if err := serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.ID = stackTemplateId + + err = h.usecase.Update(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeleteStackTemplate godoc +// +// @Tags StackTemplates +// @Summary Delete StackTemplate +// @Description Delete StackTemplate +// @Accept json +// @Produce json +// @Param stackTemplateId path string true "stackTemplateId" +// @Success 200 {object} nil +// @Router /admin/stack-templates/{stackTemplateId} [delete] +// @Security JWT +func (h *StackTemplateHandler) DeleteStackTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["stackTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + stackTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + err = h.usecase.Delete(r.Context(), stackTemplateId) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetStackTemplateServices godoc +// +// @Tags StackTemplates +// @Summary Get GetStackTemplateServices +// @Description Get GetStackTemplateServices +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetStackTemplateServicesResponse +// @Router /admin/stack-templates/services [get] +// @Security JWT +func (h *StackTemplateHandler) GetStackTemplateServices(w http.ResponseWriter, r *http.Request) { + + var out domain.GetStackTemplateServicesResponse + out.Services = make([]domain.StackTemplateServiceResponse, 2) + err := json.Unmarshal([]byte(internal.SERVICE_LMA), &out.Services[0]) + if err != nil { + log.Error(r.Context(), err) + } + + err = json.Unmarshal([]byte(internal.SERVICE_SERVICE_MESH), &out.Services[1]) + if err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetStackTemplateTemplateIds godoc +// +// @Tags StackTemplates +// @Summary Get GetStackTemplateTemplateIds +// @Description Get GetStackTemplateTemplateIds +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetStackTemplateTemplateIdsResponse +// @Router /admin/stack-templates/template-ids [get] +// @Security JWT +func (h *StackTemplateHandler) GetStackTemplateTemplateIds(w http.ResponseWriter, r *http.Request) { + + var out domain.GetStackTemplateTemplateIdsResponse + templateIds, err := h.usecase.GetTemplateIds(r.Context()) + if err != nil { + templateIds = []string{"aws-reference", "aws-msa-reference", "eks-reference", "eks-msa-reference", "byoh-reference"} + } + + out.TemplateIds = templateIds + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateStackTemplateOrganizations godoc +// +// @Tags StackTemplates +// @Summary Update StackTemplate organizations +// @Description Update StackTemplate organizations +// @Accept json +// @Produce json +// @Param body body domain.UpdateStackTemplateOrganizationsRequest true "Update stack template organizations request" +// @Success 200 {object} nil +// @Router /admin/stack-templates/{stackTemplateId}/organizations [put] +// @Security JWT +func (h *StackTemplateHandler) UpdateStackTemplateOrganizations(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["stackTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + stackTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + input := domain.UpdateStackTemplateOrganizationsRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.StackTemplate + if err := serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.ID = stackTemplateId + + err = h.usecase.UpdateOrganizations(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetOrganizationStackTemplates godoc +// +// @Tags StackTemplates +// @Summary Get Organization StackTemplates +// @Description Get Organization StackTemplates +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetStackTemplatesResponse +// @Router /organizations/{organizationId}/stack-templates [get] +// @Security JWT +func (h *StackTemplateHandler) GetOrganizationStackTemplates(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + stackTemplates, err := h.usecase.FetchWithOrganization(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetStackTemplatesResponse + out.StackTemplates = make([]domain.StackTemplateResponse, len(stackTemplates)) + for i, stackTemplate := range stackTemplates { + if err := serializer.Map(r.Context(), stackTemplate, &out.StackTemplates[i]); err != nil { + log.Info(r.Context(), err) + } + + out.StackTemplates[i].Organizations = make([]domain.SimpleOrganizationResponse, len(stackTemplate.Organizations)) + for j, organization := range stackTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.StackTemplates[i].Organizations[j]); err != nil { + log.Info(r.Context(), err) + } } - stackTemplateId, err := uuid.Parse(strId) + err := json.Unmarshal(stackTemplate.Services, &out.StackTemplates[i].Services) if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"))) - return + log.Error(r.Context(), err) } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetOrganizationStackTemplate godoc +// +// @Tags StackTemplates +// @Summary Get Organization StackTemplate +// @Description Get Organization StackTemplate +// @Accept json +// @Produce json +// @Success 200 {object} domain.GetStackTemplateResponse +// @Router /organizations/{organizationId}/stack-templates/{stackTemplateId} [get] +// @Security JWT +func (h *StackTemplateHandler) GetOrganizationStackTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + _, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + strId, ok := vars["stackTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } - var dto domain.StackTemplate - if err := serializer.Map(r, &dto); err != nil { - log.InfoWithContext(r.Context(),err) + stackTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + stackTemplate, err := h.usecase.Get(r.Context(), stackTemplateId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetStackTemplateResponse + if err := serializer.Map(r.Context(), stackTemplate, &out.StackTemplate); err != nil { + log.Info(r.Context(), err) + } + + out.StackTemplate.Organizations = make([]domain.SimpleOrganizationResponse, len(stackTemplate.Organizations)) + for i, organization := range stackTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.StackTemplate.Organizations[i]); err != nil { + log.Info(r.Context(), err) } - dto.ID = stackTemplateId + } - err = h.usecase.Update(r.Context(), dto) - if err != nil { + err = json.Unmarshal(stackTemplate.Services, &out.StackTemplate.Services) + if err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// CheckStackTemplateName godoc +// +// @Tags StackTemplates +// @Summary Check name for stackTemplate +// @Description Check name for stackTemplate +// @Accept json +// @Produce json +// @Param name path string true "name" +// @Success 200 {object} domain.CheckStackTemplateNameResponse +// @Router /admin/stack-templates/name/{name}/existence [GET] +// @Security JWT +func (h *StackTemplateHandler) CheckStackTemplateName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid name"), "ST_INVALID_STACK_TEMAPLTE_NAME", "")) + return + } + + exist := true + _, err := h.usecase.GetByName(r.Context(), name) + if err != nil { + if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { + exist = false + } else { ErrorJSON(w, r, err) return } - */ + } + + var out domain.CheckStackTemplateNameResponse + out.Existed = exist - ErrorJSON(w, r, fmt.Errorf("need implementation")) + ResponseJSON(w, r, http.StatusOK, out) } -// DeleteStackTemplate godoc -// @Tags StackTemplates -// @Summary Delete StackTemplate 'NOT IMPLEMENTED' -// @Description Delete StackTemplate -// @Accept json -// @Produce json -// @Param stackTemplateId path string true "stackTemplateId" -// @Success 200 {object} nil -// @Router /stack-templates/{stackTemplateId} [delete] -// @Security JWT -func (h *StackTemplateHandler) DeleteStackTemplate(w http.ResponseWriter, r *http.Request) { +// AddOrganizationStackTemplates godoc +// +// @Tags StackTemplates +// @Summary Add organization stackTemplates +// @Description Add organization stackTemplates +// @Accept json +// @Produce json +// @Param body body domain.AddOrganizationStackTemplatesRequest true "Add organization stack templates request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stack-templates [post] +// @Security JWT +func (h *StackTemplateHandler) AddOrganizationStackTemplates(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - _, ok := vars["stackTemplateId"] + organizationId, ok := vars["organizationId"] if !ok { - ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid stackTemplateId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.AddOrganizationStackTemplatesRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + err = h.usecase.AddOrganizationStackTemplates(r.Context(), organizationId, input.StackTemplateIds) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// RemoveOrganizationStackTemplates godoc +// +// @Tags StackTemplates +// @Summary Remove organization stackTemplates +// @Description Remove organization stackTemplates +// @Accept json +// @Produce json +// @Param body body domain.RemoveOrganizationStackTemplatesRequest true "Remove organization stack templates request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stack-templates [put] +// @Security JWT +func (h *StackTemplateHandler) RemoveOrganizationStackTemplates(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) return } - ErrorJSON(w, r, fmt.Errorf("need implementation")) + input := domain.RemoveOrganizationStackTemplatesRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + err = h.usecase.RemoveOrganizationStackTemplates(r.Context(), organizationId, input.StackTemplateIds) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) } diff --git a/internal/delivery/http/stack.go b/internal/delivery/http/stack.go index 661e38d5..726c3253 100644 --- a/internal/delivery/http/stack.go +++ b/internal/delivery/http/stack.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -15,26 +16,29 @@ import ( ) type StackHandler struct { - usecase usecase.IStackUsecase + usecase usecase.IStackUsecase + usecasePolicy usecase.IPolicyUsecase } -func NewStackHandler(h usecase.IStackUsecase) *StackHandler { +func NewStackHandler(h usecase.Usecase) *StackHandler { return &StackHandler{ - usecase: h, + usecase: h.Stack, + usecasePolicy: h.Policy, } } // CreateStack godoc -// @Tags Stacks -// @Summary Create Stack -// @Description Create Stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.CreateStackRequest true "create cloud setting request" -// @Success 200 {object} domain.CreateStackResponse -// @Router /organizations/{organizationId}/stacks [post] -// @Security JWT +// +// @Tags Stacks +// @Summary Create Stack +// @Description Create Stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.CreateStackRequest true "create cloud setting request" +// @Success 200 {object} domain.CreateStackResponse +// @Router /organizations/{organizationId}/stacks [post] +// @Security JWT func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -50,13 +54,14 @@ func (h *StackHandler) CreateStack(w http.ResponseWriter, r *http.Request) { return } - var dto domain.Stack - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.Stack + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } - if err = serializer.Map(input, &dto.Conf); err != nil { - log.InfoWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), input, &dto.Conf); err != nil { + log.Info(r.Context(), err) } + dto.OrganizationId = organizationId stackId, err := h.usecase.Create(r.Context(), dto) if err != nil { @@ -88,21 +93,21 @@ func (h *StackHandler) InstallStack(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, nil) } -// GetStack godoc -// @Tags Stacks -// @Summary Get Stacks -// @Description Get Stacks -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param combinedFilter query string false "combinedFilter" -// @Success 200 {object} domain.GetStacksResponse -// @Router /organizations/{organizationId}/stacks [get] -// @Security JWT +// GetStacks godoc +// +// @Tags Stacks +// @Summary Get Stacks +// @Description Get Stacks +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param limit query string false "pageSize" +// @Param page query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Success 200 {object} domain.GetStacksResponse +// @Router /organizations/{organizationId}/stacks [get] +// @Security JWT func (h *StackHandler) GetStacks(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -112,11 +117,7 @@ func (h *StackHandler) GetStacks(w http.ResponseWriter, r *http.Request) { } urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } + pg := pagination.NewPagination(&urlParams) stacks, err := h.usecase.Fetch(r.Context(), organizationId, pg) if err != nil { ErrorJSON(w, r, err) @@ -126,35 +127,39 @@ func (h *StackHandler) GetStacks(w http.ResponseWriter, r *http.Request) { var out domain.GetStacksResponse out.Stacks = make([]domain.StackResponse, len(stacks)) for i, stack := range stacks { - if err := serializer.Map(stack, &out.Stacks[i]); err != nil { - log.InfoWithContext(r.Context(), err) - continue + if err := serializer.Map(r.Context(), stack, &out.Stacks[i]); err != nil { + log.Info(r.Context(), err) + } + + if err := serializer.Map(r.Context(), stack.CreatedAt, &out.Stacks[i].CreatedAt); err != nil { + log.Info(r.Context(), err) } err = json.Unmarshal(stack.StackTemplate.Services, &out.Stacks[i].StackTemplate.Services) if err != nil { - log.InfoWithContext(r.Context(), err) + log.Info(r.Context(), err) } } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetStack godoc -// @Tags Stacks -// @Summary Get Stack -// @Description Get Stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Success 200 {object} domain.GetStackResponse -// @Router /organizations/{organizationId}/stacks/{stackId} [get] -// @Security JWT +// +// @Tags Stacks +// @Summary Get Stack +// @Description Get Stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Success 200 {object} domain.GetStackResponse +// @Router /organizations/{organizationId}/stacks/{stackId} [get] +// @Security JWT func (h *StackHandler) GetStack(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["stackId"] @@ -170,29 +175,30 @@ func (h *StackHandler) GetStack(w http.ResponseWriter, r *http.Request) { } var out domain.GetStackResponse - if err := serializer.Map(stack, &out.Stack); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), stack, &out.Stack); err != nil { + log.Info(r.Context(), err) } err = json.Unmarshal(stack.StackTemplate.Services, &out.Stack.StackTemplate.Services) if err != nil { - log.InfoWithContext(r.Context(), err) + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } // GetStackStatus godoc -// @Tags Stacks -// @Summary Get Stack Status -// @Description Get Stack Status -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Success 200 {object} domain.GetStackStatusResponse -// @Router /organizations/{organizationId}/stacks/{stackId}/status [get] -// @Security JWT +// +// @Tags Stacks +// @Summary Get Stack Status +// @Description Get Stack Status +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Success 200 {object} domain.GetStackStatusResponse +// @Router /organizations/{organizationId}/stacks/{stackId}/status [get] +// @Security JWT func (h *StackHandler) GetStackStatus(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -211,8 +217,8 @@ func (h *StackHandler) GetStackStatus(w http.ResponseWriter, r *http.Request) { var out domain.GetStackStatusResponse out.StepStatus = make([]domain.StackStepStatus, len(steps)) for i, step := range steps { - if err := serializer.Map(step, &out.StepStatus[i]); err != nil { - log.InfoWithContext(r.Context(), err) + if err := serializer.Map(r.Context(), step, &out.StepStatus[i]); err != nil { + log.Info(r.Context(), err) } } out.StackStatus = status @@ -221,17 +227,18 @@ func (h *StackHandler) GetStackStatus(w http.ResponseWriter, r *http.Request) { } // UpdateStack godoc -// @Tags Stacks -// @Summary Update Stack -// @Description Update Stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Param body body domain.UpdateStackRequest true "Update cloud setting request" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/stacks/{stackId} [put] -// @Security JWT +// +// @Tags Stacks +// @Summary Update Stack +// @Description Update Stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Param body body domain.UpdateStackRequest true "Update cloud setting request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId} [put] +// @Security JWT func (h *StackHandler) UpdateStack(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["stackId"] @@ -258,9 +265,9 @@ func (h *StackHandler) UpdateStack(w http.ResponseWriter, r *http.Request) { return } - var dto domain.Stack - if err = serializer.Map(input, &dto); err != nil { - log.InfoWithContext(r.Context(), err) + var dto model.Stack + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) } dto.ID = stackId dto.OrganizationId = organizationId @@ -276,16 +283,17 @@ func (h *StackHandler) UpdateStack(w http.ResponseWriter, r *http.Request) { } // DeleteStack godoc -// @Tags Stacks -// @Summary Delete Stack -// @Description Delete Stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/stacks/{stackId} [delete] -// @Security JWT +// +// @Tags Stacks +// @Summary Delete Stack +// @Description Delete Stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId} [delete] +// @Security JWT func (h *StackHandler) DeleteStack(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -300,11 +308,26 @@ func (h *StackHandler) DeleteStack(w http.ResponseWriter, r *http.Request) { return } - var dto domain.Stack + var dto model.Stack dto.ID = domain.StackId(strId) dto.OrganizationId = organizationId - err := h.usecase.Delete(r.Context(), dto) + // Delete Policies + policyIds, err := h.usecasePolicy.GetPolicyIDsByClusterID(r.Context(), domain.ClusterId(dto.ID)) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "S_FAILED_DELETE_POLICIES", "")) + return + } + + if policyIds != nil && len(*policyIds) > 0 { + err = h.usecasePolicy.DeletePoliciesForClusterID(r.Context(), organizationId, domain.ClusterId(dto.ID), *policyIds) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "S_FAILED_DELETE_POLICIES", "")) + return + } + } + + err = h.usecase.Delete(r.Context(), dto) if err != nil { ErrorJSON(w, r, err) return @@ -314,17 +337,18 @@ func (h *StackHandler) DeleteStack(w http.ResponseWriter, r *http.Request) { } // CheckStackName godoc -// @Tags Stacks -// @Summary Check name for stack -// @Description Check name for stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Param name path string true "name" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/stacks/name/{name}/existence [GET] -// @Security JWT +// +// @Tags Stacks +// @Summary Check name for stack +// @Description Check name for stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Param name path string true "name" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/name/{name}/existence [GET] +// @Security JWT func (h *StackHandler) CheckStackName(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -357,16 +381,17 @@ func (h *StackHandler) CheckStackName(w http.ResponseWriter, r *http.Request) { } // GetStackKubeConfig godoc -// @Tags Stacks -// @Summary Get KubeConfig by stack -// @Description Get KubeConfig by stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "organizationId" -// @Success 200 {object} domain.GetStackKubeConfigResponse -// @Router /organizations/{organizationId}/stacks/{stackId}/kube-config [get] -// @Security JWT +// +// @Tags Stacks +// @Summary Get KubeConfig by stack +// @Description Get KubeConfig by stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "organizationId" +// @Success 200 {object} domain.GetStackKubeConfigResponse +// @Router /organizations/{organizationId}/stacks/{stackId}/kube-config [get] +// @Security JWT func (h *StackHandler) GetStackKubeConfig(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) _, ok := vars["organizationId"] @@ -400,16 +425,17 @@ func (h *StackHandler) GetStackKubeConfig(w http.ResponseWriter, r *http.Request } // SetFavorite godoc -// @Tags Stacks -// @Summary Set favorite stack -// @Description Set favorite stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/stacks/{stackId}/favorite [post] -// @Security JWT +// +// @Tags Stacks +// @Summary Set favorite stack +// @Description Set favorite stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId}/favorite [post] +// @Security JWT func (h *StackHandler) SetFavorite(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["stackId"] @@ -427,16 +453,17 @@ func (h *StackHandler) SetFavorite(w http.ResponseWriter, r *http.Request) { } // DeleteFavorite godoc -// @Tags Stacks -// @Summary Delete favorite stack -// @Description Delete favorite stack -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param stackId path string true "stackId" -// @Success 200 {object} nil -// @Router /organizations/{organizationId}/stacks/{stackId}/favorite [delete] -// @Security JWT +// +// @Tags Stacks +// @Summary Delete favorite stack +// @Description Delete favorite stack +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param stackId path string true "stackId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/stacks/{stackId}/favorite [delete] +// @Security JWT func (h *StackHandler) DeleteFavorite(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) strId, ok := vars["stackId"] diff --git a/internal/delivery/http/system-notification-rule.go b/internal/delivery/http/system-notification-rule.go new file mode 100644 index 00000000..8c815cec --- /dev/null +++ b/internal/delivery/http/system-notification-rule.go @@ -0,0 +1,361 @@ +package http + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type SystemNotificationRuleHandler struct { + usecase usecase.ISystemNotificationRuleUsecase +} + +func NewSystemNotificationRuleHandler(h usecase.Usecase) *SystemNotificationRuleHandler { + return &SystemNotificationRuleHandler{ + usecase: h.SystemNotificationRule, + } +} + +// CreateSystemNotificationRule godoc +// +// @Tags SystemNotificationRules +// @Summary Create SystemNotificationRule +// @Description Create SystemNotificationRule +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.CreateSystemNotificationRuleRequest true "create stack template request" +// @Success 200 {object} domain.CreateSystemNotificationRuleResponse +// @Router /organizations/{organizationId}/system-notification-rules [post] +// @Security JWT +func (h *SystemNotificationRuleHandler) CreateSystemNotificationRule(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.CreateSystemNotificationRuleRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.SystemNotificationRule + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.OrganizationId = organizationId + + if !dto.SystemNotificationCondition.EnablePortal { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid EnablePortal"), "SNR_INVALID_ENABLE_PORTAL", "")) + return + } + + id, err := h.usecase.Create(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + out := domain.CreateSystemNotificationRuleResponse{ + ID: id.String(), + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetSystemNotificationRule godoc +// +// @Tags SystemNotificationRules +// @Summary Get SystemNotificationRules +// @Description Get SystemNotificationRules +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetSystemNotificationRulesResponse +// @Router /organizations/{organizationId}/system-notification-rules [get] +// @Security JWT +func (h *SystemNotificationRuleHandler) GetSystemNotificationRules(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.SystemNotificationRuleStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } + } + + systemNotificationRules, err := h.usecase.Fetch(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationRulesResponse + out.SystemNotificationRules = make([]domain.SystemNotificationRuleResponse, len(systemNotificationRules)) + for i, systemNotificationRule := range systemNotificationRules { + if err := serializer.Map(r.Context(), systemNotificationRule, &out.SystemNotificationRules[i]); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationRules[i].TargetUsers = make([]domain.SimpleUserResponse, len(systemNotificationRule.TargetUsers)) + for j, targetUser := range systemNotificationRule.TargetUsers { + if err := serializer.Map(r.Context(), targetUser, &out.SystemNotificationRules[i].TargetUsers[j]); err != nil { + log.Info(r.Context(), err) + } + } + + err = json.Unmarshal(systemNotificationRule.SystemNotificationCondition.Parameter, &out.SystemNotificationRules[i].SystemNotificationCondition.Parameters) + if err != nil { + log.Error(r.Context(), err) + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetSystemNotificationRule godoc +// +// @Tags SystemNotificationRules +// @Summary Get SystemNotificationRule +// @Description Get SystemNotificationRule +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationRuleId path string true "systemNotificationRuleId" +// @Success 200 {object} domain.GetSystemNotificationRuleResponse +// @Router /organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId} [get] +// @Security JWT +func (h *SystemNotificationRuleHandler) GetSystemNotificationRule(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationRuleId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationRuleId"), "C_INVALID_SYSTEM_NOTIFICATION_RULE_ID", "")) + return + } + + systemNotificationRuleId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_RULE_ID", "")) + return + } + + systemNotificationRule, err := h.usecase.Get(r.Context(), systemNotificationRuleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationRuleResponse + if err := serializer.Map(r.Context(), systemNotificationRule, &out.SystemNotificationRule); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationRule.TargetUsers = make([]domain.SimpleUserResponse, len(systemNotificationRule.TargetUsers)) + for i, targetUser := range systemNotificationRule.TargetUsers { + if err := serializer.Map(r.Context(), targetUser, &out.SystemNotificationRule.TargetUsers[i]); err != nil { + log.Info(r.Context(), err) + } + } + + err = json.Unmarshal(systemNotificationRule.SystemNotificationCondition.Parameter, &out.SystemNotificationRule.SystemNotificationCondition.Parameters) + if err != nil { + log.Error(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateSystemNotificationRule godoc +// +// @Tags SystemNotificationRules +// @Summary Update SystemNotificationRule +// @Description Update SystemNotificationRule +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationRuleId path string true "systemNotificationRuleId" +// @Param body body domain.UpdateSystemNotificationRuleRequest true "Update systemNotificationRule request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId} [put] +// @Security JWT +func (h *SystemNotificationRuleHandler) UpdateSystemNotificationRule(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + strId, ok := vars["systemNotificationRuleId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationRuleId"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + systemNotificationRuleId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_STACK_TEMPLATE_ID", "")) + return + } + + input := domain.UpdateSystemNotificationRuleRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.SystemNotificationRule + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.OrganizationId = organizationId + dto.ID = systemNotificationRuleId + + if !dto.SystemNotificationCondition.EnablePortal { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid EnablePortal"), "SNR_INVALID_ENABLE_PORTAL", "")) + return + } + err = h.usecase.Update(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeleteSystemNotificationRule godoc +// +// @Tags SystemNotificationRules +// @Summary Delete SystemNotificationRule +// @Description Delete SystemNotificationRule +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationRuleId path string true "systemNotificationRuleId" +// @Success 200 {object} domain.DeleteSystemNotificationRuleResponse +// @Router /organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId} [delete] +// @Security JWT +func (h *SystemNotificationRuleHandler) DeleteSystemNotificationRule(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationRuleId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationRuleId"), "C_INVALID_SYSTEM_NOTIFICATION_RULE_ID", "")) + return + } + systemNotificationRuleId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_RULE_ID", "")) + return + } + + systemNotificationRule, err := h.usecase.Delete(r.Context(), systemNotificationRuleId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + out := domain.DeleteSystemNotificationRuleResponse{ + ID: strId, + Name: systemNotificationRule.Name, + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// CheckSystemNotificationRuleName godoc +// +// @Tags SystemNotificationRules +// @Summary Check name for systemNotificationRule +// @Description Check name for systemNotificationRule +// @Accept json +// @Produce json +// @Param name path string true "name" +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.CheckSystemNotificationRuleNameResponse +// @Router /organizations/{organizationId}/system-notification-rules/name/{name}/existence [GET] +// @Security JWT +func (h *SystemNotificationRuleHandler) CheckSystemNotificationRuleName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid name"), "SNR_INVALID_STACK_TEMAPLTE_NAME", "")) + return + } + + exist := true + _, err := h.usecase.GetByName(r.Context(), name) + if err != nil { + if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { + exist = false + } else { + ErrorJSON(w, r, err) + return + } + } + + var out domain.CheckSystemNotificationRuleNameResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} + +// MakeDefaultSystemNotificationRules godoc +// +// @Tags SystemNotificationRules +// @Summary MakeDefaultSystemNotificationRules +// @Description MakeDefaultSystemNotificationRules +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notification-rules/default-system-rules [POST] +// @Security JWT +func (h *SystemNotificationRuleHandler) MakeDefaultSystemNotificationRules(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + err := h.usecase.MakeDefaultSystemNotificationRules(r.Context(), organizationId, nil) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} diff --git a/internal/delivery/http/system-notification-template.go b/internal/delivery/http/system-notification-template.go new file mode 100644 index 00000000..3597a4bf --- /dev/null +++ b/internal/delivery/http/system-notification-template.go @@ -0,0 +1,485 @@ +package http + +import ( + "fmt" + "net/http" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type SystemNotificationTemplateHandler struct { + usecase usecase.ISystemNotificationTemplateUsecase +} + +func NewSystemNotificationTemplateHandler(h usecase.Usecase) *SystemNotificationTemplateHandler { + return &SystemNotificationTemplateHandler{ + usecase: h.SystemNotificationTemplate, + } +} + +// CreateSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Create alert template. ADMIN ONLY +// @Description Create alert template. ADMIN ONLY +// @Accept json +// @Produce json +// @Success 200 {object} domain.CreateSystemNotificationTemplateRequest +// @Router /admin/system-notification-templates [post] +// @Security JWT +func (h *SystemNotificationTemplateHandler) CreateSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) { + input := domain.CreateSystemNotificationTemplateRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var dto model.SystemNotificationTemplate + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.MetricParameters = make([]model.SystemNotificationMetricParameter, len(input.MetricParameters)) + for i, metricParameter := range input.MetricParameters { + if err := serializer.Map(r.Context(), metricParameter, &dto.MetricParameters[i]); err != nil { + log.Info(r.Context(), err) + } + } + + id, err := h.usecase.Create(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + out := domain.CreateSystemNotificationTemplateResponse{ + ID: id.String(), + } + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Get SystemNotificationTemplates +// @Description Get SystemNotificationTemplates +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetSystemNotificationTemplatesResponse +// @Router /admin/system-notification-templates [get] +// @Security JWT +func (h *SystemNotificationTemplateHandler) GetSystemNotificationTemplates(w http.ResponseWriter, r *http.Request) { + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + systemNotificationTemplates, err := h.usecase.Fetch(r.Context(), pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationTemplatesResponse + out.SystemNotificationTemplates = make([]domain.SystemNotificationTemplateResponse, len(systemNotificationTemplates)) + for i, systemNotificationTemplate := range systemNotificationTemplates { + if err := serializer.Map(r.Context(), systemNotificationTemplate, &out.SystemNotificationTemplates[i]); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationTemplates[i].Organizations = make([]domain.SimpleOrganizationResponse, len(systemNotificationTemplate.Organizations)) + for j, organization := range systemNotificationTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.SystemNotificationTemplates[i].Organizations[j]); err != nil { + log.Info(r.Context(), err) + } + } + + out.SystemNotificationTemplates[i].MetricParameters = make([]domain.SystemNotificationMetricParameterResponse, len(systemNotificationTemplate.MetricParameters)) + for j, metricParameters := range systemNotificationTemplate.MetricParameters { + if err := serializer.Map(r.Context(), metricParameters, &out.SystemNotificationTemplates[i].MetricParameters[j]); err != nil { + log.Info(r.Context(), err) + } + } + + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Get SystemNotificationTemplate +// @Description Get SystemNotificationTemplate +// @Accept json +// @Produce json +// @Param systemNotificationTemplateId path string true "systemNotificationTemplateId" +// @Success 200 {object} domain.GetSystemNotificationTemplateResponse +// @Router /admin/system-notification-templates/{systemNotificationTemplateId} [get] +// @Security JWT +func (h *SystemNotificationTemplateHandler) GetSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationTemplateId"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + systemNotificationTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + systemNotificationTemplate, err := h.usecase.Get(r.Context(), systemNotificationTemplateId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationTemplateResponse + if err := serializer.Map(r.Context(), systemNotificationTemplate, &out.SystemNotificationTemplate); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationTemplate.Organizations = make([]domain.SimpleOrganizationResponse, len(systemNotificationTemplate.Organizations)) + for i, organization := range systemNotificationTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.SystemNotificationTemplate.Organizations[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + out.SystemNotificationTemplate.MetricParameters = make([]domain.SystemNotificationMetricParameterResponse, len(systemNotificationTemplate.MetricParameters)) + for i, metricParameters := range systemNotificationTemplate.MetricParameters { + if err := serializer.Map(r.Context(), metricParameters, &out.SystemNotificationTemplate.MetricParameters[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Update SystemNotificationTemplate +// @Description Update SystemNotificationTemplate +// @Accept json +// @Produce json +// @Param systemNotificationTemplateId path string true "systemNotificationTemplateId" +// @Param body body domain.UpdateSystemNotificationTemplateRequest true "Update alert template request" +// @Success 200 {object} nil +// @Router /admin/system-notification-templates/{systemNotificationTemplateId} [put] +// @Security JWT +func (h *SystemNotificationTemplateHandler) UpdateSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationTemplateId"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + systemNotificationTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + input := domain.UpdateSystemNotificationTemplateRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + var dto model.SystemNotificationTemplate + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.ID = systemNotificationTemplateId + dto.MetricParameters = make([]model.SystemNotificationMetricParameter, len(input.MetricParameters)) + for i, metricParameter := range input.MetricParameters { + if err := serializer.Map(r.Context(), metricParameter, &dto.MetricParameters[i]); err != nil { + log.Info(r.Context(), err) + } + dto.MetricParameters[i].SystemNotificationTemplateId = systemNotificationTemplateId + } + + err = h.usecase.Update(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// DeleteSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Delete SystemNotificationTemplate +// @Description Delete SystemNotificationTemplate +// @Accept json +// @Produce json +// @Param systemNotificationTemplateId path string true "systemNotificationTemplateId" +// @Success 200 {object} nil +// @Router /admin/system-notification-templates/{systemNotificationTemplateId} [delete] +// @Security JWT +func (h *SystemNotificationTemplateHandler) DeleteSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationTemplateId"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + systemNotificationTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + err = h.usecase.Delete(r.Context(), systemNotificationTemplateId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetOrganizationSystemNotificationTemplate godoc +// +// @Tags SystemNotificationTemplates +// @Summary Get Organization SystemNotificationTemplate +// @Description Get Organization SystemNotificationTemplate +// @Accept json +// @Produce json +// @Param systemNotificationTemplateId path string true "systemNotificationTemplateId" +// @Success 200 {object} domain.GetSystemNotificationTemplateResponse +// @Router /organizations/{organizationId}/system-notification-templates/{systemNotificationTemplateId} [get] +// @Security JWT +func (h *SystemNotificationTemplateHandler) GetOrganizationSystemNotificationTemplate(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + strId, ok := vars["systemNotificationTemplateId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("invalid systemNotificationTemplateId"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + systemNotificationTemplateId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID", "")) + return + } + + systemNotificationTemplate, err := h.usecase.Get(r.Context(), systemNotificationTemplateId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationTemplateResponse + if err := serializer.Map(r.Context(), systemNotificationTemplate, &out.SystemNotificationTemplate); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationTemplate.Organizations = make([]domain.SimpleOrganizationResponse, len(systemNotificationTemplate.Organizations)) + for i, organization := range systemNotificationTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.SystemNotificationTemplate.Organizations[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + out.SystemNotificationTemplate.MetricParameters = make([]domain.SystemNotificationMetricParameterResponse, len(systemNotificationTemplate.MetricParameters)) + for i, metricParameters := range systemNotificationTemplate.MetricParameters { + if err := serializer.Map(r.Context(), metricParameters, &out.SystemNotificationTemplate.MetricParameters[i]); err != nil { + log.Info(r.Context(), err) + continue + } + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetOrganizationSystemNotificationTemplates godoc +// +// @Tags SystemNotificationTemplates +// @Summary Get Organization SystemNotificationTemplates +// @Description Get Organization SystemNotificationTemplates +// @Accept json +// @Produce json +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetSystemNotificationTemplatesResponse +// @Router /organizations/{organizationId}/system-notification-templates [get] +// @Security JWT +func (h *SystemNotificationTemplateHandler) GetOrganizationSystemNotificationTemplates(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + systemNotificationTemplates, err := h.usecase.FetchWithOrganization(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationTemplatesResponse + out.SystemNotificationTemplates = make([]domain.SystemNotificationTemplateResponse, len(systemNotificationTemplates)) + for i, systemNotificationTemplate := range systemNotificationTemplates { + if err := serializer.Map(r.Context(), systemNotificationTemplate, &out.SystemNotificationTemplates[i]); err != nil { + log.Info(r.Context(), err) + } + + out.SystemNotificationTemplates[i].Organizations = make([]domain.SimpleOrganizationResponse, len(systemNotificationTemplate.Organizations)) + for j, organization := range systemNotificationTemplate.Organizations { + if err := serializer.Map(r.Context(), organization, &out.SystemNotificationTemplates[i].Organizations[j]); err != nil { + log.Info(r.Context(), err) + } + } + + out.SystemNotificationTemplates[i].MetricParameters = make([]domain.SystemNotificationMetricParameterResponse, len(systemNotificationTemplate.MetricParameters)) + for k, metricParameters := range systemNotificationTemplate.MetricParameters { + if err := serializer.Map(r.Context(), metricParameters, &out.SystemNotificationTemplates[i].MetricParameters[k]); err != nil { + log.Info(r.Context(), err) + } + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// AddOrganizationSystemNotificationTemplates godoc +// +// @Tags SystemNotificationTemplates +// @Summary Add organization systemNotificationTemplates +// @Description Add organization systemNotificationTemplates +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.AddOrganizationSystemNotificationTemplatesRequest true "Add organization systemNotification templates request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notification-templates [post] +// @Security JWT +func (h *SystemNotificationTemplateHandler) AddOrganizationSystemNotificationTemplates(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.AddOrganizationSystemNotificationTemplatesRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + err = h.usecase.AddOrganizationSystemNotificationTemplates(r.Context(), organizationId, input.SystemNotificationTemplateIds) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// RemoveOrganizationSystemNotificationTemplates godoc +// +// @Tags SystemNotificationTemplates +// @Summary Remove organization systemNotificationTemplates +// @Description Remove organization systemNotificationTemplates +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.RemoveOrganizationSystemNotificationTemplatesRequest true "Remove organization systemNotification templates request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notification-templates [put] +// @Security JWT +func (h *SystemNotificationTemplateHandler) RemoveOrganizationSystemNotificationTemplates(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "")) + return + } + + input := domain.RemoveOrganizationSystemNotificationTemplatesRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + err = h.usecase.RemoveOrganizationSystemNotificationTemplates(r.Context(), organizationId, input.SystemNotificationTemplateIds) + if err != nil { + ErrorJSON(w, r, err) + return + } + ResponseJSON(w, r, http.StatusOK, nil) +} + +// CheckSystemNotificationTemplateName godoc +// +// @Tags SystemNotificationTemplates +// @Summary Check name for systemNotificationTemplate +// @Description Check name for systemNotificationTemplate +// @Accept json +// @Produce json +// @Param name path string true "name" +// @Success 200 {object} domain.CheckSystemNotificaionTemplateNameResponse +// @Router /admin/system-notification-templates/name/{name}/existence [GET] +// @Security JWT +func (h *SystemNotificationTemplateHandler) CheckSystemNotificationTemplateName(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name, ok := vars["name"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid name"), "ST_INVALID_SYSTEM_NOTIFICATION_TEMAPLTE_NAME", "")) + return + } + + exist := true + _, err := h.usecase.GetByName(r.Context(), name) + if err != nil { + if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { + exist = false + } else { + ErrorJSON(w, r, err) + return + } + } + + var out domain.CheckSystemNotificaionTemplateNameResponse + out.Existed = exist + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/system-notification.go b/internal/delivery/http/system-notification.go new file mode 100644 index 00000000..150af690 --- /dev/null +++ b/internal/delivery/http/system-notification.go @@ -0,0 +1,285 @@ +package http + +import ( + "bytes" + "fmt" + "io" + "net/http" + "strconv" + + "github.com/google/uuid" + "github.com/gorilla/mux" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/internal/usecase" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" +) + +type SystemNotificationHandler struct { + usecase usecase.ISystemNotificationUsecase +} + +func NewSystemNotificationHandler(h usecase.Usecase) *SystemNotificationHandler { + return &SystemNotificationHandler{ + usecase: h.SystemNotification, + } +} + +// CreateSystemNotification godoc +// +// @Tags SystemNotifications +// @Summary Create systemNotification. ADMIN ONLY +// @Description Create systemNotification. ADMIN ONLY +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} nil +// @Router /system-api/organizations/{organizationId}/system-notifications [post] +// @Security JWT +func (h *SystemNotificationHandler) CreateSystemNotification(w http.ResponseWriter, r *http.Request) { + + /* + INFO[2023-04-26 18:14:11] body : {"receiver":"webhook-systemNotification","status":"firing","systemNotifications":[{"status":"firing","labels":{"systemNotificationname":"TestSystemNotification1"},"annotations":{},"startsAt":"2023-04-26T09:14:01.489894015Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":"0dafe30dffce9487"}],"groupLabels":{"systemNotificationname":"TestSystemNotification1"},"commonLabels":{"systemNotificationname":"TestSystemNotification1"},"commonAnnotations":{},"externalURL":"http://lma-systemNotificationmanager.lma:9093","version":"4","groupKey":"{}:{systemNotificationname=\"TestSystemNotification1\"}","truncatedSystemNotifications":0} + INFO[2023-04-26 18:14:11] {"receiver":"webhook-systemNotification","status":"firing","systemNotifications":[{"status":"firing","labels":{"systemNotificationname":"TestSystemNotification1"},"annotations":{},"startsAt":"2023-04-26T09:14:01.489894015Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":"0dafe30dffce9487"}],"groupLabels":{"systemNotificationname":"TestSystemNotification1"},"commonLabels":{"systemNotificationname":"TestSystemNotification1"},"commonAnnotations":{},"externalURL":"http://lma-systemNotificationmanager.lma:9093","version":"4","groupKey":"{}:{systemNotificationname=\"TestSystemNotification1\"}","truncatedSystemNotifications":0} + */ + + // webhook 으로 부터 받은 body parse + bodyBytes, err := io.ReadAll(r.Body) + if err != nil { + log.Error(r.Context(), err) + } + r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + bodyString := string(bodyBytes) + log.Info(r.Context(), bodyString) + + // 외부로부터(systemNotification manager) 오는 데이터이므로, dto 변환없이 by-pass 처리한다. + input := domain.CreateSystemNotificationRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + err = h.usecase.Create(r.Context(), input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// GetSystemNotification godoc +// +// @Tags SystemNotifications +// @Summary Get SystemNotifications +// @Description Get SystemNotifications +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} domain.GetSystemNotificationsResponse +// @Router /organizations/{organizationId}/system-notifications [get] +// @Security JWT +func (h *SystemNotificationHandler) GetSystemNotifications(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "")) + return + } + + urlParams := r.URL.Query() + pg := pagination.NewPagination(&urlParams) + for i, filter := range pg.GetFilters() { + if filter.Column == "status" { + for j, value := range filter.Values { + var s domain.SystemNotificationActionStatus + pg.GetFilters()[i].Values[j] = strconv.Itoa(int(s.FromString(value))) + } + } + } + + systemNotifications, err := h.usecase.FetchSystemNotifications(r.Context(), organizationId, pg) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationsResponse + out.SystemNotifications = make([]domain.SystemNotificationResponse, len(systemNotifications)) + for i, systemNotification := range systemNotifications { + if err := serializer.Map(r.Context(), systemNotification, &out.SystemNotifications[i]); err != nil { + log.Info(r.Context(), err) + } + + outSystemNotificationActions := make([]domain.SystemNotificationActionResponse, len(systemNotification.SystemNotificationActions)) + for j, systemNotificationAction := range systemNotification.SystemNotificationActions { + if err := serializer.Map(r.Context(), systemNotificationAction, &outSystemNotificationActions[j]); err != nil { + log.Info(r.Context(), err) + } + } + out.SystemNotifications[i].SystemNotificationActions = outSystemNotificationActions + if len(outSystemNotificationActions) > 0 { + out.SystemNotifications[i].LastTaker = outSystemNotificationActions[0].Taker + } + } + + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// GetSystemNotification godoc +// +// @Tags SystemNotifications +// @Summary Get SystemNotification +// @Description Get SystemNotification +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationId path string true "systemNotificationId" +// @Success 200 {object} domain.GetSystemNotificationResponse +// @Router /organizations/{organizationId}/system-notifications/{systemNotificationId} [get] +// @Security JWT +func (h *SystemNotificationHandler) GetSystemNotification(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid systemNotificationId"), "", "")) + return + } + + systemNotificationId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) + return + } + + systemNotification, err := h.usecase.Get(r.Context(), systemNotificationId) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.GetSystemNotificationResponse + if err := serializer.Map(r.Context(), systemNotification, &out.SystemNotification); err != nil { + log.Info(r.Context(), err) + } + outSystemNotificationActions := make([]domain.SystemNotificationActionResponse, len(systemNotification.SystemNotificationActions)) + for j, systemNotificationAction := range systemNotification.SystemNotificationActions { + if err := serializer.Map(r.Context(), systemNotificationAction, &outSystemNotificationActions[j]); err != nil { + log.Info(r.Context(), err) + continue + } + } + out.SystemNotification.SystemNotificationActions = outSystemNotificationActions + + ResponseJSON(w, r, http.StatusOK, out) +} + +// UpdateSystemNotification godoc +// +// @Tags SystemNotifications +// @Summary Update SystemNotification +// @Description Update SystemNotification +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationId path string true "systemNotificationId" +// @Param body body domain.UpdateSystemNotificationRequest true "Update cloud setting request" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notifications/{systemNotificationId} [put] +// @Security JWT +func (h *SystemNotificationHandler) UpdateSystemNotification(w http.ResponseWriter, r *http.Request) { + ErrorJSON(w, r, fmt.Errorf("Need implementation")) +} + +// DeleteSystemNotification godoc +// +// @Tags SystemNotifications +// @Summary Delete SystemNotification +// @Description Delete SystemNotification +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationId path string true "systemNotificationId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notifications/{systemNotificationId} [delete] +// @Security JWT +func (h *SystemNotificationHandler) DeleteSystemNotification(w http.ResponseWriter, r *http.Request) { + ErrorJSON(w, r, fmt.Errorf("Need implementation")) +} + +func (h *SystemNotificationHandler) SystemNotificationTest(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + ErrorJSON(w, r, err) + return + } + + log.Info(r.Context(), "TEST ", body) +} + +// CreateSystemNotificationAction godoc +// +// @Tags SystemNotifications +// @Summary Create systemNotification action +// @Description Create systemNotification action +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param systemNotificationId path string true "systemNotificationId" +// @Success 200 {object} nil +// @Router /organizations/{organizationId}/system-notifications/{systemNotificationId}/actions [post] +// @Security JWT +func (h *SystemNotificationHandler) CreateSystemNotificationAction(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + strId, ok := vars["systemNotificationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("Invalid systemNotificationId"), "", "")) + return + } + + systemNotificationId, err := uuid.Parse(strId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to parse uuid %s"), "", "")) + return + } + + input := domain.CreateSystemNotificationActionRequest{} + err = UnmarshalRequestInput(r, &input) + if err != nil { + ErrorJSON(w, r, err) + return + } + + log.Info(r.Context(), "systemNotification : ", helper.ModelToJson(input)) + + var dto model.SystemNotificationAction + if err = serializer.Map(r.Context(), input, &dto); err != nil { + log.Info(r.Context(), err) + } + dto.SystemNotificationId = systemNotificationId + + systemNotificationAction, err := h.usecase.CreateSystemNotificationAction(r.Context(), dto) + if err != nil { + ErrorJSON(w, r, err) + return + } + + var out domain.CreateSystemNotificationActionResponse + out.ID = systemNotificationAction.String() + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/delivery/http/user.go b/internal/delivery/http/user.go index 50a7ea11..d106b236 100644 --- a/internal/delivery/http/user.go +++ b/internal/delivery/http/user.go @@ -1,12 +1,14 @@ package http import ( + "context" "fmt" "net/http" "strings" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/internal/usecase" @@ -21,6 +23,7 @@ type IUserHandler interface { Get(w http.ResponseWriter, r *http.Request) Delete(w http.ResponseWriter, r *http.Request) Update(w http.ResponseWriter, r *http.Request) + UpdateUsers(w http.ResponseWriter, r *http.Request) ResetPassword(w http.ResponseWriter, r *http.Request) GetMyProfile(w http.ResponseWriter, r *http.Request) @@ -31,29 +34,42 @@ type IUserHandler interface { CheckId(w http.ResponseWriter, r *http.Request) CheckEmail(w http.ResponseWriter, r *http.Request) + GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) + + // Admin + Admin_Create(w http.ResponseWriter, r *http.Request) + Admin_Delete(w http.ResponseWriter, r *http.Request) + Admin_Update(w http.ResponseWriter, r *http.Request) } type UserHandler struct { - usecase usecase.IUserUsecase + usecase usecase.IUserUsecase + authUsecase usecase.IAuthUsecase + roleUsecase usecase.IRoleUsecase + permissionUsecase usecase.IPermissionUsecase } -func NewUserHandler(h usecase.IUserUsecase) IUserHandler { +func NewUserHandler(h usecase.Usecase) IUserHandler { return &UserHandler{ - usecase: h, + usecase: h.User, + authUsecase: h.Auth, + roleUsecase: h.Role, + permissionUsecase: h.Permission, } } // Create godoc -// @Tags Users -// @Summary Create user -// @Description Create user -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.CreateUserRequest true "create user request" -// @Success 200 {object} domain.CreateUserResponse "create user response" -// @Router /organizations/{organizationId}/users [post] -// @Security JWT +// +// @Tags Users +// @Summary Create user +// @Description Create user +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.CreateUserRequest true "create user request" +// @Success 200 {object} domain.CreateUserResponse "create user response" +// @Router /organizations/{organizationId}/users [post] +// @Security JWT func (u UserHandler) Create(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -65,30 +81,33 @@ func (u UserHandler) Create(w http.ResponseWriter, r *http.Request) { input := domain.CreateUserRequest{} err := UnmarshalRequestInput(r, &input) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } - //userInfo, ok := request.UserFrom(r.Context()) - //if !ok { - // ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("user info not found in token"))) - // return - //} - ctx := r.Context() - var user domain.User - if err = serializer.Map(input, &user); err != nil { - log.ErrorWithContext(r.Context(), err) + var user model.User + if err = serializer.Map(r.Context(), input, &user); err != nil { + log.Error(r.Context(), err) } - user.Organization = domain.Organization{ + user.Organization = model.Organization{ ID: organizationId, } + for _, role := range input.Roles { + v, err := u.roleUsecase.GetTksRole(ctx, organizationId, *role.ID) + if err != nil { + ErrorJSON(w, r, err) + return + } + + user.Roles = append(user.Roles, *v) + } resUser, err := u.usecase.Create(ctx, &user) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) if _, status := httpErrors.ErrorResponse(err); status == http.StatusConflict { ErrorJSON(w, r, httpErrors.NewConflictError(err, "", "")) return @@ -99,25 +118,28 @@ func (u UserHandler) Create(w http.ResponseWriter, r *http.Request) { } var out domain.CreateUserResponse - if err = serializer.Map(*resUser, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), *resUser, &out.User); err != nil { + log.Error(r.Context(), err) } + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) + ResponseJSON(w, r, http.StatusCreated, out) } // Get godoc -// @Tags Users -// @Summary Get user detail -// @Description Get user detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "accountId" -// @Success 200 {object} domain.GetUserResponse -// @Router /organizations/{organizationId}/users/{accountId} [get] -// @Security JWT +// +// @Tags Users +// @Summary Get user detail +// @Description Get user detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Success 200 {object} domain.GetUserResponse +// @Router /organizations/{organizationId}/users/{accountId} [get] +// @Security JWT func (u UserHandler) Get(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) userId, ok := vars["accountId"] @@ -133,7 +155,7 @@ func (u UserHandler) Get(w http.ResponseWriter, r *http.Request) { user, err := u.usecase.GetByAccountId(r.Context(), userId, organizationId) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) @@ -145,28 +167,31 @@ func (u UserHandler) Get(w http.ResponseWriter, r *http.Request) { } var out domain.GetUserResponse - if err = serializer.Map(*user, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), *user, &out.User); err != nil { + log.Error(r.Context(), err) } + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) + ResponseJSON(w, r, http.StatusOK, out) } // List godoc -// @Tags Users -// @Summary Get user list -// @Description Get user list -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param limit query string false "pageSize" -// @Param page query string false "pageNumber" -// @Param soertColumn query string false "sortColumn" -// @Param sortOrder query string false "sortOrder" -// @Param filters query []string false "filters" -// @Success 200 {object} []domain.ListUserBody -// @Router /organizations/{organizationId}/users [get] -// @Security JWT +// +// @Tags Users +// @Summary Get user list +// @Description Get user list +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param pageSize query string false "pageSize" +// @Param pageNumber query string false "pageNumber" +// @Param soertColumn query string false "sortColumn" +// @Param sortOrder query string false "sortOrder" +// @Param filters query []string false "filters" +// @Success 200 {object} []domain.ListUserBody +// @Router /organizations/{organizationId}/users [get] +// @Security JWT func (u UserHandler) List(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) organizationId, ok := vars["organizationId"] @@ -176,14 +201,10 @@ func (u UserHandler) List(w http.ResponseWriter, r *http.Request) { } urlParams := r.URL.Query() - pg, err := pagination.NewPagination(&urlParams) - if err != nil { - ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) - return - } + pg := pagination.NewPagination(&urlParams) users, err := u.usecase.ListWithPagination(r.Context(), organizationId, pg) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } @@ -191,29 +212,46 @@ func (u UserHandler) List(w http.ResponseWriter, r *http.Request) { var out domain.ListUserResponse out.Users = make([]domain.ListUserBody, len(*users)) for i, user := range *users { - if err = serializer.Map(user, &out.Users[i]); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), user, &out.Users[i]); err != nil { + log.Error(r.Context(), err) + } + + if out.Users[i].Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles); err != nil { + log.Error(r.Context(), err) } } - if err := serializer.Map(*pg, &out.Pagination); err != nil { - log.InfoWithContext(r.Context(), err) + if out.Pagination, err = pg.Response(r.Context()); err != nil { + log.Info(r.Context(), err) } ResponseJSON(w, r, http.StatusOK, out) } +func (u UserHandler) convertUserRolesToSimpleRoleResponse(roles []model.Role) []domain.SimpleRoleResponse { + var simpleRoles []domain.SimpleRoleResponse + for _, role := range roles { + simpleRoles = append(simpleRoles, domain.SimpleRoleResponse{ + ID: role.ID, + Name: role.Name, + }) + } + + return simpleRoles +} + // Delete godoc -// @Tags Users -// @Summary Delete user -// @Description Delete user -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "accountId" -// @Success 200 {object} domain.User -// @Router /organizations/{organizationId}/users/{accountId} [delete] -// @Security JWT +// +// @Tags Users +// @Summary Delete user +// @Description Delete user +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Success 200 {object} domain.DeleteUserResponse +// @Router /organizations/{organizationId}/users/{accountId} [delete] +// @Security JWT func (u UserHandler) Delete(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) userId, ok := vars["accountId"] @@ -233,27 +271,31 @@ func (u UserHandler) Delete(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } - ResponseJSON(w, r, http.StatusOK, nil) + out := domain.DeleteUserResponse{ + AccountId: userId, + } + ResponseJSON(w, r, http.StatusOK, out) } // Update godoc -// @Tags Users -// @Summary Update user -// @Description Update user -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "accountId" -// @Param body body domain.UpdateUserRequest true "input" -// @Success 200 {object} domain.UpdateUserResponse -// @Router /organizations/{organizationId}/users/{accountId} [put] -// @Security JWT +// +// @Tags Users +// @Summary Update user +// @Description Update user +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Param body body domain.UpdateUserRequest true "input" +// @Success 200 {object} domain.UpdateUserResponse +// @Router /organizations/{organizationId}/users/{accountId} [put] +// @Security JWT func (u UserHandler) Update(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) accountId, ok := vars["accountId"] @@ -270,25 +312,32 @@ func (u UserHandler) Update(w http.ResponseWriter, r *http.Request) { input := domain.UpdateUserRequest{} err := UnmarshalRequestInput(r, &input) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } ctx := r.Context() - var user domain.User - if err = serializer.Map(input, &user); err != nil { - ErrorJSON(w, r, err) - return + var user model.User + if err = serializer.Map(r.Context(), input, &user); err != nil { + log.Error(r.Context(), err) } - user.Organization = domain.Organization{ + user.Organization = model.Organization{ ID: organizationId, } user.AccountId = accountId - user.Role.Name = input.Role + for _, role := range input.Roles { + v, err := u.roleUsecase.GetTksRole(ctx, organizationId, *role.ID) + if err != nil { + ErrorJSON(w, r, err) + return + } + + user.Roles = append(user.Roles, *v) + } - resUser, err := u.usecase.UpdateByAccountIdByAdmin(ctx, accountId, &user) + resUser, err := u.usecase.UpdateByAccountIdByAdmin(ctx, &user) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) @@ -300,26 +349,96 @@ func (u UserHandler) Update(w http.ResponseWriter, r *http.Request) { } var out domain.UpdateUserResponse - if err = serializer.Map(*resUser, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), *resUser, &out.User); err != nil { + log.Error(r.Context(), err) ErrorJSON(w, r, err) return } + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) ResponseJSON(w, r, http.StatusOK, out) } +// UpdateUsers godoc +// +// @Tags Users +// @Summary Update multiple users +// @Description Update multiple users +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body []domain.UpdateUsersRequest true "input" +// @Success 200 +// @Router /organizations/{organizationId}/users [put] +// @Security JWT +func (u UserHandler) UpdateUsers(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 + } + + input := domain.UpdateUsersRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ctx := r.Context() + + users := make([]model.User, len(input.Users)) + for i, user := range input.Users { + if err = serializer.Map(r.Context(), user, &users[i]); err != nil { + ErrorJSON(w, r, err) + return + } + users[i].Organization = model.Organization{ + ID: organizationId, + } + users[i].AccountId = user.AccountId + + for _, role := range user.Roles { + v, err := u.roleUsecase.GetTksRole(ctx, organizationId, *role.ID) + if err != nil { + ErrorJSON(w, r, err) + return + } + + users[i].Roles = append(users[i].Roles, *v) + } + + //ToDo: Implement transaction + _, err := u.usecase.UpdateByAccountIdByAdmin(r.Context(), &users[i]) + if err != nil { + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + // ResetPassword godoc -// @Tags Users -// @Summary Reset user's password as temporary password by admin -// @Description Reset user's password as temporary password by admin and send email to user -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "accountId" -// @Success 200 -// @Router /organizations/{organizationId}/users/{accountId}/reset-password [put] -// @Security JWT +// +// @Tags Users +// @Summary Reset user's password as temporary password by admin +// @Description Reset user's password as temporary password by admin and send email to user +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Success 200 +// @Router /organizations/{organizationId}/users/{accountId}/reset-password [put] +// @Security JWT func (u UserHandler) ResetPassword(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) accountId, ok := vars["accountId"] @@ -333,7 +452,7 @@ func (u UserHandler) ResetPassword(w http.ResponseWriter, r *http.Request) { return } - err := u.usecase.ResetPasswordByAccountId(accountId, organizationId) + err := u.usecase.ResetPasswordByAccountId(r.Context(), accountId, organizationId) if err != nil { ErrorJSON(w, r, err) return @@ -343,15 +462,16 @@ func (u UserHandler) ResetPassword(w http.ResponseWriter, r *http.Request) { } // GetMyProfile godoc -// @Tags My-profile -// @Summary Get my profile detail -// @Description Get my profile detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 {object} domain.GetMyProfileResponse -// @Router /organizations/{organizationId}/my-profile [get] -// @Security JWT +// +// @Tags My-profile +// @Summary Get my profile detail +// @Description Get my profile detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 {object} domain.GetMyProfileResponse +// @Router /organizations/{organizationId}/my-profile [get] +// @Security JWT func (u UserHandler) GetMyProfile(w http.ResponseWriter, r *http.Request) { requestUserInfo, ok := request.UserFrom(r.Context()) if !ok { @@ -359,33 +479,42 @@ func (u UserHandler) GetMyProfile(w http.ResponseWriter, r *http.Request) { return } - user, err := u.usecase.Get(requestUserInfo.GetUserId()) + user, err := u.usecase.Get(r.Context(), requestUserInfo.GetUserId()) if err != nil { ErrorJSON(w, r, err) } var out domain.GetMyProfileResponse - if err = serializer.Map(*user, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), *user, &out.User); err != nil { + log.Error(r.Context(), err) ErrorJSON(w, r, err) return } + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) + ResponseJSON(w, r, http.StatusOK, out) } // UpdateMyProfile godoc -// @Tags My-profile -// @Summary Update my profile detail -// @Description Update my profile detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdateMyProfileRequest true "Required fields: password due to double-check" -// @Success 200 {object} domain.UpdateMyProfileResponse -// @Router /organizations/{organizationId}/my-profile [put] -// @Security JWT +// +// @Tags My-profile +// @Summary Update my profile detail +// @Description Update my profile detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.UpdateMyProfileRequest true "Required fields: password due to double-check" +// @Success 200 {object} domain.UpdateMyProfileResponse +// @Router /organizations/{organizationId}/my-profile [put] +// @Security JWT func (u UserHandler) UpdateMyProfile(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + _, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), "C_INVALID_ORGANIZATION_ID", "")) + return + } requestUserInfo, ok := request.UserFrom(r.Context()) if !ok { ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found in request"), "A_INVALID_TOKEN", "")) @@ -395,31 +524,31 @@ func (u UserHandler) UpdateMyProfile(w http.ResponseWriter, r *http.Request) { input := domain.UpdateMyProfileRequest{} err := UnmarshalRequestInput(r, &input) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } - err = u.usecase.ValidateAccount(requestUserInfo.GetUserId(), input.Password, requestUserInfo.GetOrganizationId()) + err = u.usecase.ValidateAccount(r.Context(), requestUserInfo.GetUserId(), input.Password, requestUserInfo.GetOrganizationId()) if err != nil { - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return } ctx := r.Context() - var user domain.User - if err = serializer.Map(input, &user); err != nil { - log.ErrorWithContext(r.Context(), err) + user, err := u.usecase.Get(r.Context(), requestUserInfo.GetUserId()) + if err != nil { ErrorJSON(w, r, err) return } - user.Organization = domain.Organization{ - ID: requestUserInfo.GetOrganizationId(), - } + user.ID = requestUserInfo.GetUserId() + user.Name = input.Name + user.Email = input.Email + user.Department = input.Department - resUser, err := u.usecase.Update(ctx, requestUserInfo.GetUserId(), &user) + resUser, err := u.usecase.Update(ctx, user) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) @@ -430,26 +559,29 @@ func (u UserHandler) UpdateMyProfile(w http.ResponseWriter, r *http.Request) { } var out domain.UpdateMyProfileResponse - if err = serializer.Map(*resUser, &out.User); err != nil { - log.ErrorWithContext(r.Context(), err) + if err = serializer.Map(r.Context(), *resUser, &out.User); err != nil { + log.Error(r.Context(), err) ErrorJSON(w, r, err) return } + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) + ResponseJSON(w, r, http.StatusOK, out) } // UpdateMyPassword godoc -// @Tags My-profile -// @Summary Update user password detail -// @Description Update user password detail -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param body body domain.UpdatePasswordRequest true "update user password request" -// @Success 200 -// @Router /organizations/{organizationId}/my-profile/password [put] -// @Security JWT +// +// @Tags My-profile +// @Summary Update user password detail +// @Description Update user password detail +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.UpdatePasswordRequest true "update user password request" +// @Success 200 +// @Router /organizations/{organizationId}/my-profile/password [put] +// @Security JWT func (u UserHandler) UpdateMyPassword(w http.ResponseWriter, r *http.Request) { requestUserInfo, ok := request.UserFrom(r.Context()) if !ok { @@ -464,7 +596,7 @@ func (u UserHandler) UpdateMyPassword(w http.ResponseWriter, r *http.Request) { return } - user, err := u.usecase.Get(requestUserInfo.GetUserId()) + user, err := u.usecase.Get(r.Context(), requestUserInfo.GetUserId()) if err != nil { ErrorJSON(w, r, err) return @@ -480,7 +612,7 @@ func (u UserHandler) UpdateMyPassword(w http.ResponseWriter, r *http.Request) { ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) return } - log.ErrorfWithContext(r.Context(), "error is :%s(%T)", err.Error(), err) + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) ErrorJSON(w, r, err) return @@ -490,16 +622,17 @@ func (u UserHandler) UpdateMyPassword(w http.ResponseWriter, r *http.Request) { } // RenewPasswordExpiredDate godoc -// @Tags My-profile -// @Summary Update user's password expired date to current date -// @Description Update user's password expired date to current date -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 -// @Failure 400 {object} httpErrors.RestError -// @Router /organizations/{organizationId}/my-profile/next-password-change [put] -// @Security JWT +// +// @Tags My-profile +// @Summary Update user's password expired date to current date +// @Description Update user's password expired date to current date +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 +// @Failure 400 {object} httpErrors.RestError +// @Router /organizations/{organizationId}/my-profile/next-password-change [put] +// @Security JWT func (u UserHandler) RenewPasswordExpiredDate(w http.ResponseWriter, r *http.Request) { requestUserInfo, ok := request.UserFrom(r.Context()) if !ok { @@ -517,23 +650,24 @@ func (u UserHandler) RenewPasswordExpiredDate(w http.ResponseWriter, r *http.Req } // DeleteMyProfile godoc -// @Tags My-profile -// @Summary Delete myProfile -// @Description Delete myProfile -// @Accept json -// @Produce json -// @Param organizationId path string true "organizationId" -// @Success 200 -// @Failure 400 -// @Router /organizations/{organizationId}/my-profile [delete] -// @Security JWT +// +// @Tags My-profile +// @Summary Delete myProfile +// @Description Delete myProfile +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Success 200 +// @Failure 400 +// @Router /organizations/{organizationId}/my-profile [delete] +// @Security JWT func (u UserHandler) DeleteMyProfile(w http.ResponseWriter, r *http.Request) { requestUserInfo, ok := request.UserFrom(r.Context()) if !ok { ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found in request context"), "A_INVALID_TOKEN", "")) return } - if err := u.usecase.Delete(requestUserInfo.GetUserId(), requestUserInfo.GetOrganizationId()); err != nil { + if err := u.usecase.Delete(r.Context(), requestUserInfo.GetUserId(), requestUserInfo.GetOrganizationId()); err != nil { ErrorJSON(w, r, err) return } @@ -542,15 +676,16 @@ func (u UserHandler) DeleteMyProfile(w http.ResponseWriter, r *http.Request) { } // CheckId godoc -// @Tags Users -// @Summary Get user id existence -// @Description return true when accountId exists -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "accountId" -// @Success 200 {object} domain.CheckExistedResponse -// @Router /organizations/{organizationId}/users/account-id/{accountId}/existence [get] -// @Security JWT +// +// @Tags Users +// @Summary Get user id existence +// @Description return true when accountId exists +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/users/account-id/{accountId}/existence [get] +// @Security JWT func (u UserHandler) CheckId(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) accountId, ok := vars["accountId"] @@ -582,15 +717,16 @@ func (u UserHandler) CheckId(w http.ResponseWriter, r *http.Request) { } // CheckEmail godoc -// @Tags Users -// @Summary Get user email existence -// @Description return true when email exists -// @Produce json -// @Param organizationId path string true "organizationId" -// @Param accountId path string true "email" -// @Success 200 {object} domain.CheckExistedResponse -// @Router /organizations/{organizationId}/users/email/{email}/existence [get] -// @Security JWT +// +// @Tags Users +// @Summary Get user email existence +// @Description return true when email exists +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "email" +// @Success 200 {object} domain.CheckExistedResponse +// @Router /organizations/{organizationId}/users/email/{email}/existence [get] +// @Security JWT func (u UserHandler) CheckEmail(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) email, ok := vars["email"] @@ -620,3 +756,332 @@ func (u UserHandler) CheckEmail(w http.ResponseWriter, r *http.Request) { ResponseJSON(w, r, http.StatusOK, out) } + +// GetPermissionsByAccountId godoc +// +// @Tags Users +// @Summary Get Permissions By Account ID +// @Description Get Permissions By Account ID +// @Produce json +// @Param organizationId path string true "Organization ID" +// @Param accountId path string true "Account ID" +// @Success 200 {object} domain.GetUsersPermissionsResponse +// @Router /organizations/{organizationId}/users/{accountId}/permissions [get] +// @Security JWT +func (u UserHandler) GetPermissionsByAccountId(w http.ResponseWriter, r *http.Request) { + var organizationId, accountId string + + vars := mux.Vars(r) + if v, ok := vars["accountId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + accountId = v + } + if v, ok := vars["organizationId"]; !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(nil, "", "")) + return + } else { + organizationId = v + } + + // ToDo: admin portal에 대해서는 현재 permission 관련 로직을 적용하지 않아 임시로 빈 값을 리턴하도록 함 + if organizationId == "master" { + ResponseJSON(w, r, http.StatusOK, domain.GetUsersPermissionsResponse{}) + return + } + + user, err := u.usecase.GetByAccountId(r.Context(), accountId, organizationId) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + + var permissionSets []*model.PermissionSet + for _, role := range user.Roles { + permissionSet, err := u.permissionUsecase.GetPermissionSetByRoleId(r.Context(), role.ID) + if err != nil { + ErrorJSON(w, r, httpErrors.NewInternalServerError(err, "", "")) + return + } + permissionSets = append(permissionSets, permissionSet) + } + + mergedPermissionSet := u.permissionUsecase.MergePermissionWithOrOperator(r.Context(), permissionSets...) + + var out domain.GetUsersPermissionsResponse + out.Permissions = make([]*domain.MergePermissionResponse, 0) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Dashboard)) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Stack)) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Policy)) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.ProjectManagement)) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Notification)) + out.Permissions = append(out.Permissions, convertModelToMergedPermissionSetResponse(r.Context(), mergedPermissionSet.Configuration)) + + ResponseJSON(w, r, http.StatusOK, out) +} + +func convertModelToMergedPermissionSetResponse(ctx context.Context, permission *model.Permission) *domain.MergePermissionResponse { + var permissionResponse domain.MergePermissionResponse + + permissionResponse.Key = permission.Key + if permission.IsAllowed != nil { + permissionResponse.IsAllowed = permission.IsAllowed + } + + for _, child := range permission.Children { + permissionResponse.Children = append(permissionResponse.Children, convertModelToMergedPermissionSetResponse(ctx, child)) + } + + return &permissionResponse +} + +// Admin_Create godoc +// +// @Tags Users +// @Summary Create user by admin in Admin Portal +// @Description Create user by admin in Admin Portal +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param body body domain.Admin_CreateUserRequest true "create user request" +// @Success 200 {object} domain.Admin_CreateUserResponse "create user response" +// @Router /admin/organizations/{organizationId}/users [post] +// @Security JWT +func (u UserHandler) Admin_Create(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 + } + + input := domain.Admin_CreateUserRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + // TKS 관리자가 아닌 경우 Password 확인 + if organizationId != "master" { + // check admin password + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found in request"), "A_INVALID_TOKEN", "")) + return + } + err = u.usecase.ValidateAccount(r.Context(), requestUserInfo.GetUserId(), input.AdminPassword, requestUserInfo.GetOrganizationId()) + if err != nil { + ErrorJSON(w, r, err) + return + } + } + + user := model.User{ + Name: input.Name, + AccountId: input.AccountId, + Email: input.Email, + Department: input.Department, + Description: input.Description, + } + + for _, role := range input.Roles { + v, err := u.roleUsecase.GetTksRole(r.Context(), organizationId, *role.ID) + if err != nil { + ErrorJSON(w, r, err) + return + } + + user.Roles = append(user.Roles, *v) + } + + user.Organization = model.Organization{ + ID: organizationId, + } + + user.Password = u.usecase.GenerateRandomPassword(r.Context()) + + resUser, err := u.usecase.Create(r.Context(), &user) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + if _, status := httpErrors.ErrorResponse(err); status == http.StatusConflict { + ErrorJSON(w, r, httpErrors.NewConflictError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + err = u.usecase.SendEmailForTemporaryPassword(r.Context(), user.AccountId, organizationId, user.Password) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + ErrorJSON(w, r, err) + return + } + + var out domain.Admin_CreateUserResponse + + out.ID = resUser.ID.String() + ResponseJSON(w, r, http.StatusCreated, out) + +} + +// Admin_Delete godoc +// +// @Tags Users +// @Summary Delete user by admin in Admin Portal +// @Description Delete user by admin in Admin Portal +// @Accept json +// @Produce json +// @Param body body domain.DeleteUserRequest true "input" +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Success 200 +// @Router /admin/organizations/{organizationId}/users/{accountId} [delete] +// @Security JWT +func (u UserHandler) Admin_Delete(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + userId, ok := vars["accountId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("accountId not found in path"), "C_INVALID_ACCOUNT_ID", "")) + return + } + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), "", "")) + return + } + + input := domain.DeleteUserRequest{} + + // TKS 관리자가 아닌 경우 Password 확인 + if organizationId != "master" { + err := UnmarshalRequestInput(r, &input) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + // check admin password + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found in request"), "A_INVALID_TOKEN", "")) + return + } + err = u.usecase.ValidateAccount(r.Context(), requestUserInfo.GetUserId(), input.AdminPassword, requestUserInfo.GetOrganizationId()) + if err != nil { + ErrorJSON(w, r, err) + return + } + } + + err := u.usecase.DeleteByAccountId(r.Context(), userId, organizationId) + if err != nil { + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + ResponseJSON(w, r, http.StatusOK, nil) +} + +// Admin_Update godoc +// +// @Tags Users +// @Summary Update user by admin in Admin Portal +// @Description Update user by admin in Admin Portal +// @Accept json +// @Produce json +// @Param organizationId path string true "organizationId" +// @Param accountId path string true "accountId" +// @Param body body domain.Admin_UpdateUserRequest true "input" +// @Success 200 {object} domain.Admin_UpdateUserResponse +// @Router /admin/organizations/{organizationId}/users/{accountId} [put] +// @Security JWT +func (u UserHandler) Admin_Update(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + accountId, ok := vars["accountId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("accountId not found in path"), "C_INVALID_ACCOUNT_ID", "")) + return + } + organizationId, ok := vars["organizationId"] + if !ok { + ErrorJSON(w, r, httpErrors.NewBadRequestError(fmt.Errorf("organizationId not found in path"), "", "")) + return + } + + input := domain.Admin_UpdateUserRequest{} + err := UnmarshalRequestInput(r, &input) + if err != nil { + log.Errorf(r.Context(), "error is :%s(%T)", err.Error(), err) + + ErrorJSON(w, r, err) + return + } + + // TKS 관리자가 아닌 경우 Password 확인 + if organizationId != "master" { + // check admin password + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found in request"), "A_INVALID_TOKEN", "")) + return + } + err = u.usecase.ValidateAccount(r.Context(), requestUserInfo.GetUserId(), input.AdminPassword, requestUserInfo.GetOrganizationId()) + if err != nil { + ErrorJSON(w, r, err) + return + } + } + + ctx := r.Context() + user := model.User{ + AccountId: accountId, + Name: input.Name, + Email: input.Email, + Department: input.Department, + Description: input.Description, + } + user.Organization = model.Organization{ + ID: organizationId, + } + for _, role := range input.Roles { + user.Roles = append(user.Roles, model.Role{ + ID: *role.ID, + }) + } + + resUser, err := u.usecase.UpdateByAccountIdByAdmin(ctx, &user) + if err != nil { + if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { + ErrorJSON(w, r, httpErrors.NewBadRequestError(err, "", "")) + return + } + + ErrorJSON(w, r, err) + return + } + + var out domain.Admin_UpdateUserResponse + if err = serializer.Map(r.Context(), *resUser, &out.User); err != nil { + log.Error(r.Context(), err) + ErrorJSON(w, r, err) + return + } + + out.User.Roles = u.convertUserRolesToSimpleRoleResponse(user.Roles) + + ResponseJSON(w, r, http.StatusOK, out) +} diff --git a/internal/filter/filter.go b/internal/filter/filter.go new file mode 100644 index 00000000..e8d8cf52 --- /dev/null +++ b/internal/filter/filter.go @@ -0,0 +1,70 @@ +package filter + +import ( + "fmt" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" +) + +type Filter struct { + Field string + Operator *Operator + Args []string + Or bool +} + +func (f *Filter) Scope(settings *Settings, sch *schema.Schema) (func(*gorm.DB) *gorm.DB, func(*gorm.DB) *gorm.DB) { + field, s, joinName := getField(f.Field, sch, &settings.Blacklist) + if field == nil { + return nil, nil + } + + dataType := getDataType(field) + + joinScope := func(tx *gorm.DB) *gorm.DB { + if dataType == DataTypeUnsupported { + return tx + } + if joinName != "" { + if err := tx.Statement.Parse(tx.Statement.Model); err != nil { + _ = tx.AddError(err) + return tx + } + tx = join(tx, joinName, sch) + } + + return tx + } + + computed := field.StructField.Tag.Get("computed") + + conditionScope := func(tx *gorm.DB) *gorm.DB { + if dataType == DataTypeUnsupported { + return tx + } + + table := tx.Statement.Quote(tableFromJoinName(s.Table, joinName)) + var fieldExpr string + if computed != "" { + fieldExpr = fmt.Sprintf("(%s)", strings.ReplaceAll(computed, clause.CurrentTable, table)) + } else { + fieldExpr = table + "." + tx.Statement.Quote(field.DBName) + } + + return f.Operator.Function(tx, f, fieldExpr, dataType) + } + + return joinScope, conditionScope +} + +// Where applies a condition to given transaction, automatically taking the "Or" +// filter value into account. +func (f *Filter) Where(tx *gorm.DB, query string, args ...interface{}) *gorm.DB { + if f.Or { + return tx.Or(query, args...) + } + return tx.Where(query, args...) +} diff --git a/internal/filter/join.go b/internal/filter/join.go new file mode 100644 index 00000000..72240709 --- /dev/null +++ b/internal/filter/join.go @@ -0,0 +1,246 @@ +package filter + +import ( + "bytes" + "fmt" + "regexp" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + "goyave.dev/goyave/v4/util/sliceutil" +) + +var ( + joinRegex = regexp.MustCompile("(?i)((LEFT|RIGHT|FULL)\\s+)?((OUTER|INNER)\\s+)?JOIN\\s+[\"'`]?(?P\\w+)[\"'`]?\\s+((AS\\s+)?[\"'`]?(?P\\w+)[\"'`]?)?\\s*ON") +) + +type Join struct { + selectCache map[string][]string + Relation string + Fields []string +} + +func (j *Join) Scopes(settings *Settings, schema *schema.Schema) []func(*gorm.DB) *gorm.DB { + scopes := j.applyRelation(schema, &settings.Blacklist, j.Relation, 0, make([]func(*gorm.DB) *gorm.DB, 0, strings.Count(j.Relation, ".")+1)) + if scopes != nil { + return scopes + } + return nil +} + +func (j *Join) applyRelation(schema *schema.Schema, blacklist *Blacklist, relationName string, startIndex int, scopes []func(*gorm.DB) *gorm.DB) []func(*gorm.DB) *gorm.DB { + if blacklist != nil && blacklist.IsFinal { + return nil + } + trimmedRelationName := relationName[startIndex:] + i := strings.Index(trimmedRelationName, ".") + if i == -1 { + if blacklist != nil { + if sliceutil.ContainsStr(blacklist.RelationsBlacklist, trimmedRelationName) { + return nil + } + blacklist = blacklist.Relations[trimmedRelationName] + } + + r, ok := schema.Relationships.Relations[trimmedRelationName] + if !ok { + return nil + } + + j.selectCache[relationName] = j.Fields + return append(scopes, joinScope(relationName, r, j.Fields, blacklist)) + } + + if startIndex+i+1 >= len(relationName) { + return nil + } + + name := trimmedRelationName[:i] + var b *Blacklist + if blacklist != nil { + if sliceutil.ContainsStr(blacklist.RelationsBlacklist, name) { + return nil + } + b = blacklist.Relations[name] + } + r, ok := schema.Relationships.Relations[name] + if !ok { + return nil + } + n := relationName[:startIndex+i] + fields := []string{} + if f, ok := j.selectCache[n]; ok { + fields = f + } + scopes = append(scopes, joinScope(n, r, fields, b)) + + return j.applyRelation(r.FieldSchema, b, relationName, startIndex+i+1, scopes) +} + +func joinScope(relationName string, rel *schema.Relationship, fields []string, blacklist *Blacklist) func(*gorm.DB) *gorm.DB { + var columns []*schema.Field + if fields == nil { + columns = getSelectableFields(blacklist, rel.FieldSchema) + } else { + var b []string + if blacklist != nil { + b = blacklist.FieldsBlacklist + } + columns = cleanColumns(rel.FieldSchema, fields, b) + } + + return func(tx *gorm.DB) *gorm.DB { + if rel.FieldSchema.Table == "" { + _ = tx.AddError(fmt.Errorf("Relation %q is anonymous, could not get table name", relationName)) + return tx + } + if columns != nil { + for _, primaryField := range rel.FieldSchema.PrimaryFields { + if !columnsContain(columns, primaryField) && (blacklist == nil || !sliceutil.ContainsStr(blacklist.FieldsBlacklist, primaryField.DBName)) { + columns = append(columns, primaryField) + } + } + for _, backwardsRelation := range rel.FieldSchema.Relationships.Relations { + if backwardsRelation.FieldSchema == rel.Schema && backwardsRelation.Type == schema.BelongsTo { + for _, ref := range backwardsRelation.References { + if !columnsContain(columns, ref.ForeignKey) && (blacklist == nil || !sliceutil.ContainsStr(blacklist.FieldsBlacklist, ref.ForeignKey.DBName)) { + columns = append(columns, ref.ForeignKey) + } + } + } + } + } + + return tx.Preload(relationName, selectScope(rel.FieldSchema.Table, columns, true)) + } +} + +func join(tx *gorm.DB, joinName string, sch *schema.Schema) *gorm.DB { + + var lastTable string + var relation *schema.Relationship + joins := make([]clause.Join, 0, strings.Count(joinName, ".")+1) + for _, rel := range strings.Split(joinName, ".") { + lastTable = sch.Table + if relation != nil { + lastTable = relation.Name + } + relation = sch.Relationships.Relations[rel] + sch = relation.FieldSchema + exprs := make([]clause.Expression, len(relation.References)) + for idx, ref := range relation.References { + if ref.OwnPrimaryKey { + exprs[idx] = clause.Eq{ + Column: clause.Column{Table: lastTable, Name: ref.PrimaryKey.DBName}, + Value: clause.Column{Table: relation.Name, Name: ref.ForeignKey.DBName}, + } + } else { + if ref.PrimaryValue == "" { + exprs[idx] = clause.Eq{ + Column: clause.Column{Table: lastTable, Name: ref.ForeignKey.DBName}, + Value: clause.Column{Table: relation.Name, Name: ref.PrimaryKey.DBName}, + } + } else { + exprs[idx] = clause.Eq{ + Column: clause.Column{Table: relation.Name, Name: ref.ForeignKey.DBName}, + Value: ref.PrimaryValue, + } + } + } + } + j := clause.Join{ + Type: clause.LeftJoin, + Table: clause.Table{Name: sch.Table, Alias: relation.Name}, + ON: clause.Where{Exprs: exprs}, + } + if !joinExists(tx.Statement, j) && !findStatementJoin(tx.Statement, &j) { + joins = append(joins, j) + } + } + if c, ok := tx.Statement.Clauses["FROM"]; ok { + from, _ := c.Expression.(clause.From) + from.Joins = append(from.Joins, joins...) + c.Expression = from + tx.Statement.Clauses["FROM"] = c + return tx + } + return tx.Clauses(clause.From{Joins: joins}) +} + +func joinExists(stmt *gorm.Statement, join clause.Join) bool { + if c, ok := stmt.Clauses["FROM"]; ok { + from, _ := c.Expression.(clause.From) + c.Expression = from + for _, j := range from.Joins { + if j.Table == join.Table { + return true + } + } + } + for _, j := range stmt.Joins { + groups := joinRegex.FindStringSubmatch(j.Name) + if groups != nil { + tableName := groups[joinRegex.SubexpIndex("TableName")] + aliasName := groups[joinRegex.SubexpIndex("Alias")] + tableMatch := tableName == join.Table.Name + aliasMatch := aliasName == "" || aliasName == join.Table.Alias + if tableMatch && aliasMatch { + return true + } + } + } + return false +} + +func findStatementJoin(stmt *gorm.Statement, join *clause.Join) bool { + for _, j := range stmt.Joins { + if j.Name == join.Table.Alias { + return true + } + } + + return false +} + +func quoteString(stmt *gorm.Statement, str string) string { + writer := bytes.NewBufferString("") + stmt.DB.Dialector.QuoteTo(writer, str) + return writer.String() +} + +func processJoinsComputedColumns(stmt *gorm.Statement, sch *schema.Schema) { + for i, j := range stmt.Joins { + rel, ok := sch.Relationships.Relations[j.Name] + if !ok { + continue + } + + columnStmt := gorm.Statement{ + Table: j.Name, + Schema: rel.FieldSchema, + Selects: j.Selects, + Omits: j.Omits, + } + if len(columnStmt.Selects) == 0 { + columnStmt.Selects = []string{"*"} + } + + selectColumns, restricted := columnStmt.SelectAndOmitColumns(false, false) + j.Selects = nil + j.Omits = []string{"*"} + for _, s := range rel.FieldSchema.DBNames { + if v, ok := selectColumns[s]; (ok && v) || (!ok && !restricted) { + field := rel.FieldSchema.FieldsByDBName[s] + computed := field.StructField.Tag.Get("computed") + if computed != "" { + stmt.Selects = append(stmt.Selects, fmt.Sprintf("(%s) %s", strings.ReplaceAll(computed, clause.CurrentTable, quoteString(stmt, j.Name)), quoteString(stmt, j.Name+"__"+s))) + continue + } + stmt.Selects = append(stmt.Selects, fmt.Sprintf("%s.%s %s", quoteString(stmt, j.Name), quoteString(stmt, s), quoteString(stmt, j.Name+"__"+s))) + } + } + stmt.Joins[i] = j + } +} diff --git a/internal/filter/operator.go b/internal/filter/operator.go new file mode 100644 index 00000000..4dcd01b5 --- /dev/null +++ b/internal/filter/operator.go @@ -0,0 +1,162 @@ +package filter + +import ( + "fmt" + + "gorm.io/gorm" + "goyave.dev/goyave/v4/util/sqlutil" +) + +// Operator used by filters to build the SQL query. +// The operator function modifies the GORM statement (most of the time by adding +// a WHERE condition) then returns the modified statement. +// +// Operators may need arguments (e.g. "$eq", equals needs a value to compare the field to); +// RequiredArguments define the minimum number of arguments a client must send in order to +// use this operator in a filter. RequiredArguments is checked during Filter parsing. +// +// Operators may return the given tx without change if they don't support the given dataType or +// add a condition that will always be false. +type Operator struct { + Function func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB + RequiredArguments uint8 +} + +var ( + Operators = map[string]*Operator{ + "$eq": {Function: basicComparison("="), RequiredArguments: 1}, + "$ne": {Function: basicComparison("<>"), RequiredArguments: 1}, + "$gt": {Function: basicComparison(">"), RequiredArguments: 1}, + "$lt": {Function: basicComparison("<"), RequiredArguments: 1}, + "$gte": {Function: basicComparison(">="), RequiredArguments: 1}, + "$lte": {Function: basicComparison("<="), RequiredArguments: 1}, + "$starts": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeText && dataType != DataTypeEnum { + return filter.Where(tx, "FALSE") + } + query := castEnumAsText(column, dataType) + " ILIKE ?" + value := sqlutil.EscapeLike(filter.Args[0]) + "%" + return filter.Where(tx, query, value) + }, + RequiredArguments: 1, + }, + "$ends": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeText && dataType != DataTypeEnum { + return filter.Where(tx, "FALSE") + } + query := castEnumAsText(column, dataType) + " ILIKE ?" + value := "%" + sqlutil.EscapeLike(filter.Args[0]) + return filter.Where(tx, query, value) + }, + RequiredArguments: 1, + }, + "$cont": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeText && dataType != DataTypeEnum { + return filter.Where(tx, "FALSE") + } + query := castEnumAsText(column, dataType) + " ILIKE ?" + value := "%" + sqlutil.EscapeLike(filter.Args[0]) + "%" + return filter.Where(tx, query, value) + }, + RequiredArguments: 1, + }, + "$excl": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeText && dataType != DataTypeEnum { + return filter.Where(tx, "FALSE") + } + query := castEnumAsText(column, dataType) + " NOT ILIKE ?" + value := "%" + sqlutil.EscapeLike(filter.Args[0]) + "%" + return filter.Where(tx, query, value) + }, + RequiredArguments: 1, + }, + "$in": {Function: multiComparison("IN"), RequiredArguments: 1}, + "$notin": {Function: multiComparison("NOT IN"), RequiredArguments: 1}, + "$isnull": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + return filter.Where(tx, column+" IS NULL") + }, + RequiredArguments: 0, + }, + "$istrue": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeBool { + return filter.Where(tx, "FALSE") + } + return filter.Where(tx, column+" IS TRUE") + }, + RequiredArguments: 0, + }, + "$isfalse": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType != DataTypeBool { + return filter.Where(tx, "FALSE") + } + return filter.Where(tx, column+" IS FALSE") + }, + RequiredArguments: 0, + }, + "$notnull": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + return filter.Where(tx, column+" IS NOT NULL") + }, + RequiredArguments: 0, + }, + "$between": { + Function: func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType.IsArray() { + return filter.Where(tx, "FALSE") + } + + args, ok := ConvertArgsToSafeType(filter.Args[:2], dataType) + if !ok { + return filter.Where(tx, "FALSE") + } + query := castEnumAsText(column, dataType) + " BETWEEN ? AND ?" + return filter.Where(tx, query, args...) + }, + RequiredArguments: 2, + }, + } +) + +func castEnumAsText(column string, dataType DataType) string { + if dataType == DataTypeEnum || dataType == DataTypeEnumArray { + return fmt.Sprintf("CAST(%s AS TEXT)", column) + } + return column +} + +func basicComparison(op string) func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + return func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType.IsArray() { + return filter.Where(tx, "FALSE") + } + arg, ok := ConvertToSafeType(filter.Args[0], dataType) + if !ok { + return filter.Where(tx, "FALSE") + } + + query := fmt.Sprintf("%s %s ?", castEnumAsText(column, dataType), op) + return filter.Where(tx, query, arg) + } +} + +func multiComparison(op string) func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + return func(tx *gorm.DB, filter *Filter, column string, dataType DataType) *gorm.DB { + if dataType.IsArray() { + return filter.Where(tx, "FALSE") + } + args, ok := ConvertArgsToSafeType(filter.Args, dataType) + if !ok { + return filter.Where(tx, "FALSE") + } + + query := fmt.Sprintf("%s %s ?", castEnumAsText(column, dataType), op) + return filter.Where(tx, query, args) + } +} diff --git a/internal/filter/search.go b/internal/filter/search.go new file mode 100644 index 00000000..47f56d6d --- /dev/null +++ b/internal/filter/search.go @@ -0,0 +1,67 @@ +package filter + +import ( + "fmt" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" +) + +type Search struct { + Query string + Operator *Operator + Fields []string +} + +func (s *Search) Scope(schema *schema.Schema) func(*gorm.DB) *gorm.DB { + if len(s.Fields) == 0 { + return nil + } + + return func(tx *gorm.DB) *gorm.DB { + searchQuery := tx.Session(&gorm.Session{NewDB: true}) + + for _, field := range s.Fields { + + f, sch, joinName := getField(field, schema, nil) + if f == nil { + continue + } + dataType := getDataType(f) + if dataType == DataTypeUnsupported { + continue + } + + if joinName != "" { + if err := tx.Statement.Parse(tx.Statement.Model); err != nil { + _ = tx.AddError(err) + return tx + } + tx = join(tx, joinName, schema) + } + + filter := &Filter{ + Field: f.DBName, + Operator: s.Operator, + Args: []string{s.Query}, + Or: true, + } + + table := tx.Statement.Quote(tableFromJoinName(sch.Table, joinName)) + + computed := f.StructField.Tag.Get("computed") + var fieldExpr string + if computed != "" { + fieldExpr = fmt.Sprintf("(%s)", strings.ReplaceAll(computed, clause.CurrentTable, table)) + } else { + fieldExpr = table + "." + tx.Statement.Quote(f.DBName) + } + + searchQuery = s.Operator.Function(searchQuery, filter, fieldExpr, dataType) + } + + return tx.Where(searchQuery) + } +} diff --git a/internal/filter/settings.go b/internal/filter/settings.go new file mode 100644 index 00000000..4e06fc48 --- /dev/null +++ b/internal/filter/settings.go @@ -0,0 +1,348 @@ +package filter + +import ( + "fmt" + "strings" + "sync" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + "goyave.dev/goyave/v4" + "goyave.dev/goyave/v4/database" + "goyave.dev/goyave/v4/util/sliceutil" +) + +type Settings struct { + FieldsSearch []string + SearchOperator *Operator + + Blacklist + + DisableFields bool + DisableFilter bool + DisableSort bool + DisableJoin bool + DisableSearch bool +} + +type Blacklist struct { + Relations map[string]*Blacklist + FieldsBlacklist []string + RelationsBlacklist []string + IsFinal bool +} + +var ( + DefaultPageSize = 10 + modelCache = &sync.Map{} +) + +func parseModel(db *gorm.DB, model interface{}) (*schema.Schema, error) { + return schema.Parse(model, modelCache, db.NamingStrategy) +} + +func Scope(db *gorm.DB, request *goyave.Request, dest interface{}) (*database.Paginator, *gorm.DB) { + return (&Settings{}).Scope(db, request, dest) +} + +func ScopeUnpaginated(db *gorm.DB, request *goyave.Request, dest interface{}) *gorm.DB { + return (&Settings{}).ScopeUnpaginated(db, request, dest) +} + +func (s *Settings) Scope(db *gorm.DB, request *goyave.Request, dest interface{}) (*database.Paginator, *gorm.DB) { + db, schema, hasJoins := s.scopeCommon(db, request, dest) + + page := 1 + if request.Has("page") { + page = request.Integer("page") + } + pageSize := DefaultPageSize + if request.Has("per_page") { + pageSize = request.Integer("per_page") + } + + paginator := database.NewPaginator(db, page, pageSize, dest) + paginator.UpdatePageInfo() + + paginator.DB = s.scopeSort(paginator.DB, request, schema) + if fieldsDB := s.scopeFields(paginator.DB, request, schema, hasJoins); fieldsDB != nil { + paginator.DB = fieldsDB + } else { + return nil, paginator.DB + } + + return paginator, paginator.Find() +} + +func (s *Settings) ScopeUnpaginated(db *gorm.DB, request *goyave.Request, dest interface{}) *gorm.DB { + db, schema, hasJoins := s.scopeCommon(db, request, dest) + db = s.scopeSort(db, request, schema) + if fieldsDB := s.scopeFields(db, request, schema, hasJoins); fieldsDB != nil { + db = fieldsDB + } else { + return db + } + return db.Find(dest) +} + +func (s *Settings) scopeCommon(db *gorm.DB, request *goyave.Request, dest interface{}) (*gorm.DB, *schema.Schema, bool) { + schema, err := parseModel(db, dest) + if err != nil { + panic(err) + } + + db = db.Model(dest) + db = s.applyFilters(db, request, schema) + + hasJoins := false + if !s.DisableJoin && request.Has("join") { + joins, ok := request.Data["join"].([]*Join) + if ok { + selectCache := map[string][]string{} + for _, j := range joins { + hasJoins = true + j.selectCache = selectCache + if s := j.Scopes(s, schema); s != nil { + db = db.Scopes(s...) + } + } + } + } + + if !s.DisableSearch && request.Has("search") { + if search := s.applySearch(request, schema); search != nil { + if scope := search.Scope(schema); scope != nil { + db = db.Scopes(scope) + } + } + } + + db.Scopes(func(tx *gorm.DB) *gorm.DB { + processJoinsComputedColumns(tx.Statement, schema) + return tx + }) + + return db, schema, hasJoins +} + +func (s *Settings) scopeFields(db *gorm.DB, request *goyave.Request, schema *schema.Schema, hasJoins bool) *gorm.DB { + if !s.DisableFields && request.Has("fields") { + fields := strings.Split(request.String("fields"), ",") + if hasJoins { + if len(schema.PrimaryFieldDBNames) == 0 { + _ = db.AddError(fmt.Errorf("Could not find primary key. Add `gorm:\"primaryKey\"` to your model")) + return nil + } + fields = addPrimaryKeys(schema, fields) + fields = addForeignKeys(schema, fields) + } + return db.Scopes(selectScope(schema.Table, cleanColumns(schema, fields, s.FieldsBlacklist), false)) + } + return db.Scopes(selectScope(schema.Table, getSelectableFields(&s.Blacklist, schema), false)) +} + +func (s *Settings) scopeSort(db *gorm.DB, request *goyave.Request, schema *schema.Schema) *gorm.DB { + if !s.DisableSort && request.Has("sort") { + sorts, ok := request.Data["sort"].([]*Sort) + if ok { + for _, sort := range sorts { + if scope := sort.Scope(s, schema); scope != nil { + db = db.Scopes(scope) + } + } + } + } + return db +} + +func (s *Settings) applyFilters(db *gorm.DB, request *goyave.Request, schema *schema.Schema) *gorm.DB { + if s.DisableFilter { + return db + } + filterScopes := make([]func(*gorm.DB) *gorm.DB, 0, 2) + joinScopes := make([]func(*gorm.DB) *gorm.DB, 0, 2) + + andLen := filterLen(request, "filter") + orLen := filterLen(request, "or") + mixed := orLen > 1 && andLen > 0 + + for _, queryParam := range []string{"filter", "or"} { + if request.Has(queryParam) { + filters, ok := request.Data[queryParam].([]*Filter) + if ok { + group := make([]func(*gorm.DB) *gorm.DB, 0, 4) + for _, f := range filters { + if mixed { + f = &Filter{ + Field: f.Field, + Operator: f.Operator, + Args: f.Args, + Or: false, + } + } + joinScope, conditionScope := f.Scope(s, schema) + if conditionScope != nil { + group = append(group, conditionScope) + } + if joinScope != nil { + joinScopes = append(joinScopes, joinScope) + } + } + filterScopes = append(filterScopes, groupFilters(group, false)) + } + } + } + if len(joinScopes) > 0 { + db = db.Scopes(joinScopes...) + } + if len(filterScopes) > 0 { + db = db.Scopes(groupFilters(filterScopes, true)) + } + return db +} + +func filterLen(request *goyave.Request, name string) int { + count := 0 + if data, ok := request.Data[name]; ok { + if filters, ok := data.([]*Filter); ok { + count = len(filters) + } + } + return count +} + +func groupFilters(scopes []func(*gorm.DB) *gorm.DB, and bool) func(*gorm.DB) *gorm.DB { + return func(tx *gorm.DB) *gorm.DB { + processedFilters := tx.Session(&gorm.Session{NewDB: true}) + for _, f := range scopes { + processedFilters = f(processedFilters) + } + if and { + return tx.Where(processedFilters) + } + return tx.Or(processedFilters) + } +} + +func (s *Settings) applySearch(request *goyave.Request, schema *schema.Schema) *Search { + // Note: the search condition is not in a group condition (parenthesis) + query, ok := request.Data["search"].(string) + if ok { + fields := s.FieldsSearch + if fields == nil { + for _, f := range getSelectableFields(&s.Blacklist, schema) { + fields = append(fields, f.DBName) + } + } + + operator := s.SearchOperator + if operator == nil { + operator = Operators["$cont"] + } + + search := &Search{ + Query: query, + Operator: operator, + Fields: fields, + } + + return search + } + + return nil +} + +func getSelectableFields(blacklist *Blacklist, sch *schema.Schema) []*schema.Field { + b := []string{} + if blacklist != nil && blacklist.FieldsBlacklist != nil { + b = blacklist.FieldsBlacklist + } + columns := make([]*schema.Field, 0, len(sch.DBNames)) + for _, f := range sch.DBNames { + if !sliceutil.ContainsStr(b, f) { + columns = append(columns, sch.FieldsByDBName[f]) + } + } + + return columns +} + +func selectScope(table string, fields []*schema.Field, override bool) func(*gorm.DB) *gorm.DB { + return func(tx *gorm.DB) *gorm.DB { + + if fields == nil { + return tx + } + + var fieldsWithTableName []string + if len(fields) == 0 { + fieldsWithTableName = []string{"1"} + } else { + fieldsWithTableName = make([]string, 0, len(fields)) + tableName := tx.Statement.Quote(table) + for _, f := range fields { + computed := f.StructField.Tag.Get("computed") + var fieldExpr string + if computed != "" { + fieldExpr = fmt.Sprintf("(%s) %s", strings.ReplaceAll(computed, clause.CurrentTable, tableName), tx.Statement.Quote(f.DBName)) + } else { + fieldExpr = tableName + "." + tx.Statement.Quote(f.DBName) + } + + fieldsWithTableName = append(fieldsWithTableName, fieldExpr) + } + } + + if override { + return tx.Select(fieldsWithTableName) + } + + return tx.Select(tx.Statement.Selects, fieldsWithTableName) + } +} + +func getField(field string, sch *schema.Schema, blacklist *Blacklist) (*schema.Field, *schema.Schema, string) { + joinName := "" + s := sch + if i := strings.LastIndex(field, "."); i != -1 && i+1 < len(field) { + rel := field[:i] + field = field[i+1:] + for _, v := range strings.Split(rel, ".") { + if blacklist != nil && (sliceutil.ContainsStr(blacklist.RelationsBlacklist, v) || blacklist.IsFinal) { + return nil, nil, "" + } + relation, ok := s.Relationships.Relations[v] + if !ok || (relation.Type != schema.HasOne && relation.Type != schema.BelongsTo) { + return nil, nil, "" + } + + s = relation.FieldSchema + if blacklist != nil { + blacklist = blacklist.Relations[v] + } + } + joinName = rel + } + if blacklist != nil && sliceutil.ContainsStr(blacklist.FieldsBlacklist, field) { + return nil, nil, "" + } + col, ok := s.FieldsByDBName[field] + if !ok { + return nil, nil, "" + } + + return col, s, joinName +} + +func tableFromJoinName(table string, joinName string) string { + if joinName != "" { + i := strings.LastIndex(joinName, ".") + if i != -1 { + table = joinName[i+1:] + } else { + table = joinName + } + } + return table +} diff --git a/internal/filter/sort.go b/internal/filter/sort.go new file mode 100644 index 00000000..958e32d6 --- /dev/null +++ b/internal/filter/sort.go @@ -0,0 +1,60 @@ +package filter + +import ( + "fmt" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" +) + +type Sort struct { + Field string + Order SortOrder +} + +type SortOrder string + +const ( + SortAscending SortOrder = "ASC" + SortDescending SortOrder = "DESC" +) + +func (s *Sort) Scope(settings *Settings, schema *schema.Schema) func(*gorm.DB) *gorm.DB { + field, sch, joinName := getField(s.Field, schema, &settings.Blacklist) + if field == nil { + return nil + } + + computed := field.StructField.Tag.Get("computed") + + return func(tx *gorm.DB) *gorm.DB { + if joinName != "" { + if err := tx.Statement.Parse(tx.Statement.Model); err != nil { + _ = tx.AddError(err) + return tx + } + tx = join(tx, joinName, schema) + } + + table := tableFromJoinName(sch.Table, joinName) + var column clause.Column + if computed != "" { + column = clause.Column{ + Raw: true, + Name: fmt.Sprintf("(%s)", strings.ReplaceAll(computed, clause.CurrentTable, tx.Statement.Quote(table))), + } + } else { + column = clause.Column{ + Table: tableFromJoinName(sch.Table, joinName), + Name: field.DBName, + } + } + c := clause.OrderByColumn{ + Column: column, + Desc: s.Order == SortDescending, + } + return tx.Order(c) + } +} diff --git a/internal/filter/util.go b/internal/filter/util.go new file mode 100644 index 00000000..44f350f7 --- /dev/null +++ b/internal/filter/util.go @@ -0,0 +1,242 @@ +package filter + +import ( + "strconv" + "strings" + "time" + + "gorm.io/gorm/schema" + "goyave.dev/goyave/v4/util/sliceutil" +) + +type DataType string + +func (d DataType) IsArray() bool { + return strings.HasSuffix(string(d), "[]") +} + +const ( + DataTypeText DataType = "text" + DataTypeTextArray DataType = "text[]" + + DataTypeEnum DataType = "enum" + DataTypeEnumArray DataType = "enum[]" + + DataTypeBool DataType = "bool" + DataTypeBoolArray DataType = "bool[]" + + DataTypeInt8 DataType = "int8" + DataTypeInt8Array DataType = "int8[]" + DataTypeInt16 DataType = "int16" + DataTypeInt16Array DataType = "int16[]" + DataTypeInt32 DataType = "int32" + DataTypeInt32Array DataType = "int32[]" + DataTypeInt64 DataType = "int64" + DataTypeInt64Array DataType = "int64[]" + + DataTypeUint8 DataType = "uint8" + DataTypeUint8Array DataType = "uint8[]" + DataTypeUint16 DataType = "uint16" + DataTypeUint16Array DataType = "uint16[]" + DataTypeUint32 DataType = "uint32" + DataTypeUint32Array DataType = "uint32[]" + DataTypeUint64 DataType = "uint64" + DataTypeUint64Array DataType = "uint64[]" + + DataTypeFloat32 DataType = "float32" + DataTypeFloat32Array DataType = "float32[]" + DataTypeFloat64 DataType = "float64" + DataTypeFloat64Array DataType = "float64[]" + + DataTypeTime DataType = "time" + DataTypeTimeArray DataType = "time[]" + + DataTypeUnsupported DataType = "-" +) + +func cleanColumns(sch *schema.Schema, columns []string, blacklist []string) []*schema.Field { + fields := make([]*schema.Field, 0, len(columns)) + for _, c := range columns { + f, ok := sch.FieldsByDBName[c] + if ok && !sliceutil.ContainsStr(blacklist, c) { + fields = append(fields, f) + } + } + + return fields +} + +func addPrimaryKeys(schema *schema.Schema, fields []string) []string { + for _, k := range schema.PrimaryFieldDBNames { + if !sliceutil.ContainsStr(fields, k) { + fields = append(fields, k) + } + } + return fields +} + +func addForeignKeys(sch *schema.Schema, fields []string) []string { + for _, r := range sch.Relationships.Relations { + if r.Type == schema.HasOne || r.Type == schema.BelongsTo { + for _, ref := range r.References { + if !sliceutil.ContainsStr(fields, ref.ForeignKey.DBName) { + fields = append(fields, ref.ForeignKey.DBName) + } + } + } + } + return fields +} + +func columnsContain(fields []*schema.Field, field *schema.Field) bool { + for _, f := range fields { + if f.DBName == field.DBName { + return true + } + } + return false +} + +func getDataType(field *schema.Field) DataType { + fromTag := DataType(strings.ToLower(field.Tag.Get("filterType"))) + switch fromTag { + case DataTypeText, DataTypeTextArray, + DataTypeEnum, DataTypeEnumArray, + DataTypeBool, DataTypeBoolArray, + DataTypeFloat32, DataTypeFloat32Array, + DataTypeFloat64, DataTypeFloat64Array, + DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, + DataTypeInt8Array, DataTypeInt16Array, DataTypeInt32Array, DataTypeInt64Array, + DataTypeUint8, DataTypeUint16, DataTypeUint32, DataTypeUint64, + DataTypeUint8Array, DataTypeUint16Array, DataTypeUint32Array, DataTypeUint64Array, + DataTypeTime, DataTypeTimeArray, + DataTypeUnsupported: + return fromTag + case "": + switch field.GORMDataType { + case schema.String: + return DataTypeText + case schema.Bool: + return DataTypeBool + case schema.Float: + switch field.Size { + case 32: + return DataTypeFloat32 + case 64: + return DataTypeFloat64 + } + case schema.Int: + switch field.Size { + case 8: + return DataTypeInt8 + case 16: + return DataTypeInt16 + case 32: + return DataTypeInt32 + case 64: + return DataTypeInt64 + } + case schema.Uint: + switch field.Size { + case 8: + return DataTypeUint8 + case 16: + return DataTypeUint16 + case 32: + return DataTypeUint32 + case 64: + return DataTypeUint64 + } + case schema.Time: + return DataTypeTime + } + } + return DataTypeUnsupported +} + +func ConvertToSafeType(arg string, dataType DataType) (interface{}, bool) { + switch dataType { + case DataTypeText, DataTypeTextArray, DataTypeEnum, DataTypeEnumArray: + return arg, true + case DataTypeBool, DataTypeBoolArray: + switch arg { + case "1", "on", "true", "yes": + return true, true + case "0", "off", "false", "no": + return false, true + } + return nil, false + case DataTypeFloat32, DataTypeFloat32Array: + return validateFloat(arg, 32) + case DataTypeFloat64, DataTypeFloat64Array: + return validateFloat(arg, 64) + case DataTypeInt8, DataTypeInt8Array: + return validateInt(arg, 8) + case DataTypeInt16, DataTypeInt16Array: + return validateInt(arg, 16) + case DataTypeInt32, DataTypeInt32Array: + return validateInt(arg, 32) + case DataTypeInt64, DataTypeInt64Array: + return validateInt(arg, 64) + case DataTypeUint8, DataTypeUint8Array: + return validateUint(arg, 8) + case DataTypeUint16, DataTypeUint16Array: + return validateUint(arg, 16) + case DataTypeUint32, DataTypeUint32Array: + return validateUint(arg, 32) + case DataTypeUint64, DataTypeUint64Array: + return validateUint(arg, 64) + case DataTypeTime, DataTypeTimeArray: + if validateTime(arg) { + return arg, true + } + } + return nil, false +} + +func validateInt(arg string, bitSize int) (int64, bool) { + i, err := strconv.ParseInt(arg, 10, bitSize) + if err != nil { + return 0, false + } + return i, true +} + +func validateUint(arg string, bitSize int) (uint64, bool) { + i, err := strconv.ParseUint(arg, 10, bitSize) + if err != nil { + return 0, false + } + return i, true +} + +func validateFloat(arg string, bitSize int) (float64, bool) { + i, err := strconv.ParseFloat(arg, bitSize) + if err != nil { + return 0, false + } + return i, true +} + +func validateTime(timeStr string) bool { + for _, format := range []string{time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05", "2006-01-02"} { + _, err := time.Parse(format, timeStr) + if err == nil { + return true + } + } + + return false +} + +func ConvertArgsToSafeType(args []string, dataType DataType) ([]interface{}, bool) { + result := make([]interface{}, 0, len(args)) + for _, arg := range args { + a, ok := ConvertToSafeType(arg, dataType) + if !ok { + return nil, false + } + result = append(result, a) + } + return result, true +} diff --git a/internal/filter/validation.go b/internal/filter/validation.go new file mode 100644 index 00000000..90fb385f --- /dev/null +++ b/internal/filter/validation.go @@ -0,0 +1,213 @@ +package filter + +import ( + "fmt" + "strings" + + "goyave.dev/goyave/v4/lang" + "goyave.dev/goyave/v4/validation" +) + +var Separator = "||" + +func init() { + validation.AddRule("filter", &validation.RuleDefinition{ + Function: validateFilter, + }) + validation.AddRule("sort", &validation.RuleDefinition{ + Function: validateSort, + }) + validation.AddRule("join", &validation.RuleDefinition{ + Function: validateJoin, + }) + lang.SetDefaultValidationRule("filter.array", "The filter format is invalid.") + lang.SetDefaultValidationRule("join.array", "The join format is invalid.") + lang.SetDefaultValidationRule("sort.array", "The sort format is invalid.") +} + +func validateFilter(ctx *validation.Context) bool { + if _, ok := ctx.Value.(*Filter); ok { + return true + } + str, ok := ctx.Value.(string) + if !ok { + return false + } + f, err := ParseFilter(str) + if err != nil { + return false + } + if len(ctx.Rule.Params) > 0 && ctx.Rule.Params[0] == "or" { + f.Or = true + } + ctx.Value = f + return true +} + +func validateSort(ctx *validation.Context) bool { + if _, ok := ctx.Value.(*Sort); ok { + return true + } + str, ok := ctx.Value.(string) + if !ok { + return false + } + sort, err := ParseSort(str) + if err != nil { + return false + } + ctx.Value = sort + return true +} + +func validateJoin(ctx *validation.Context) bool { + if _, ok := ctx.Value.(*Join); ok { + return true + } + str, ok := ctx.Value.(string) + if !ok { + return false + } + join, err := ParseJoin(str) + if err != nil { + return false + } + ctx.Value = join + return true +} + +func ApplyValidation(set validation.RuleSet) { + set["filter"] = validation.List{"array"} + set["filter[]"] = validation.List{"filter"} + set["or"] = validation.List{"array"} + set["or[]"] = validation.List{"filter:or"} + set["sort"] = validation.List{"array"} + set["sort[]"] = validation.List{"sort"} + set["join"] = validation.List{"array"} + set["join[]"] = validation.List{"join"} + set["fields"] = validation.List{"string"} + set["page"] = validation.List{"integer", "min:1"} + set["per_page"] = validation.List{"integer", "between:1,500"} + set["search"] = validation.List{"string"} +} + +func ApplyValidationRules(set *validation.Rules) { + set.Fields["filter"] = &validation.Field{Rules: []*validation.Rule{{Name: "array"}}} + set.Fields["filter[]"] = &validation.Field{Rules: []*validation.Rule{{Name: "filter"}}} + set.Fields["or"] = &validation.Field{Rules: []*validation.Rule{{Name: "array"}}} + set.Fields["or[]"] = &validation.Field{Rules: []*validation.Rule{{Name: "filter", Params: []string{"or"}}}} + set.Fields["sort"] = &validation.Field{Rules: []*validation.Rule{{Name: "array"}}} + set.Fields["sort[]"] = &validation.Field{Rules: []*validation.Rule{{Name: "sort"}}} + set.Fields["join"] = &validation.Field{Rules: []*validation.Rule{{Name: "array"}}} + set.Fields["join[]"] = &validation.Field{Rules: []*validation.Rule{{Name: "join"}}} + set.Fields["fields"] = &validation.Field{Rules: []*validation.Rule{{Name: "string"}}} + set.Fields["page"] = &validation.Field{Rules: []*validation.Rule{{Name: "integer"}, {Name: "min", Params: []string{"1"}}}} + set.Fields["per_page"] = &validation.Field{Rules: []*validation.Rule{{Name: "integer"}, {Name: "between", Params: []string{"1", "500"}}}} + set.Fields["search"] = &validation.Field{Rules: []*validation.Rule{{Name: "string"}}} +} + +func ParseFilter(filter string) (*Filter, error) { + res := &Filter{} + f := filter + op := "" + + index := strings.Index(f, Separator) + if index == -1 { + return nil, fmt.Errorf("Missing operator") + } + res.Field = strings.TrimSpace(f[:index]) + if res.Field == "" { + return nil, fmt.Errorf("Invalid filter syntax") + } + f = f[index+2:] + + index = strings.Index(f, Separator) + if index == -1 { + index = len(f) + } + op = strings.TrimSpace(f[:index]) + operator, ok := Operators[op] + if !ok { + return nil, fmt.Errorf("Unknown operator: %q", f[:index]) + } + res.Operator = operator + + if index < len(f) { + f = f[index+2:] + for paramIndex := strings.Index(f, ","); paramIndex < len(f); paramIndex = strings.Index(f, ",") { + if paramIndex == -1 { + paramIndex = len(f) + } + p := strings.TrimSpace(f[:paramIndex]) + if p == "" { + return nil, fmt.Errorf("Invalid filter syntax") + } + res.Args = append(res.Args, p) + if paramIndex+1 >= len(f) { + break + } + f = f[paramIndex+1:] + } + } + + if len(res.Args) < int(res.Operator.RequiredArguments) { + return nil, fmt.Errorf("Operator %q requires at least %d argument(s)", op, res.Operator.RequiredArguments) + } + + return res, nil +} + +func ParseSort(sort string) (*Sort, error) { + commaIndex := strings.Index(sort, ",") + if commaIndex == -1 { + return nil, fmt.Errorf("Invalid sort syntax") + } + + fieldName := strings.TrimSpace(sort[:commaIndex]) + order := strings.TrimSpace(strings.ToUpper(sort[commaIndex+1:])) + if fieldName == "" || order == "" { + return nil, fmt.Errorf("Invalid sort syntax") + } + + if order != string(SortAscending) && order != string(SortDescending) { + return nil, fmt.Errorf("Invalid sort order %q", order) + } + + s := &Sort{ + Field: fieldName, + Order: SortOrder(order), + } + return s, nil +} + +func ParseJoin(join string) (*Join, error) { + separatorIndex := strings.Index(join, Separator) + if separatorIndex == -1 { + separatorIndex = len(join) + } + + relation := strings.TrimSpace(join[:separatorIndex]) + if relation == "" { + return nil, fmt.Errorf("Invalid join syntax") + } + + var fields []string + if separatorIndex+2 < len(join) { + fields = strings.Split(join[separatorIndex+2:], ",") + for i, f := range fields { + f = strings.TrimSpace(f) + if f == "" { + return nil, fmt.Errorf("Invalid join syntax") + } + fields[i] = f + } + } else { + fields = nil + } + + j := &Join{ + Relation: relation, + Fields: fields, + } + return j, nil +} diff --git a/internal/gorm/gorm.go b/internal/gorm/gorm.go new file mode 100644 index 00000000..30b95d01 --- /dev/null +++ b/internal/gorm/gorm.go @@ -0,0 +1,152 @@ +// This file contains the custom Gorm logger +// Origin Source: https://github.com/go-gorm/gorm/blob/master/logger/logger.go +// Author: Jinzhu +// Changes: Modified functions related to logging +// Licensed under the MIT License +/* +The MIT License (MIT) + +Copyright (c) 2013-NOW Jinzhu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package gorm + +import ( + "context" + "fmt" + internalLogger "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm/logger" + "gorm.io/gorm/utils" + "time" +) + +func (l *customGormLogger) LogMode(level logger.LogLevel) logger.Interface { + newlogger := *l + newlogger.LogLevel = level + return &newlogger +} + +func (l *customGormLogger) Info(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Info { + internalLogger.Infof(ctx, l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...) + } +} + +func (l *customGormLogger) Warn(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Warn { + internalLogger.Warnf(ctx, l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...) + } +} + +func (l *customGormLogger) Error(ctx context.Context, msg string, data ...interface{}) { + if l.LogLevel >= logger.Error { + internalLogger.Errorf(ctx, l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...) + } +} + +func (l *customGormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { + if l.LogLevel <= logger.Silent { + return + } + + elapsed := time.Since(begin) + switch { + case err != nil && l.LogLevel >= logger.Error && (!errors.Is(err, logger.ErrRecordNotFound) || !l.IgnoreRecordNotFoundError): + sql, rows := fc() + if rows == -1 { + internalLogger.Errorf(ctx, l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql) + //l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + internalLogger.Errorf(ctx, l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql) + //l.Printf(l.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql) + } + case elapsed > l.SlowThreshold && l.SlowThreshold != 0 && l.LogLevel >= logger.Warn: + sql, rows := fc() + slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold) + if rows == -1 { + internalLogger.Warnf(ctx, l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql) + //l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + internalLogger.Warnf(ctx, l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql) + //l.Printf(l.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql) + } + case l.LogLevel == logger.Info: + sql, rows := fc() + if rows == -1 { + internalLogger.Infof(ctx, l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql) + //l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql) + } else { + internalLogger.Infof(ctx, l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql) + //l.Printf(l.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql) + } + default: + internalLogger.Info(ctx, "Trace") + } +} + +type customGormLogger struct { + logger.Writer + logger.Config + infoStr, warnStr, errStr string + traceStr, traceErrStr, traceWarnStr string +} + +const ( + Reset = "\033[0m" + Red = "\033[31m" + Green = "\033[32m" + Yellow = "\033[33m" + Blue = "\033[34m" + Magenta = "\033[35m" + Cyan = "\033[36m" + White = "\033[37m" + BlueBold = "\033[34;1m" + MagentaBold = "\033[35;1m" + RedBold = "\033[31;1m" + YellowBold = "\033[33;1m" +) + +func NewGormLogger() logger.Interface { + infoStr := Green + "%s\n" + Reset + Green + "[info] " + Reset + warnStr := BlueBold + "%s\n" + Reset + Magenta + "[warn] " + Reset + errStr := Magenta + "%s\n" + Reset + Red + "[error] " + Reset + traceStr := Green + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s" + traceWarnStr := Green + "%s " + Yellow + "%s\n" + Reset + RedBold + "[%.3fms] " + Yellow + "[rows:%v]" + Magenta + " %s" + Reset + traceErrStr := RedBold + "%s " + MagentaBold + "%s\n" + Reset + Yellow + "[%.3fms] " + BlueBold + "[rows:%v]" + Reset + " %s" + + return &customGormLogger{ + Writer: customGormLogger{}, + Config: logger.Config{ + SlowThreshold: time.Second, + LogLevel: logger.Silent, + IgnoreRecordNotFoundError: true, + ParameterizedQueries: true, + Colorful: false, + }, + infoStr: infoStr, + warnStr: warnStr, + errStr: errStr, + traceStr: traceStr, + traceErrStr: traceErrStr, + traceWarnStr: traceWarnStr, + } +} diff --git a/internal/helper/jwt.go b/internal/helper/jwt.go index 68170a55..3db7184a 100644 --- a/internal/helper/jwt.go +++ b/internal/helper/jwt.go @@ -1,9 +1,10 @@ package helper import ( + "fmt" "time" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v4" "github.com/spf13/viper" ) @@ -37,3 +38,18 @@ func VerifyToken(tokenString string) (*jwt.Token, error) { } return token, err } + +func StringToTokenWithoutVerification(tokenString string) (*jwt.Token, error) { + token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) + if err != nil { + return nil, fmt.Errorf("invalid token") + } + return token, nil +} +func RetrieveClaims(token *jwt.Token) (map[string]interface{}, error) { + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return nil, fmt.Errorf("invalid token") + } + return claims, nil +} diff --git a/internal/helper/ssh.go b/internal/helper/ssh.go index 7b65b929..83f45d68 100644 --- a/internal/helper/ssh.go +++ b/internal/helper/ssh.go @@ -2,12 +2,13 @@ package helper import ( "bytes" + "context" + "github.com/openinfradev/tks-api/pkg/log" "io" "sync" "time" "github.com/gorilla/websocket" - "github.com/openinfradev/tks-api/pkg/log" "golang.org/x/crypto/ssh" ) @@ -97,11 +98,12 @@ func (sws *LogicSshWsSession) Close() { } } func (sws *LogicSshWsSession) Start(quitChan chan bool) { - go sws.receiveWsMsg(quitChan) - go sws.sendComboOutput(quitChan) + ctx := context.Background() + go sws.receiveWsMsg(ctx, quitChan) + go sws.sendComboOutput(ctx, quitChan) } -func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { +func (sws *LogicSshWsSession) receiveWsMsg(ctx context.Context, exitCh chan bool) { wsConn := sws.wsConn //tells other go routine quit defer setQuit(exitCh) @@ -114,12 +116,12 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { //read websocket msg _, wsData, err := wsConn.ReadMessage() if err != nil { - log.Error(err) - log.Error("reading webSocket message failed") + //log.Error(err) + //log.Error("reading webSocket message failed") return } - cmd := bytes.NewBuffer(wsData).String() - log.Debug(cmd) + //cmd := bytes.NewBuffer(wsData).String() + //log.Debug(cmd) //unmashal bytes into struct /* if err := json.Unmarshal(wsData, &msgObj); err != nil { @@ -132,19 +134,19 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { } */ - sws.sendWebsocketInputCommandToSshSessionStdinPipe(wsData) + sws.sendWebsocketInputCommandToSshSessionStdinPipe(ctx, wsData) } } } // sendWebsocketInputCommandToSshSessionStdinPipe -func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) { +func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(ctx context.Context, cmdBytes []byte) { if _, err := sws.stdinPipe.Write(cmdBytes); err != nil { - log.Error("ws cmd bytes write to ssh.stdin pipe failed") + log.Error(ctx, "ws cmd bytes write to ssh.stdin pipe failed") } } -func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) { +func (sws *LogicSshWsSession) sendComboOutput(ctx context.Context, exitCh chan bool) { wsConn := sws.wsConn defer setQuit(exitCh) @@ -162,11 +164,11 @@ func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) { if len(bs) > 0 { err := wsConn.WriteMessage(websocket.TextMessage, bs) if err != nil { - log.Error("ssh sending combo output to webSocket failed") + log.Error(ctx, "ssh sending combo output to webSocket failed") } _, err = sws.logBuff.Write(bs) if err != nil { - log.Error("combo output to log buffer failed") + log.Error(ctx, "combo output to log buffer failed") } sws.comboOutput.buffer.Reset() } @@ -179,7 +181,7 @@ func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) { func (sws *LogicSshWsSession) Wait(quitChan chan bool) { if err := sws.session.Wait(); err != nil { - log.Error("ssh session wait failed") + //log.Error("ssh session wait failed") setQuit(quitChan) } } diff --git a/internal/helper/util.go b/internal/helper/util.go index 59933cbf..b63bc911 100644 --- a/internal/helper/util.go +++ b/internal/helper/util.go @@ -1,7 +1,7 @@ package helper import ( - "bytes" + "context" "crypto/rand" "encoding/json" "fmt" @@ -37,20 +37,10 @@ func ModelToJson(in any) string { return s } -func Transcode(in, out interface{}) { - buf := new(bytes.Buffer) - if err := json.NewEncoder(buf).Encode(in); err != nil { - log.Error(err) - } - if err := json.NewDecoder(buf).Decode(out); err != nil { - log.Error(err) - } -} - -func GenerateEmailCode() (string, error) { +func GenerateEmailCode(ctx context.Context) (string, error) { num, err := rand.Int(rand.Reader, big.NewInt(900000)) if err != nil { - log.Error(err) + log.Error(ctx, err) return "", err } num = num.Add(num, big.NewInt(100000)) @@ -64,7 +54,7 @@ func IsDurationExpired(targetTime time.Time, duration time.Duration) bool { return diff > duration } -func SplitAddress(url string) (address string, port int) { +func SplitAddress(ctx context.Context, url string) (address string, port int) { url = strings.TrimSuffix(url, "\n") arr := strings.Split(url, ":") address = arr[0] + ":" + arr[1] @@ -79,7 +69,7 @@ func SplitAddress(url string) (address string, port int) { } port = portNum - log.Infof("address : %s, port : %d", address, port) + log.Infof(ctx, "address : %s, port : %d", address, port) return } @@ -91,3 +81,24 @@ func ToSnakeCase(str string) string { snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") return strings.ToLower(snake) } + +func BoolP(value bool) *bool { + return &value +} + +func StringP(value string) *string { + return &value +} + +func UUIDP(value uuid.UUID) *uuid.UUID { + return &value +} + +func DeepCopy(src, dest interface{}) error { + bytes, err := json.Marshal(src) + if err != nil { + return err + } + + return json.Unmarshal(bytes, dest) +} diff --git a/internal/keycloak/config.go b/internal/keycloak/config.go index 212748c1..eb26042c 100644 --- a/internal/keycloak/config.go +++ b/internal/keycloak/config.go @@ -12,7 +12,7 @@ const ( DefaultClientID = "tks" DefaultClientSecret = "secret" AdminCliClientID = "admin-cli" - accessTokenLifespan = 60 * 60 * 24 // 1 day - ssoSessionIdleTimeout = 60 * 60 * 24 // 1 day - ssoSessionMaxLifespan = 60 * 60 * 24 // 1 day + AccessTokenLifespan = 60 * 60 * 24 // 1 day + SsoSessionIdleTimeout = 60 * 60 * 24 // 1 day + SsoSessionMaxLifespan = 60 * 60 * 24 // 1 day ) diff --git a/internal/keycloak/keycloak.go b/internal/keycloak/keycloak.go index 45976edb..46cd34bd 100644 --- a/internal/keycloak/keycloak.go +++ b/internal/keycloak/keycloak.go @@ -4,40 +4,48 @@ import ( "context" "crypto/tls" "fmt" - "github.com/spf13/viper" "time" "github.com/Nerzal/gocloak/v13" - "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" ) type IKeycloak interface { - InitializeKeycloak() error - - LoginAdmin(accountId string, password string) (*domain.User, error) - Login(accountId string, password string, organizationId string) (*domain.User, error) - Logout(sessionId string, organizationId string) error - - CreateRealm(organizationId string) (string, error) - GetRealm(organizationId string) (*domain.Organization, error) - GetRealms() ([]*domain.Organization, error) - DeleteRealm(organizationId string) error - UpdateRealm(organizationId string, organizationConfig domain.Organization) error - - CreateUser(organizationId string, user *gocloak.User) error - GetUser(organizationId string, userAccountId string) (*gocloak.User, error) - GetUsers(organizationId string) ([]*gocloak.User, error) - DeleteUser(organizationId string, userAccountId string) error - UpdateUser(organizationId string, user *gocloak.User) error - JoinGroup(organizationId string, userId string, groupName string) error - LeaveGroup(organizationId string, userId string, groupName string) error - - VerifyAccessToken(token string, organizationId string) error - GetSessions(userId string, organizationId string) (*[]string, error) + InitializeKeycloak(ctx context.Context) error + + LoginAdmin(ctx context.Context, accountId string, password string) (*model.User, error) + Login(ctx context.Context, accountId string, password string, organizationId string) (*model.User, error) + Logout(ctx context.Context, sessionId string, organizationId string) error + + CreateRealm(ctx context.Context, organizationId string) (string, error) + GetRealm(ctx context.Context, organizationId string) (*model.Organization, error) + GetRealms(ctx context.Context) ([]*model.Organization, error) + DeleteRealm(ctx context.Context, organizationId string) error + UpdateRealm(ctx context.Context, organizationId string, organizationConfig model.Organization) error + + CreateUser(ctx context.Context, organizationId string, user *gocloak.User) (string, error) + GetUser(ctx context.Context, organizationId string, userAccountId string) (*gocloak.User, error) + GetUsers(ctx context.Context, organizationId string) ([]*gocloak.User, error) + DeleteUser(ctx context.Context, organizationId string, userAccountId string) error + UpdateUser(ctx context.Context, organizationId string, user *gocloak.User) error + JoinGroup(ctx context.Context, organizationId string, userId string, groupName string) error + LeaveGroup(ctx context.Context, organizationId string, userId string, groupName string) error + CreateGroup(ctx context.Context, organizationId string, groupName string) (string, error) + DeleteGroup(ctx context.Context, organizationId string, groupName string) error + UpdateGroup(ctx context.Context, organizationId string, oldGroupName string, newGroupName string) error + EnsureClientRoleWithClientName(ctx context.Context, organizationId string, clientName string, roleName string) error + DeleteClientRoleWithClientName(ctx context.Context, organizationId string, clientName string, roleName string) error + + AssignClientRoleToUser(ctx context.Context, organizationId string, userId string, clientName string, roleName string) error + UnassignClientRoleToUser(ctx context.Context, organizationId string, userId string, clientName string, roleName string) error + + VerifyAccessToken(ctx context.Context, token string, organizationId string) (bool, error) + GetSessions(ctx context.Context, userId string, organizationId string) (*[]string, error) + SetClientScopeRolesToOptionalToTksClient(ctx context.Context, organizationId string) error } type Keycloak struct { config *Config @@ -45,25 +53,79 @@ type Keycloak struct { adminCliToken *gocloak.JWT } -func (k *Keycloak) LoginAdmin(accountId string, password string) (*domain.User, error) { - ctx := context.Background() - JWTToken, err := k.client.LoginAdmin(ctx, accountId, password, DefaultMasterRealm) +func (k *Keycloak) CreateGroup(ctx context.Context, organizationId string, groupName string) (string, error) { + token := k.adminCliToken + group := gocloak.Group{ + Name: gocloak.StringP(groupName + "@" + organizationId), + } + groupId, err := k.client.CreateGroup(context.Background(), token.AccessToken, organizationId, group) + if err != nil { + return "", err + } + + return groupId, nil +} + +func (k *Keycloak) DeleteGroup(ctx context.Context, organizationId string, groupName string) error { + token := k.adminCliToken + groups, err := k.client.GetGroups(context.Background(), token.AccessToken, organizationId, gocloak.GetGroupsParams{ + Search: &groupName, + }) + if err != nil { + log.Error(ctx, err) + return httpErrors.NewInternalServerError(err, "", "") + } + if len(groups) == 0 { + return httpErrors.NewNotFoundError(fmt.Errorf("group not found"), "", "") + } + err = k.client.DeleteGroup(context.Background(), token.AccessToken, organizationId, *groups[0].ID) if err != nil { - log.Error(err) + return err + } + + return nil +} + +func (k *Keycloak) UpdateGroup(ctx context.Context, organizationId string, oldGroupName string, newGroupName string) error { + token := k.adminCliToken + groups, err := k.client.GetGroups(context.Background(), token.AccessToken, organizationId, gocloak.GetGroupsParams{ + Search: &oldGroupName, + }) + if err != nil { + log.Error(ctx, err) + return httpErrors.NewInternalServerError(err, "", "") + } + if len(groups) == 0 { + return httpErrors.NewNotFoundError(fmt.Errorf("group not found"), "", "") + } + err = k.client.UpdateGroup(context.Background(), token.AccessToken, organizationId, gocloak.Group{ + ID: groups[0].ID, + Name: gocloak.StringP(newGroupName + "@" + organizationId), + }) + if err != nil { + log.Error(ctx, err) + return httpErrors.NewInternalServerError(err, "", "") + } + return nil +} + +func (k *Keycloak) LoginAdmin(ctx context.Context, accountId string, password string) (*model.User, error) { + JWTToken, err := k.client.LoginAdmin(context.Background(), accountId, password, DefaultMasterRealm) + if err != nil { + log.Error(ctx, err) return nil, err } - return &domain.User{Token: JWTToken.AccessToken}, nil + return &model.User{Token: JWTToken.AccessToken}, nil } -func (k *Keycloak) Login(accountId string, password string, organizationId string) (*domain.User, error) { - ctx := context.Background() - JWTToken, err := k.client.Login(ctx, DefaultClientID, k.config.ClientSecret, organizationId, accountId, password) +func (k *Keycloak) Login(ctx context.Context, accountId string, password string, organizationId string) (*model.User, error) { + JWTToken, err := k.client.Login(context.Background(), DefaultClientID, k.config.ClientSecret, organizationId, accountId, password) if err != nil { - log.Error(err) + log.Error(ctx, err) return nil, err } - return &domain.User{Token: JWTToken.AccessToken}, nil + return &model.User{Token: JWTToken.AccessToken}, nil } func New(config *Config) IKeycloak { @@ -71,16 +133,43 @@ func New(config *Config) IKeycloak { config: config, } } -func (k *Keycloak) InitializeKeycloak() error { + +func (k *Keycloak) SetClientScopeRolesToOptionalToTksClient(ctx context.Context, organizationId string) error { + token := k.adminCliToken + c, err := k.client.GetClients(context.TODO(), token.AccessToken, organizationId, gocloak.GetClientsParams{ + ClientID: gocloak.StringP(DefaultClientID), + }) + if err != nil { + return err + } + + defaultClientScopes, err := k.client.GetClientsDefaultScopes(context.TODO(), token.AccessToken, organizationId, *c[0].ID) + if err != nil { + return err + } + for _, defaultClientScope := range defaultClientScopes { + if *defaultClientScope.Name == "roles" { + if err := k.client.RemoveDefaultScopeFromClient(context.TODO(), token.AccessToken, organizationId, *c[0].ID, *defaultClientScope.ID); err != nil { + return err + } + if err := k.client.AddOptionalScopeToClient(context.TODO(), token.AccessToken, organizationId, *c[0].ID, *defaultClientScope.ID); err != nil { + return err + } + } + } + + return nil +} + +func (k *Keycloak) InitializeKeycloak(ctx context.Context) error { k.client = gocloak.NewClient(k.config.Address) - ctx := context.Background() restyClient := k.client.RestyClient() restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) var token *gocloak.JWT var err error - if token, err = k.client.LoginAdmin(ctx, k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm); err != nil { - log.Fatal(err) + if token, err = k.client.LoginAdmin(context.Background(), k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm); err != nil { + log.Fatal(ctx, err) return err } k.adminCliToken = token @@ -92,18 +181,18 @@ func (k *Keycloak) InitializeKeycloak() error { group, err := k.ensureGroupByName(ctx, token, DefaultMasterRealm, "tks-admin@master") if err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } user, err := k.ensureUserByName(ctx, token, DefaultMasterRealm, k.config.AdminId, k.config.AdminPassword) if err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } if err := k.addUserToGroup(ctx, token, DefaultMasterRealm, *user.ID, *group.ID); err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } @@ -111,26 +200,33 @@ func (k *Keycloak) InitializeKeycloak() error { redirectURIs = append(redirectURIs, viper.GetString("external-address")+"/*") tksClient, err := k.ensureClient(ctx, token, DefaultMasterRealm, DefaultClientID, k.config.ClientSecret, &redirectURIs) if err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } + // for _, defaultMapper := range defaultProtocolTksMapper { if err := k.ensureClientProtocolMappers(ctx, token, DefaultMasterRealm, *tksClient.ClientID, "openid", defaultMapper); err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } } adminCliClient, err := k.ensureClient(ctx, token, DefaultMasterRealm, AdminCliClientID, k.config.ClientSecret, nil) if err != nil { - log.Fatal(err) + log.Fatal(ctx, err) + return err + } + + err = k.SetClientScopeRolesToOptionalToTksClient(ctx, DefaultMasterRealm) + if err != nil { + log.Fatal(ctx, err) return err } for _, defaultMapper := range defaultProtocolTksMapper { if err := k.ensureClientProtocolMappers(ctx, token, DefaultMasterRealm, *adminCliClient.ClientID, "openid", defaultMapper); err != nil { - log.Fatal(err) + log.Fatal(ctx, err) return err } } @@ -140,11 +236,11 @@ func (k *Keycloak) InitializeKeycloak() error { go func() { for { if token, err := k.client.RefreshToken(context.Background(), k.adminCliToken.RefreshToken, AdminCliClientID, k.config.ClientSecret, DefaultMasterRealm); err != nil { - log.Errorf("[Refresh]error is :%s(%T)", err.Error(), err) - log.Info("[Do Keycloak Admin CLI Login]") + log.Errorf(ctx, "[Refresh]error is :%s(%T)", err.Error(), err) + log.Info(ctx, "[Do Keycloak Admin CLI Login]") k.adminCliToken, err = k.client.LoginAdmin(ctx, k.config.AdminId, k.config.AdminPassword, DefaultMasterRealm) if err != nil { - log.Errorf("[LoginAdmin]error is :%s(%T)", err.Error(), err) + log.Errorf(ctx, "[LoginAdmin]error is :%s(%T)", err.Error(), err) } } else { k.adminCliToken = token @@ -156,12 +252,11 @@ func (k *Keycloak) InitializeKeycloak() error { return nil } -func (k *Keycloak) CreateRealm(organizationId string) (string, error) { +func (k *Keycloak) CreateRealm(ctx context.Context, organizationId string) (string, error) { //TODO implement me - ctx := context.Background() token := k.adminCliToken - realmUUID, err := k.client.CreateRealm(ctx, token.AccessToken, defaultRealmSetting(organizationId)) + realmUUID, err := k.client.CreateRealm(context.Background(), token.AccessToken, defaultRealmSetting(organizationId)) if err != nil { return "", err } @@ -170,7 +265,13 @@ func (k *Keycloak) CreateRealm(organizationId string) (string, error) { redirectURIs = append(redirectURIs, viper.GetString("external-address")+"/*") clientUUID, err := k.createDefaultClient(context.Background(), token.AccessToken, organizationId, DefaultClientID, k.config.ClientSecret, &redirectURIs) if err != nil { - log.Error(err, "createDefaultClient") + log.Error(ctx, err, "createDefaultClient") + return "", err + } + + err = k.SetClientScopeRolesToOptionalToTksClient(ctx, organizationId) + if err != nil { + log.Error(ctx, err, "SetClientScopeRolesToOptionalToTksClient") return "", err } @@ -185,54 +286,10 @@ func (k *Keycloak) CreateRealm(organizationId string) (string, error) { "userinfo.token.claim": "false", } } - if _, err := k.createClientProtocolMapper(ctx, token.AccessToken, organizationId, clientUUID, defaultMapper); err != nil { + if _, err := k.createClientProtocolMapper(context.Background(), token.AccessToken, organizationId, clientUUID, defaultMapper); err != nil { return "", err } } - adminGroupUuid, err := k.createGroup(ctx, token.AccessToken, organizationId, "admin@"+organizationId) - if err != nil { - return realmUUID, err - } - - realmManagementClientUuid, err := k.getClientByClientId(ctx, token.AccessToken, organizationId, "realm-management") - if err != nil { - return realmUUID, err - } - - realmAdminRole, err := k.getClientRole(ctx, token.AccessToken, organizationId, realmManagementClientUuid, "realm-admin") - if err != nil { - return realmUUID, err - } - - err = k.addClientRoleToGroup(ctx, token.AccessToken, organizationId, realmManagementClientUuid, adminGroupUuid, - &gocloak.Role{ - ID: realmAdminRole.ID, - Name: realmAdminRole.Name, - }) - - if err != nil { - return "", err - } - - userGroupUuid, err := k.createGroup(ctx, token.AccessToken, organizationId, "user@"+organizationId) - if err != nil { - return "", err - } - - viewUserRole, err := k.getClientRole(ctx, token.AccessToken, organizationId, realmManagementClientUuid, "view-users") - if err != nil { - return "", err - } - - err = k.addClientRoleToGroup(ctx, token.AccessToken, organizationId, realmManagementClientUuid, userGroupUuid, - &gocloak.Role{ - ID: viewUserRole.ID, - Name: viewUserRole.Name, - }) - - if err != nil { - return "", err - } // TODO: implement leader, member, viewer //leaderGroup, err := c.ensureGroup(ctx, token, realmName, "leader@"+realmName) @@ -242,10 +299,9 @@ func (k *Keycloak) CreateRealm(organizationId string) (string, error) { return realmUUID, nil } -func (k *Keycloak) GetRealm(organizationId string) (*domain.Organization, error) { - ctx := context.Background() +func (k *Keycloak) GetRealm(ctx context.Context, organizationId string) (*model.Organization, error) { token := k.adminCliToken - realm, err := k.client.GetRealm(ctx, token.AccessToken, organizationId) + realm, err := k.client.GetRealm(context.Background(), token.AccessToken, organizationId) if err != nil { return nil, err } @@ -253,14 +309,13 @@ func (k *Keycloak) GetRealm(organizationId string) (*domain.Organization, error) return k.reflectOrganization(*realm), nil } -func (k *Keycloak) GetRealms() ([]*domain.Organization, error) { - ctx := context.Background() +func (k *Keycloak) GetRealms(ctx context.Context) ([]*model.Organization, error) { token := k.adminCliToken - realms, err := k.client.GetRealms(ctx, token.AccessToken) + realms, err := k.client.GetRealms(context.Background(), token.AccessToken) if err != nil { return nil, err } - organization := make([]*domain.Organization, 0) + organization := make([]*model.Organization, 0) for _, realm := range realms { organization = append(organization, k.reflectOrganization(*realm)) } @@ -268,21 +323,19 @@ func (k *Keycloak) GetRealms() ([]*domain.Organization, error) { return organization, nil } -func (k *Keycloak) UpdateRealm(organizationId string, organizationConfig domain.Organization) error { - ctx := context.Background() +func (k *Keycloak) UpdateRealm(ctx context.Context, organizationId string, organizationConfig model.Organization) error { token := k.adminCliToken realm := k.reflectRealmRepresentation(organizationConfig) - err := k.client.UpdateRealm(ctx, token.AccessToken, *realm) + err := k.client.UpdateRealm(context.Background(), token.AccessToken, *realm) if err != nil { return err } return nil } -func (k *Keycloak) DeleteRealm(organizationId string) error { - ctx := context.Background() +func (k *Keycloak) DeleteRealm(ctx context.Context, organizationId string) error { token := k.adminCliToken - err := k.client.DeleteRealm(ctx, token.AccessToken, organizationId) + err := k.client.DeleteRealm(context.Background(), token.AccessToken, organizationId) if err != nil { return err } @@ -290,24 +343,22 @@ func (k *Keycloak) DeleteRealm(organizationId string) error { return nil } -func (k *Keycloak) CreateUser(organizationId string, user *gocloak.User) error { - ctx := context.Background() +func (k *Keycloak) CreateUser(ctx context.Context, organizationId string, user *gocloak.User) (string, error) { token := k.adminCliToken user.Enabled = gocloak.BoolP(true) - _, err := k.client.CreateUser(ctx, token.AccessToken, organizationId, *user) + uuid, err := k.client.CreateUser(context.Background(), token.AccessToken, organizationId, *user) if err != nil { - return err + return "", err } - return nil + return uuid, nil } -func (k *Keycloak) GetUser(organizationId string, accountId string) (*gocloak.User, error) { - ctx := context.Background() +func (k *Keycloak) GetUser(ctx context.Context, organizationId string, accountId string) (*gocloak.User, error) { token := k.adminCliToken //TODO: this is rely on the fact that username is the same as userAccountId and unique - users, err := k.client.GetUsers(ctx, token.AccessToken, organizationId, gocloak.GetUsersParams{ + users, err := k.client.GetUsers(context.Background(), token.AccessToken, organizationId, gocloak.GetUsersParams{ Username: gocloak.StringP(accountId), }) if err != nil { @@ -321,11 +372,10 @@ func (k *Keycloak) GetUser(organizationId string, accountId string) (*gocloak.Us return users[0], nil } -func (k *Keycloak) GetUsers(organizationId string) ([]*gocloak.User, error) { - ctx := context.Background() +func (k *Keycloak) GetUsers(ctx context.Context, organizationId string) ([]*gocloak.User, error) { token := k.adminCliToken //TODO: this is rely on the fact that username is the same as userAccountId and unique - users, err := k.client.GetUsers(ctx, token.AccessToken, organizationId, gocloak.GetUsersParams{}) + users, err := k.client.GetUsers(context.Background(), token.AccessToken, organizationId, gocloak.GetUsersParams{}) if err != nil { return nil, err } @@ -337,11 +387,10 @@ func (k *Keycloak) GetUsers(organizationId string) ([]*gocloak.User, error) { return users, nil } -func (k *Keycloak) UpdateUser(organizationId string, user *gocloak.User) error { - ctx := context.Background() +func (k *Keycloak) UpdateUser(ctx context.Context, organizationId string, user *gocloak.User) error { token := k.adminCliToken user.Enabled = gocloak.BoolP(true) - err := k.client.UpdateUser(ctx, token.AccessToken, organizationId, *user) + err := k.client.UpdateUser(context.Background(), token.AccessToken, organizationId, *user) if err != nil { return err } @@ -349,40 +398,37 @@ func (k *Keycloak) UpdateUser(organizationId string, user *gocloak.User) error { return nil } -func (k *Keycloak) DeleteUser(organizationId string, userAccountId string) error { - ctx := context.Background() +func (k *Keycloak) DeleteUser(ctx context.Context, organizationId string, userAccountId string) error { token := k.adminCliToken - u, err := k.GetUser(organizationId, userAccountId) + u, err := k.GetUser(ctx, organizationId, userAccountId) if err != nil { - log.Errorf("error is :%s(%T)", err.Error(), err) + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) return httpErrors.NewNotFoundError(err, "", "") } - err = k.client.DeleteUser(ctx, token.AccessToken, organizationId, *u.ID) + err = k.client.DeleteUser(context.Background(), token.AccessToken, organizationId, *u.ID) if err != nil { return err } return nil } -func (k *Keycloak) VerifyAccessToken(token string, organizationId string) error { - ctx := context.Background() - rptResult, err := k.client.RetrospectToken(ctx, token, DefaultClientID, k.config.ClientSecret, organizationId) +func (k *Keycloak) VerifyAccessToken(ctx context.Context, token string, organizationId string) (bool, error) { + rptResult, err := k.client.RetrospectToken(context.Background(), token, DefaultClientID, k.config.ClientSecret, organizationId) if err != nil { - return err + return false, err } - if !(*rptResult.Active) { - return err + return false, nil } - return nil + + return true, nil } -func (k *Keycloak) GetSessions(userId string, organizationId string) (*[]string, error) { - ctx := context.Background() +func (k *Keycloak) GetSessions(ctx context.Context, userId string, organizationId string) (*[]string, error) { token := k.adminCliToken - sessions, err := k.client.GetUserSessions(ctx, token.AccessToken, organizationId, userId) + sessions, err := k.client.GetUserSessions(context.Background(), token.AccessToken, organizationId, userId) if err != nil { - log.Errorf("error is :%s(%T)", err.Error(), err) + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) return nil, err } @@ -394,10 +440,9 @@ func (k *Keycloak) GetSessions(userId string, organizationId string) (*[]string, return &sessionIds, nil } -func (k *Keycloak) Logout(sessionId string, organizationId string) error { - ctx := context.Background() +func (k *Keycloak) Logout(ctx context.Context, sessionId string, organizationId string) error { token := k.adminCliToken - err := k.client.LogoutUserSession(ctx, token.AccessToken, organizationId, sessionId) + err := k.client.LogoutUserSession(context.Background(), token.AccessToken, organizationId, sessionId) if err != nil { return err } @@ -405,113 +450,272 @@ func (k *Keycloak) Logout(sessionId string, organizationId string) error { return nil } -func (k *Keycloak) JoinGroup(organizationId string, userId string, groupName string) error { - ctx := context.Background() +func (k *Keycloak) JoinGroup(ctx context.Context, organizationId string, userId string, groupName string) error { token := k.adminCliToken - groups, err := k.client.GetGroups(ctx, token.AccessToken, organizationId, gocloak.GetGroupsParams{ + groups, err := k.client.GetGroups(context.Background(), token.AccessToken, organizationId, gocloak.GetGroupsParams{ Search: &groupName, }) if err != nil { - log.Error(err) + log.Error(ctx, err) return httpErrors.NewInternalServerError(err, "", "") } if len(groups) == 0 { return httpErrors.NewNotFoundError(fmt.Errorf("group not found"), "", "") } if err := k.client.AddUserToGroup(ctx, token.AccessToken, organizationId, userId, *groups[0].ID); err != nil { - log.Error(err) + log.Error(ctx, err) return httpErrors.NewInternalServerError(err, "", "") } return nil } -func (k *Keycloak) LeaveGroup(organizationId string, userId string, groupName string) error { - ctx := context.Background() +func (k *Keycloak) LeaveGroup(ctx context.Context, organizationId string, userId string, groupName string) error { token := k.adminCliToken - groups, err := k.client.GetGroups(ctx, token.AccessToken, organizationId, gocloak.GetGroupsParams{ + groups, err := k.client.GetGroups(context.Background(), token.AccessToken, organizationId, gocloak.GetGroupsParams{ Search: &groupName, }) if err != nil { - log.Error(err) + log.Error(ctx, err) return httpErrors.NewInternalServerError(err, "", "") } if len(groups) == 0 { return httpErrors.NewNotFoundError(fmt.Errorf("group not found"), "", "") } - if err := k.client.DeleteUserFromGroup(ctx, token.AccessToken, organizationId, userId, *groups[0].ID); err != nil { - log.Error(err) + if err := k.client.DeleteUserFromGroup(context.Background(), token.AccessToken, organizationId, userId, *groups[0].ID); err != nil { + log.Error(ctx, err) return httpErrors.NewInternalServerError(err, "", "") } return nil } + +func (k *Keycloak) EnsureClientRoleWithClientName(ctx context.Context, organizationId string, clientName string, roleName string) error { + token := k.adminCliToken + + clients, err := k.client.GetClients(context.Background(), token.AccessToken, organizationId, gocloak.GetClientsParams{ + ClientID: &clientName, + }) + if err != nil { + log.Error(ctx, "Getting Client is failed", err) + return err + } + + targetClient := clients[0] + + role := gocloak.Role{ + Name: gocloak.StringP(roleName), + } + + _, err = k.client.GetClientRole(context.Background(), token.AccessToken, organizationId, *targetClient.ID, roleName) + if err != nil { + if apiErr, ok := err.(*gocloak.APIError); ok { + if apiErr.Code == 404 { + _, err = k.client.CreateClientRole(context.Background(), token.AccessToken, organizationId, *targetClient.ID, role) + if err != nil { + log.Error(ctx, "Creating Client Role is failed", err) + return err + } + return nil + } else { + return err + } + } else { + return err + } + } + + return nil +} + +func (k *Keycloak) DeleteClientRoleWithClientName(ctx context.Context, organizationId string, clientName string, roleName string) error { + token := k.adminCliToken + + clients, err := k.client.GetClients(context.Background(), token.AccessToken, organizationId, gocloak.GetClientsParams{ + ClientID: &clientName, + }) + if err != nil { + log.Error(ctx, "Getting Client is failed", err) + return err + } + + targetClient := clients[0] + + roles, err := k.client.GetClientRoles(context.Background(), token.AccessToken, organizationId, *targetClient.ID, gocloak.GetRoleParams{ + Search: &roleName, + }) + if err != nil { + log.Error(ctx, "Getting Client Role is failed", err) + return err + } + + if len(roles) == 0 { + log.Warn(ctx, "Client Role not found", roleName) + return nil + } + + r, err := k.client.GetClientRole(context.Background(), token.AccessToken, organizationId, *targetClient.ID, roleName) + if err != nil { + log.Error(ctx, "Getting Client Role is failed", err) + return err + } + + if r != nil { + err = k.client.DeleteClientRole(context.Background(), token.AccessToken, organizationId, *targetClient.ID, *roles[0].ID) + if err != nil { + log.Error(ctx, "Deleting Client Role is failed", err) + return err + } + } + + return nil +} + +func (k *Keycloak) AssignClientRoleToUser(ctx context.Context, organizationId string, userId string, clientName string, roleName string) error { + token := k.adminCliToken + + clients, err := k.client.GetClients(context.Background(), token.AccessToken, organizationId, gocloak.GetClientsParams{ + ClientID: &clientName, + }) + if err != nil { + log.Error(ctx, "Getting Client is failed", err) + return err + } + if len(clients) == 0 { + log.Warn(ctx, "Client not found", clientName) + return nil + } + + targetClient := clients[0] + + roles, err := k.client.GetClientRoles(context.Background(), token.AccessToken, organizationId, *targetClient.ID, gocloak.GetRoleParams{ + Search: &roleName, + }) + if err != nil { + log.Error(ctx, "Getting Client Role is failed", err) + return err + } + + if len(roles) == 0 { + log.Warn(ctx, "Client Role not found", roleName) + return nil + } + + err = k.client.AddClientRolesToUser(context.Background(), token.AccessToken, organizationId, *targetClient.ID, userId, []gocloak.Role{*roles[0]}) + + if err != nil { + log.Error(ctx, "Assigning Client Role to User is failed", err) + return err + } + + return nil +} + +func (k *Keycloak) UnassignClientRoleToUser(ctx context.Context, organizationId string, userId string, clientName string, roleName string) error { + token := k.adminCliToken + + clients, err := k.client.GetClients(context.Background(), token.AccessToken, organizationId, gocloak.GetClientsParams{ + ClientID: &clientName, + }) + if err != nil { + log.Error(ctx, "Getting Client is failed", err) + return err + } + if len(clients) == 0 { + log.Warn(ctx, "Client not found", clientName) + return nil + } + + targetClient := clients[0] + + roles, err := k.client.GetClientRoles(context.Background(), token.AccessToken, organizationId, *targetClient.ID, gocloak.GetRoleParams{ + Search: &roleName, + }) + if err != nil { + log.Error(ctx, "Getting Client Role is failed", err) + return err + } + + if len(roles) == 0 { + log.Warn(ctx, "Client Role not found", roleName) + return nil + } + + err = k.client.DeleteClientRolesFromUser(context.Background(), token.AccessToken, organizationId, *targetClient.ID, userId, []gocloak.Role{*roles[0]}) + if err != nil { + log.Error(ctx, "Unassigning Client Role to User is failed", err) + return err + } + + return nil +} + func (k *Keycloak) ensureClientProtocolMappers(ctx context.Context, token *gocloak.JWT, realm string, clientId string, scope string, mapper gocloak.ProtocolMapperRepresentation) error { //TODO: Check current logic(if exist, do nothing) is fine - clients, err := k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ + clients, err := k.client.GetClients(context.Background(), token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { - log.Error("Getting Client is failed", err) + log.Error(ctx, "Getting Client is failed", err) return err } if clients[0].ProtocolMappers != nil { for _, protocolMapper := range *clients[0].ProtocolMappers { if *protocolMapper.Name == *mapper.Name { - log.Warn("Protocol Mapper already exists", *protocolMapper.Name) + log.Warn(ctx, "Protocol Mapper already exists", *protocolMapper.Name) return nil } } } - if _, err := k.client.CreateClientProtocolMapper(ctx, token.AccessToken, realm, *clients[0].ID, mapper); err != nil { - log.Error("Creating Client Protocol Mapper is failed", err) + if _, err := k.client.CreateClientProtocolMapper(context.Background(), token.AccessToken, realm, *clients[0].ID, mapper); err != nil { + log.Error(ctx, "Creating Client Protocol Mapper is failed", err) return err } return nil } func (k *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm string, clientId string, secret string, redirectURIs *[]string) (*gocloak.Client, error) { - keycloakClient, err := k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ + keycloakClient, err := k.client.GetClients(context.Background(), token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { - log.Error("Getting Client is failed", err) + log.Error(ctx, "Getting Client is failed", err) } if len(keycloakClient) == 0 { - _, err = k.client.CreateClient(ctx, token.AccessToken, realm, gocloak.Client{ + _, err = k.client.CreateClient(context.Background(), token.AccessToken, realm, gocloak.Client{ ClientID: gocloak.StringP(clientId), Enabled: gocloak.BoolP(true), DirectAccessGrantsEnabled: gocloak.BoolP(true), RedirectURIs: redirectURIs, }) if err != nil { - log.Error("Creating Client is failed", err) + log.Error(ctx, "Creating Client is failed", err) } - keycloakClient, err = k.client.GetClients(ctx, token.AccessToken, realm, gocloak.GetClientsParams{ + keycloakClient, err = k.client.GetClients(context.Background(), token.AccessToken, realm, gocloak.GetClientsParams{ ClientID: &clientId, }) if err != nil { - log.Error("Getting Client is failed", err) + log.Error(ctx, "Getting Client is failed", err) } } else { - err = k.client.UpdateClient(ctx, token.AccessToken, realm, gocloak.Client{ + err = k.client.UpdateClient(context.Background(), token.AccessToken, realm, gocloak.Client{ ID: keycloakClient[0].ID, Enabled: gocloak.BoolP(true), DirectAccessGrantsEnabled: gocloak.BoolP(true), RedirectURIs: redirectURIs, }) if err != nil { - log.Error("Update Client is failed", err) + log.Error(ctx, "Update Client is failed", err) } } if keycloakClient[0].Secret == nil || *keycloakClient[0].Secret != secret { - log.Warn("Client secret is not matched. Overwrite it") + log.Warn(ctx, "Client secret is not matched. Overwrite it") keycloakClient[0].Secret = gocloak.StringP(secret) - if err := k.client.UpdateClient(ctx, token.AccessToken, realm, *keycloakClient[0]); err != nil { - log.Error("Updating Client is failed", err) + if err := k.client.UpdateClient(context.Background(), token.AccessToken, realm, *keycloakClient[0]); err != nil { + log.Error(ctx, "Updating Client is failed", err) } } @@ -519,9 +723,9 @@ func (k *Keycloak) ensureClient(ctx context.Context, token *gocloak.JWT, realm s } func (k *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm string, userID string, groupID string) error { - groups, err := k.client.GetUserGroups(ctx, token.AccessToken, realm, userID, gocloak.GetGroupsParams{}) + groups, err := k.client.GetUserGroups(context.Background(), token.AccessToken, realm, userID, gocloak.GetGroupsParams{}) if err != nil { - log.Error("Getting User Groups is failed") + log.Error(ctx, "Getting User Groups is failed") } for _, group := range groups { if *group.ID == groupID { @@ -529,15 +733,15 @@ func (k *Keycloak) addUserToGroup(ctx context.Context, token *gocloak.JWT, realm } } - err = k.client.AddUserToGroup(ctx, token.AccessToken, realm, userID, groupID) + err = k.client.AddUserToGroup(context.Background(), token.AccessToken, realm, userID, groupID) if err != nil { - log.Error("Assigning User to Group is failed", err) + log.Error(ctx, "Assigning User to Group is failed", err) } return err } func (k *Keycloak) ensureUserByName(ctx context.Context, token *gocloak.JWT, realm string, userName string, password string) (*gocloak.User, error) { - user, err := k.ensureUser(ctx, token, realm, userName, password) + user, err := k.ensureUser(context.Background(), token, realm, userName, password) return user, err } @@ -545,9 +749,9 @@ func (k *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm str searchParam := gocloak.GetUsersParams{ Search: gocloak.StringP(userName), } - users, err := k.client.GetUsers(ctx, token.AccessToken, realm, searchParam) + users, err := k.client.GetUsers(context.Background(), token.AccessToken, realm, searchParam) if err != nil { - log.Error("Getting User is failed", err) + log.Error(ctx, "Getting User is failed", err) } if len(users) == 0 { user := gocloak.User{ @@ -561,14 +765,14 @@ func (k *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm str }, }, } - _, err = k.client.CreateUser(ctx, token.AccessToken, realm, user) + _, err = k.client.CreateUser(context.Background(), token.AccessToken, realm, user) if err != nil { - log.Error("Creating User is failed", err) + log.Error(ctx, "Creating User is failed", err) } - users, err = k.client.GetUsers(ctx, token.AccessToken, realm, searchParam) + users, err = k.client.GetUsers(context.Background(), token.AccessToken, realm, searchParam) if err != nil { - log.Error("Getting User is failed", err) + log.Error(ctx, "Getting User is failed", err) } } @@ -576,7 +780,7 @@ func (k *Keycloak) ensureUser(ctx context.Context, token *gocloak.JWT, realm str } func (k *Keycloak) ensureGroupByName(ctx context.Context, token *gocloak.JWT, realm string, groupName string, groupParam ...gocloak.Group) (*gocloak.Group, error) { - group, err := k.ensureGroup(ctx, token, realm, groupName) + group, err := k.ensureGroup(context.Background(), token, realm, groupName) return group, err } @@ -588,67 +792,29 @@ func (k *Keycloak) ensureGroup(ctx context.Context, token *gocloak.JWT, realm st Name: gocloak.StringP(groupName), } - groups, err := k.client.GetGroups(ctx, token.AccessToken, realm, searchParam) + groups, err := k.client.GetGroups(context.Background(), token.AccessToken, realm, searchParam) if err != nil { - log.Error("Getting Group is failed", err) + log.Error(ctx, "Getting Group is failed", err) } if len(groups) == 0 { - _, err = k.client.CreateGroup(ctx, token.AccessToken, realm, groupParam) + _, err = k.client.CreateGroup(context.Background(), token.AccessToken, realm, groupParam) if err != nil { - log.Error("Creating Group is failed", err) + log.Error(ctx, "Creating Group is failed", err) } - groups, err = k.client.GetGroups(ctx, token.AccessToken, realm, searchParam) + groups, err = k.client.GetGroups(context.Background(), token.AccessToken, realm, searchParam) if err != nil { - log.Error("Getting Group is failed", err) + log.Error(ctx, "Getting Group is failed", err) } } return groups[0], err } -func (k *Keycloak) createGroup(ctx context.Context, accessToken string, realm string, groupName string) (string, error) { - id, err := k.client.CreateGroup(ctx, accessToken, realm, gocloak.Group{Name: gocloak.StringP(groupName)}) - if err != nil { - log.Error("Creating Group is failed", err) - return "", err - } - return id, nil -} - -func (k *Keycloak) getClientByClientId(ctx context.Context, accessToken string, realm string, clientId string) ( - string, error) { - clients, err := k.client.GetClients(ctx, accessToken, realm, gocloak.GetClientsParams{ClientID: &clientId}) - if err != nil { - log.Error("Getting Client is failed", err) - return "", err - } - return *clients[0].ID, nil -} - -func (k *Keycloak) getClientRole(ctx context.Context, accessToken string, realm string, clientUuid string, - roleName string) (*gocloak.Role, error) { - role, err := k.client.GetClientRole(ctx, accessToken, realm, clientUuid, roleName) - if err != nil { - log.Error("Getting Client Role is failed", err) - return nil, err - } - return role, nil -} - -func (k *Keycloak) addClientRoleToGroup(ctx context.Context, accessToken string, realm string, clientUuid string, - groupUuid string, role *gocloak.Role) error { - err := k.client.AddClientRolesToGroup(ctx, accessToken, realm, clientUuid, groupUuid, []gocloak.Role{*role}) - if err != nil { - log.Error("Adding Client Role to Group is failed", err) - return err - } - return nil -} func (k *Keycloak) createClientProtocolMapper(ctx context.Context, accessToken string, realm string, id string, mapper gocloak.ProtocolMapperRepresentation) (string, error) { - id, err := k.client.CreateClientProtocolMapper(ctx, accessToken, realm, id, mapper) + id, err := k.client.CreateClientProtocolMapper(context.Background(), accessToken, realm, id, mapper) if err != nil { - log.Error("Creating Client Protocol Mapper is failed", err) + log.Error(ctx, "Creating Client Protocol Mapper is failed", err) return "", err } @@ -657,7 +823,7 @@ func (k *Keycloak) createClientProtocolMapper(ctx context.Context, accessToken s func (k *Keycloak) createDefaultClient(ctx context.Context, accessToken string, realm string, clientId string, clientSecret string, redirectURIs *[]string) (string, error) { - id, err := k.client.CreateClient(ctx, accessToken, realm, gocloak.Client{ + id, err := k.client.CreateClient(context.Background(), accessToken, realm, gocloak.Client{ ClientID: gocloak.StringP(clientId), DirectAccessGrantsEnabled: gocloak.BoolP(true), Enabled: gocloak.BoolP(true), @@ -665,16 +831,16 @@ func (k *Keycloak) createDefaultClient(ctx context.Context, accessToken string, }) if err != nil { - log.Error("Creating Client is failed", err) + log.Error(ctx, "Creating Client is failed", err) return "", err } - client, err := k.client.GetClient(ctx, accessToken, realm, id) + client, err := k.client.GetClient(context.Background(), accessToken, realm, id) if err != nil { - log.Error("Getting Client is failed", err) + log.Error(ctx, "Getting Client is failed", err) return "", err } client.Secret = gocloak.StringP(clientSecret) - err = k.client.UpdateClient(ctx, accessToken, realm, *client) + err = k.client.UpdateClient(context.Background(), accessToken, realm, *client) if err != nil { return "", err } @@ -682,20 +848,19 @@ func (k *Keycloak) createDefaultClient(ctx context.Context, accessToken string, return id, nil } -func (k *Keycloak) reflectOrganization(org gocloak.RealmRepresentation) *domain.Organization { - return &domain.Organization{ +func (k *Keycloak) reflectOrganization(org gocloak.RealmRepresentation) *model.Organization { + return &model.Organization{ ID: *org.ID, Name: *org.Realm, } } -func (k *Keycloak) reflectRealmRepresentation(org domain.Organization) *gocloak.RealmRepresentation { +func (k *Keycloak) reflectRealmRepresentation(org model.Organization) *gocloak.RealmRepresentation { return &gocloak.RealmRepresentation{ Realm: gocloak.StringP(org.Name), } } -// var defaultProtocolTksMapper = make([]gocloak.ProtocolMapperRepresentation, 3) var defaultProtocolTksMapper = []gocloak.ProtocolMapperRepresentation{ { Name: gocloak.StringP("org"), @@ -734,15 +899,32 @@ var defaultProtocolTksMapper = []gocloak.ProtocolMapperRepresentation{ "userinfo.token.claim": "false", }, }, + { + Name: gocloak.StringP("project-role"), + Protocol: gocloak.StringP("openid-connect"), + ProtocolMapper: gocloak.StringP("oidc-usermodel-client-role-mapper"), + Config: &map[string]string{ + "access.token.claim": "true", + "id.token.claim": "false", + "userinfo.token.claim": "false", + + "claim.name": "project-role", + "jsonType.label": "String", + "multivalued": "true", + + "usermodel.clientRoleMapping.clientId": "tks", + "usermodel.clientRoleMapping.role_prefix": "", + }, + }, } func defaultRealmSetting(realmId string) gocloak.RealmRepresentation { return gocloak.RealmRepresentation{ Realm: gocloak.StringP(realmId), Enabled: gocloak.BoolP(true), - AccessTokenLifespan: gocloak.IntP(accessTokenLifespan), - SsoSessionIdleTimeout: gocloak.IntP(ssoSessionIdleTimeout), - SsoSessionMaxLifespan: gocloak.IntP(ssoSessionMaxLifespan), + AccessTokenLifespan: gocloak.IntP(AccessTokenLifespan), + SsoSessionIdleTimeout: gocloak.IntP(SsoSessionIdleTimeout), + SsoSessionMaxLifespan: gocloak.IntP(SsoSessionMaxLifespan), } } diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go deleted file mode 100644 index d202a55d..00000000 --- a/internal/kubernetes/kubernetes.go +++ /dev/null @@ -1,187 +0,0 @@ -package kubernetes - -import ( - "context" - "fmt" - "strings" - - "github.com/spf13/viper" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/discovery" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - - clientcmd "k8s.io/client-go/tools/clientcmd" - - "github.com/openinfradev/tks-api/pkg/log" -) - -func getAdminConfig() (*rest.Config, error) { - kubeconfigPath := viper.GetString("kubeconfig-path") - if kubeconfigPath == "" { - log.Info("Use in-cluster config") - config, err := rest.InClusterConfig() - if err != nil { - log.Error("Failed to load incluster kubeconfig") - return nil, err - } - return config, nil - } else { - config, err := clientcmd.BuildConfigFromFlags("", viper.GetString("kubeconfig-path")) - if err != nil { - log.Error("Failed to local kubeconfig") - return nil, err - } - return config, nil - } -} - -func GetClientAdminCluster() (*kubernetes.Clientset, error) { - config, err := getAdminConfig() - if err != nil { - log.Error("Failed to load kubeconfig") - return nil, err - } - - // create the clientset - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - return clientset, nil -} - -func GetAwsSecret() (awsAccessKeyId string, awsSecretAccessKey string, err error) { - clientset, err := GetClientAdminCluster() - if err != nil { - return "", "", err - } - - secrets, err := clientset.CoreV1().Secrets("argo").Get(context.TODO(), "awsconfig-secret", metav1.GetOptions{}) - if err != nil { - log.Error(err) - return "", "", err - } - - strCredentials := string(secrets.Data["credentials"][:]) - arr := strings.Split(strCredentials, "\n") - if len(arr) < 3 { - return "", "", err - } - - fmt.Sscanf(arr[1], "aws_access_key_id = %s", &awsAccessKeyId) - fmt.Sscanf(arr[2], "aws_secret_access_key = %s", &awsSecretAccessKey) - - return -} - -func GetAwsAccountIdSecret() (awsAccountId string, err error) { - clientset, err := GetClientAdminCluster() - if err != nil { - return "", err - } - - secrets, err := clientset.CoreV1().Secrets("argo").Get(context.TODO(), "tks-aws-user", metav1.GetOptions{}) - if err != nil { - log.Error(err) - return "", err - } - - awsAccountId = string(secrets.Data["account_id"][:]) - return -} - -func GetKubeConfig(clusterId string) ([]byte, error) { - clientset, err := GetClientAdminCluster() - if err != nil { - return nil, err - } - - secrets, err := clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-user-kubeconfig", metav1.GetOptions{}) - if err != nil { - log.Error(err) - return nil, err - } - - return secrets.Data["value"], nil -} - -func GetClientFromClusterId(clusterId string) (*kubernetes.Clientset, error) { - clientset, err := GetClientAdminCluster() - if err != nil { - return nil, err - } - - secrets, err := clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-kubeconfig", metav1.GetOptions{}) - if err != nil { - log.Error(err) - return nil, err - } - - config_user, err := clientcmd.RESTConfigFromKubeConfig(secrets.Data["value"]) - if err != nil { - log.Error(err) - return nil, err - } - clientset_user, err := kubernetes.NewForConfig(config_user) - if err != nil { - return nil, err - } - - return clientset_user, nil -} - -func GetKubernetesVserionByClusterId(clusterId string) (string, error) { - clientset, err := GetClientAdminCluster() - if err != nil { - return "", err - } - - secrets, err := clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-kubeconfig", metav1.GetOptions{}) - if err != nil { - log.Error(err) - return "", err - } - - config_user, err := clientcmd.RESTConfigFromKubeConfig(secrets.Data["value"]) - if err != nil { - log.Error(err) - return "", err - } - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config_user) - if err != nil { - log.Error(err) - return "", err - } - - information, err := discoveryClient.ServerVersion() - if err != nil { - log.Error("Error while fetching server version information", err) - return "", err - } - - return information.GitVersion, nil -} - -func GetKubernetesVserion() (string, error) { - config, err := getAdminConfig() - if err != nil { - log.Error("Failed to load kubeconfig") - return "", err - } - - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - log.Error(err) - return "", err - } - - information, err := discoveryClient.ServerVersion() - if err != nil { - log.Error("Error while fetching server version information", err) - return "", err - } - - return information.GitVersion, nil -} diff --git a/internal/mail/content.go b/internal/mail/content.go index d8ac55c9..06787c9b 100644 --- a/internal/mail/content.go +++ b/internal/mail/content.go @@ -2,17 +2,18 @@ package mail import ( "bytes" + "context" "html/template" "github.com/openinfradev/tks-api/pkg/log" ) -func MakeVerityIdentityMessage(to, code string) (*MessageInfo, error) { +func MakeVerityIdentityMessage(ctx context.Context, to, code string) (*MessageInfo, error) { subject := "[TKS] [인증번호:" + code + "] 인증번호가 발급되었습니다." tmpl, err := template.ParseFS(templateFS, "contents/authcode.html") if err != nil { - log.Errorf("failed to parse template, %v", err) + log.Errorf(ctx, "failed to parse template, %v", err) return nil, err } @@ -20,13 +21,13 @@ func MakeVerityIdentityMessage(to, code string) (*MessageInfo, error) { var tpl bytes.Buffer if err := tmpl.Execute(&tpl, data); err != nil { - log.Errorf("failed to execute template, %v", err) + log.Errorf(ctx, "failed to execute template, %v", err) return nil, err } m := &MessageInfo{ From: from, - To: to, + To: []string{to}, Subject: subject, Body: tpl.String(), } @@ -34,12 +35,12 @@ func MakeVerityIdentityMessage(to, code string) (*MessageInfo, error) { return m, nil } -func MakeTemporaryPasswordMessage(to, organizationId, accountId, randomPassword string) (*MessageInfo, error) { +func MakeTemporaryPasswordMessage(ctx context.Context, to, organizationId, accountId, randomPassword string) (*MessageInfo, error) { subject := "[TKS] 임시 비밀번호가 발급되었습니다." tmpl, err := template.ParseFS(templateFS, "contents/temporary_password.html") if err != nil { - log.Errorf("failed to parse template, %v", err) + log.Errorf(ctx, "failed to parse template, %v", err) return nil, err } @@ -47,13 +48,13 @@ func MakeTemporaryPasswordMessage(to, organizationId, accountId, randomPassword var tpl bytes.Buffer if err := tmpl.Execute(&tpl, data); err != nil { - log.Errorf("failed to execute template, %v", err) + log.Errorf(ctx, "failed to execute template, %v", err) return nil, err } m := &MessageInfo{ From: from, - To: to, + To: []string{to}, Subject: subject, Body: tpl.String(), } @@ -62,13 +63,14 @@ func MakeTemporaryPasswordMessage(to, organizationId, accountId, randomPassword } func MakeGeneratingOrganizationMessage( + ctx context.Context, organizationId string, organizationName string, to string, userAccountId string, randomPassword string) (*MessageInfo, error) { subject := "[TKS] 조직이 생성되었습니다." tmpl, err := template.ParseFS(templateFS, "contents/organization_creation.html") if err != nil { - log.Errorf("failed to parse template, %v", err) + log.Errorf(ctx, "failed to parse template, %v", err) return nil, err } @@ -82,16 +84,45 @@ func MakeGeneratingOrganizationMessage( var tpl bytes.Buffer if err := tmpl.Execute(&tpl, data); err != nil { - log.Errorf("failed to execute template, %v", err) + log.Errorf(ctx, "failed to execute template, %v", err) return nil, err } m := &MessageInfo{ From: from, - To: to, + To: []string{to}, Subject: subject, Body: tpl.String(), } return m, nil } + +func MakeSystemNotificationMessage(ctx context.Context, organizationId string, title string, content string, to []string) (*MessageInfo, error) { + tmpl, err := template.ParseFS(templateFS, "contents/system_notification.html") + if err != nil { + log.Errorf(ctx, "failed to parse template, %v", err) + return nil, err + } + + data := map[string]string{ + "OrganizationId": organizationId, + "Title": title, + "Content": content, + } + + var tpl bytes.Buffer + if err := tmpl.Execute(&tpl, data); err != nil { + log.Errorf(ctx, "failed to execute template, %v", err) + return nil, err + } + + m := &MessageInfo{ + From: from, + To: to, + Subject: title, + Body: tpl.String(), + } + + return m, nil +} diff --git a/internal/mail/contents/authcode.html b/internal/mail/contents/authcode.html index 09f79474..ab429c31 100644 --- a/internal/mail/contents/authcode.html +++ b/internal/mail/contents/authcode.html @@ -2,106 +2,136 @@ - 이메일인증 안내 - - - -
+ 이메일 인증 안내 + +
- - - - - - - - -
SKT Enterprise
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
- 이메일 인증 안내 -
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
- 고객님께서 입력하신 이메일 주소 인증을 위해 아래 6자리 인증번호를 -
화면에 입력해 주세요.
이메일 인증코드
{{.AuthCode}}
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
SKT Enterprise
+ + + + + + + + + + + + + + + + + + + + + + - - - - + + +
+ 이메일 인증 안내 +
+ 안녕하세요.
+ 항상 저희 SKT Enterprise를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 고객님께서 입력하신 이메일 주소 인증을 위해 아래 6자리 인증번호를 화면에 입력해 주세요. +
+ 이메일 인증 코드 + +
+ + + + + + +
{{.AuthCode}}
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + + + +
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
+ 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
+ 감사합니다. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 본 메일은 발신 전용 메일로, 회신 되지 않습니다. +
+ 우편번호: 04539 서울특별시 중구 을지로 65 (을지로 2가) SK T-타워 SK텔레콤(주) 대표이사 : 유영상
+ COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED. +
-
+
- - - - + + + + \ No newline at end of file diff --git a/internal/mail/contents/organization_creation.html b/internal/mail/contents/organization_creation.html index 988411d7..de0ad1f8 100644 --- a/internal/mail/contents/organization_creation.html +++ b/internal/mail/contents/organization_creation.html @@ -1,129 +1,260 @@ - - - - - 조직 생성 - - - -
- - - - - - - - - - - - - - - -
SKT Enterprise
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ​ - - - - - - - - - - - - -
- 조직 생성 안내 -
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
- 조직이 생성되었습니다. -
아래의 정보를 이용하여 로그인 해주시기 바랍니다.
로그인 정보
- • 조직코드: {{.OrganizationId}} -
• 아이디: {{.Id}} -
• 비밀번호: {{.Password}} -
조직 정보
- • 조직이름: {{.OrganizationName}} -
• 관리자 이름: {{.AdminName}} -
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
-
-
- - + + + + + + + 조직 생성 + + + +
+ + + + + + + + + + + + + + + + + + + + +
SKT Enterprise
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 조직 생성 안내 +
+ 안녕하세요.
+ 항상 저희 SKT Enterprise를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 조직이 생성되었습니다.
+ 아래의 정보를 이용하여 로그인 해주시기 바랍니다. +
+ 로그인 정보 +
+ + + + + + + + + + + + + + + + + + + +
+ 조직코드 + + {{.OrganizationId}} +
+ 아이디 + + {{.Id}} +
+ 비밀번호 + + {{.Password}} +
+
+ 조직 정보 +
+ + + + + + + + + + + + +
+ 조직 이름 + + {{.OrganizationName}} +
+ 관리자 이름 + + {{.AdminName}} +
+
+ 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
+ 감사합니다. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 본 메일은 발신 전용 메일로, 회신 되지 않습니다. +
+ 우편번호: 04539 서울특별시 중구 을지로 65 (을지로 2가) SK T-타워 SK텔레콤(주) 대표이사 : 유영상
+ COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED. +
+
+
+
+ + diff --git a/internal/mail/contents/system_notification.html b/internal/mail/contents/system_notification.html new file mode 100644 index 00000000..1e222cba --- /dev/null +++ b/internal/mail/contents/system_notification.html @@ -0,0 +1,161 @@ + + + + + + + 시스템 알림 + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ SKT Enterprise +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + {{.Title}} + +
+ 안녕하세요.
+ 항상 저희 SKT Enterprise를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 아래 내용으로 시스템 알림이 발생 되었습니다.
+ 내용 확인 후 조치 해주시기 바랍니다. +
+ 내용 +
+ + + + +
+ {{.Content}} +
+
+ 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
+ 감사합니다. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 본 메일은 발신 전용 메일로, 회신 되지 않습니다. +
+ 우편번호: 04539 서울특별시 중구 을지로 65 (을지로 2가) SK T-타워 SK텔레콤(주) 대표이사 : 유영상
+ COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED. +
+
+
+
+ + + diff --git a/internal/mail/contents/temporary_password.html b/internal/mail/contents/temporary_password.html index 654ee0d0..5f4ed8b3 100644 --- a/internal/mail/contents/temporary_password.html +++ b/internal/mail/contents/temporary_password.html @@ -2,112 +2,182 @@ - 임시 비밀번호 발급 + 임시 비밀번호 발급 안내 - - -
+
+ + + - + + + + + + - - - - + + - - - - +
SKT EnterpriseSKT Enterprise
- +
+ - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 임시 비밀번호 발급 안내 + + + 임시 비밀번호 발급 안내 +
안녕하세요.
항상 저희 TKS Cloud Service를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
- 임시 비밀번호가 발급되었습니다. -
로그인 후 비밀번호를 변경하여 사용해 주시기 바랍니다.
조직 코드: {{.OrganizationId}}
아이디: {{.AccountId}}
임시 비밀번호
{{.TemporaryPassword}}
더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
감사합니다.
-
- - - - - + - - - - + + - - - + + + + - + + + + - - - + - - - - + + + + + +
+ 안녕하세요.
+ 항상 저희 SKT Enterprise를 사랑해 주시고 성원해 주시는 고객님께 감사드립니다.
+ 로그인 후 비밀번호를 변경하여 사용해 주시기 바랍니다. +
본 메일은 발신 전용 메일로, 회신 되지 않습니다.
+ 임시 비밀번호 +
+ + + + + + + + + + + + + + + + + + + +
+ 조직코드 + + {{.OrganizationId}} +
+ 아이디 + + {{.AccountId}} +
+ 비밀번호 + + {{.TemporaryPassword}} +
+
우편번호: 04539 서울특별시중구을지로65 (을지로2가) SK T-타워 SK텔레콤㈜ 대표이사 : 유영상
COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED.
+ 더욱 편리한 서비스를 제공하기 위해 항상 최선을 다하겠습니다.
+ 감사합니다. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 본 메일은 발신 전용 메일로, 회신 되지 않습니다. +
+ 우편번호: 04539 서울특별시 중구 을지로 65 (을지로 2가) SK T-타워 SK텔레콤(주) 대표이사 : 유영상
+ COPYRIGHT SK TELECOM CO., LTD. ALL RIGHTS RESERVED. +
+
- + \ No newline at end of file diff --git a/internal/mail/ses.go b/internal/mail/ses.go index 98e9a429..787e1324 100644 --- a/internal/mail/ses.go +++ b/internal/mail/ses.go @@ -20,10 +20,10 @@ type AwsMailer struct { message *MessageInfo } -func (a *AwsMailer) SendMail() error { +func (a *AwsMailer) SendMail(ctx context.Context) error { input := &awsSes.SendEmailInput{ Destination: &types.Destination{ - ToAddresses: []string{a.message.To}, + ToAddresses: a.message.To, }, Message: &types.Message{ Subject: &types.Content{ @@ -39,16 +39,16 @@ func (a *AwsMailer) SendMail() error { } if _, err := a.client.SendEmail(context.Background(), input); err != nil { - log.Errorf("failed to send email, %v", err) + log.Errorf(ctx, "failed to send email, %v", err) return err } return nil } -func initialize() error { +func initialize(ctx context.Context) error { if viper.GetString("aws-access-key-id") != "" || viper.GetString("aws-secret-access-key") != "" { - log.Warn("aws access key information is used on env. Be aware of security") + log.Warn(ctx, "aws access key information is used on env. Be aware of security") } if viper.GetString("aws-access-key-id") != "" { err := os.Setenv("AWS_ACCESS_KEY_ID", viper.GetString("aws-access-key-id")) diff --git a/internal/mail/smtp.go b/internal/mail/smtp.go index 12a401ec..ef673310 100644 --- a/internal/mail/smtp.go +++ b/internal/mail/smtp.go @@ -2,6 +2,7 @@ package mail import ( "bytes" + "context" "crypto/tls" "embed" "errors" @@ -27,12 +28,12 @@ var ( ) type Mailer interface { - SendMail() error + SendMail(ctx context.Context) error } type MessageInfo struct { From string - To string + To []string Subject string Body string } @@ -47,22 +48,25 @@ type SmtpMailer struct { message *MessageInfo } -func (s *SmtpMailer) SendMail() error { +func (s *SmtpMailer) SendMail(ctx context.Context) error { s.client.SetHeader("From", s.message.From) - s.client.SetHeader("To", s.message.To) s.client.SetHeader("Subject", s.message.Subject) s.client.SetBody("text/html", s.message.Body) - d := NewDialer(s.Host, s.Port, s.Username, s.Password) - if err := d.DialAndSend(s.client); err != nil { - log.Errorf("failed to send email, %v", err) - return err + + for _, to := range s.message.To { + s.client.SetHeader("To", to) + + if err := d.DialAndSend(s.client); err != nil { + log.Errorf(ctx, "failed to send email, %v", err) + continue + } } return nil } -func Initialize() error { +func Initialize(ctx context.Context) error { mailProvider = viper.GetString("mail-provider") if mailProvider != "smtp" { mailProvider = "aws" @@ -70,8 +74,8 @@ func Initialize() error { switch mailProvider { case "aws": - if err := initialize(); err != nil { - log.Errorf("aws config initialize error, %v", err) + if err := initialize(ctx); err != nil { + log.Errorf(ctx, "aws config initialize error, %v", err) return err } from = "tks-dev@sktelecom.com" @@ -83,23 +87,23 @@ func Initialize() error { from = viper.GetString("smtp-from-email") if host == "" { - log.Error("smtp-host is not set") + log.Error(ctx, "smtp-host is not set") return fmt.Errorf("smtp-host is not set") } if port == 0 { - log.Error("smtp-port is not set") + log.Error(ctx, "smtp-port is not set") return fmt.Errorf("smtp-port is not set") } if username == "" { - log.Error("smtp-username is not set") + log.Error(ctx, "smtp-username is not set") return fmt.Errorf("smtp-username is not set") } if password == "" { - log.Error("smtp-password is not set") + log.Error(ctx, "smtp-password is not set") return fmt.Errorf("smtp-password is not set") } if from == "" { - log.Error("smtp-from-email is not set") + log.Error(ctx, "smtp-from-email is not set") return fmt.Errorf("smtp-from-email is not set") } @@ -115,11 +119,11 @@ func New(m *MessageInfo) Mailer { switch mailProvider { case "aws": mailer = NewAwsMailer(m) - log.Infof("aws ses mailer, %v", mailer) + log.Infof(context.TODO(), "aws ses mailer, %v", mailer) case "smtp": mailer = NewSmtpMailer(m) - log.Infof("smtp mailer, %v", mailer) + log.Infof(context.TODO(), "smtp mailer, %v", mailer) } return mailer diff --git a/internal/middleware/audit/audit-map.go b/internal/middleware/audit/audit-map.go new file mode 100644 index 00000000..7e1e4f5a --- /dev/null +++ b/internal/middleware/audit/audit-map.go @@ -0,0 +1,205 @@ +package audit + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" +) + +type fnAudit = func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) + +var auditMap = map[internalApi.Endpoint]fnAudit{ + internalApi.CreateStack: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateStackRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("스택 [%s]을 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("스택 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.CreateProject: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateProjectRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("프로젝트 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("프로젝트 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.CreateCloudAccount: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateCloudAccountRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("클라우드 어카운트 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("클라우드 어카운트 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.DeleteCloudAccount: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + if isSuccess(statusCode) { + output := domain.DeleteCloudAccountResponse{} + if err := json.Unmarshal(out, &output); err != nil { + log.Error(ctx, err) + } + return fmt.Sprintf("클라우드어카운트 [%s]를 삭제하였습니다.", output.Name), "" + } else { + return "클라우드어카운트를 삭제하는데 실패하였습니다. ", errorText(ctx, out) + } + }, internalApi.DeleteForceCloudAccount: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + if isSuccess(statusCode) { + output := domain.DeleteCloudAccountResponse{} + if err := json.Unmarshal(out, &output); err != nil { + log.Error(ctx, err) + } + return fmt.Sprintf("클라우드어카운트 [%s]를 강제 삭제하였습니다.", output.Name), "" + } else { + return "클라우드어카운트를 강제 삭제하는데 실패하였습니다. ", errorText(ctx, out) + } + }, internalApi.CreateUser: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateUserRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("사용자 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("사용자 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.DeleteUser: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + if isSuccess(statusCode) { + output := domain.DeleteUserResponse{} + if err := json.Unmarshal(out, &output); err != nil { + log.Error(ctx, err) + } + return fmt.Sprintf("사용자 [%s]를 삭제하였습니다.", output.AccountId), "" + } else { + return "사용자를 삭제하는데 실패하였습니다. ", errorText(ctx, out) + } + }, internalApi.Admin_CreateOrganization: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateOrganizationRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("조직 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("조직 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.Admin_DeleteOrganization: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + if isSuccess(statusCode) { + output := domain.DeleteOrganizationResponse{} + if err := json.Unmarshal(out, &output); err != nil { + log.Error(ctx, err) + } + return fmt.Sprintf("조직 [ID:%s]를 삭제하였습니다.", output.ID), "" + } else { + return "조직을 삭제하는데 실패하였습니다. ", errorText(ctx, out) + } + }, internalApi.CreateAppServeApp: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateAppServeAppRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("앱서빙 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("앱서빙 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.Admin_CreateStackTemplate: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateStackTemplateRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("스택 템플릿 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("스택 템플릿 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.Admin_CreateUser: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateUserRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("어드민 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("어드민 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.Admin_CreatePolicyTemplate: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreatePolicyTemplateRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("폴리시템플릿 [%s]를 생성하였습니다.", input.TemplateName), "" + } else { + return fmt.Sprintf("폴리시템플릿 [%s]을 생성하는데 실패하였습니다.", input.TemplateName), errorText(ctx, out) + } + }, internalApi.CreateSystemNotificationRule: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreateSystemNotificationRuleRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("시스템알림설정 [%s]를 생성하였습니다.", input.Name), "" + } else { + return fmt.Sprintf("시스템알림설정 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) + } + }, internalApi.DeleteSystemNotificationRule: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + if isSuccess(statusCode) { + output := domain.DeleteSystemNotificationRuleResponse{} + if err := json.Unmarshal(out, &output); err != nil { + log.Error(ctx, err) + } + return fmt.Sprintf("시스템알림설정 [%s]를 삭제하였습니다.", output.Name), "" + } else { + return "시스템알림설정을 삭제하는데 실패하였습니다. ", errorText(ctx, out) + } + }, internalApi.CreatePolicyTemplate: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreatePolicyTemplateRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("정책템플릿 [%s]를 생성하였습니다.", input.TemplateName), "" + } else { + return fmt.Sprintf("정책템플릿 [%s]을 생성하는데 실패하였습니다.", input.TemplateName), errorText(ctx, out) + } + }, internalApi.CreatePolicy: func(ctx context.Context, out []byte, in []byte, statusCode int) (message string, description string) { + input := domain.CreatePolicyRequest{} + if err := json.Unmarshal(in, &input); err != nil { + log.Error(ctx, err) + } + if isSuccess(statusCode) { + return fmt.Sprintf("정책 [%s]를 생성하였습니다.", input.PolicyName), "" + } else { + return fmt.Sprintf("정책 [%s]을 생성하는데 실패하였습니다.", input.PolicyName), errorText(ctx, out) + } + }, +} + +func errorText(ctx context.Context, out []byte) string { + var e httpErrors.RestError + if err := json.NewDecoder(bytes.NewBuffer(out)).Decode(&e); err != nil { + log.Error(ctx, err) + return "" + } + return e.Text() +} + +func isSuccess(statusCode int) bool { + if statusCode >= 200 && statusCode < 300 { + return true + } + return false +} diff --git a/internal/middleware/audit/audit.go b/internal/middleware/audit/audit.go new file mode 100644 index 00000000..189232ec --- /dev/null +++ b/internal/middleware/audit/audit.go @@ -0,0 +1,112 @@ +package audit + +import ( + "bytes" + "io" + "net" + "net/http" + + "github.com/gorilla/mux" + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/middleware/logging" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/log" +) + +type Interface interface { + WithAudit(endpoint internalApi.Endpoint, handler http.Handler) http.Handler +} + +type defaultAudit struct { + repo repository.IAuditRepository + userRepo repository.IUserRepository +} + +func NewDefaultAudit(repo repository.Repository) *defaultAudit { + return &defaultAudit{ + repo: repo.Audit, + userRepo: repo.User, + } +} + +func (a *defaultAudit) WithAudit(endpoint internalApi.Endpoint, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, ok := request.UserFrom(r.Context()) + if !ok { + log.Error(r.Context(), "Invalid user token") + return + } + userId := user.GetUserId() + + lrw := logging.NewLoggingResponseWriter(w) + handler.ServeHTTP(lrw, r) + statusCode := lrw.GetStatusCode() + + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + organizationId = user.GetOrganizationId() + } + + message, description := "", "" + if fn, ok := auditMap[endpoint]; ok { + // workarround pingtoken + if endpoint != internalApi.VerifyToken { + body, err := io.ReadAll(r.Body) + if err != nil { + log.Error(r.Context(), err) + } + message, description = fn(r.Context(), lrw.GetBody().Bytes(), body, statusCode) + r.Body = io.NopCloser(bytes.NewBuffer(body)) + + u, err := a.userRepo.GetByUuid(r.Context(), userId) + if err != nil { + log.Error(r.Context(), err) + return + } + + userRoles := "" + for i, role := range u.Roles { + if i > 0 { + userRoles = userRoles + "," + } + userRoles = userRoles + role.Name + } + + dto := model.Audit{ + OrganizationId: organizationId, + OrganizationName: u.Organization.Name, + Group: internalApi.ApiMap[endpoint].Group, + Message: message, + Description: description, + ClientIP: GetClientIpAddress(w, r), + UserId: &u.ID, + UserAccountId: u.AccountId, + UserName: u.Name, + UserRoles: userRoles, + } + if _, err := a.repo.Create(r.Context(), dto); err != nil { + log.Error(r.Context(), err) + } + } + } + + }) +} + +var X_FORWARDED_FOR = "X-Forwarded-For" + +func GetClientIpAddress(w http.ResponseWriter, r *http.Request) string { + xforward := r.Header.Get(X_FORWARDED_FOR) + if xforward != "" { + return xforward + } + + clientAddr, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + return clientAddr + } + return "" +} diff --git a/internal/middleware/auth/authenticator/authenticator.go b/internal/middleware/auth/authenticator/authenticator.go index 07390f74..8f8ab1b3 100644 --- a/internal/middleware/auth/authenticator/authenticator.go +++ b/internal/middleware/auth/authenticator/authenticator.go @@ -2,6 +2,7 @@ package authenticator import ( "fmt" + "github.com/openinfradev/tks-api/internal/repository" "net/http" internalHttp "github.com/openinfradev/tks-api/internal/delivery/http" @@ -19,23 +20,38 @@ type Request interface { } type defaultAuthenticator struct { - auth Request + kcAuth Request + customAuth Request + repo repository.Repository } -func NewAuthenticator(kc Request) *defaultAuthenticator { +func NewAuthenticator(kc Request, repo repository.Repository, c Request) *defaultAuthenticator { return &defaultAuthenticator{ - auth: kc, + kcAuth: kc, + repo: repo, + customAuth: c, } } func (a *defaultAuthenticator) WithAuthentication(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - resp, ok, err := a.auth.AuthenticateRequest(r) + resp, ok, err := a.kcAuth.AuthenticateRequest(r) if !ok { - log.Error(err) - internalHttp.ErrorJSON(w, r, httpErrors.NewUnauthorizedError(err, "", "")) + log.Error(r.Context(), err) + internalHttp.ErrorJSON(w, r, err) return } + if err != nil { + internalHttp.ErrorJSON(w, r, err) + return + } + + _, ok, err = a.customAuth.AuthenticateRequest(r) + if !ok { + internalHttp.ErrorJSON(w, r, err) + return + } + r = r.WithContext(request.WithUser(r.Context(), resp.User)) _, ok = request.UserFrom(r.Context()) diff --git a/internal/middleware/auth/authenticator/custom/authenticator.go b/internal/middleware/auth/authenticator/custom/authenticator.go new file mode 100644 index 00000000..f92f218a --- /dev/null +++ b/internal/middleware/auth/authenticator/custom/authenticator.go @@ -0,0 +1,80 @@ +package custom + +import ( + "fmt" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "net/http" + "strings" +) + +type CustomAuthenticator struct { + repo repository.Repository +} + +func NewCustomAuthenticator(repo repository.Repository) *CustomAuthenticator { + return &CustomAuthenticator{ + repo: repo, + } +} +func (a *CustomAuthenticator) AuthenticateRequest(r *http.Request) (*authenticator.Response, bool, error) { + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + if authHeader == "" { + return nil, false, fmt.Errorf("authorizer header is invalid") + } + parts := strings.SplitN(authHeader, " ", 3) + if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { + return nil, false, fmt.Errorf("authorizer header is invalid") + } + token := parts[1] + + if len(token) == 0 { + // The space before the token case + if len(parts) == 3 { + log.Warn(r.Context(), "the provided Authorization header contains extra space before the bearer token, and is ignored") + } + return nil, false, fmt.Errorf("token is empty") + } + + parsedToken, err := helper.StringToTokenWithoutVerification(token) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + claims, err := helper.RetrieveClaims(parsedToken) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + organizationId, ok := claims["organization"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("organizationId is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + userId, ok := claims["sub"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("userId is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + expiredTime, err := a.repo.Auth.GetExpiredTimeOnToken(r.Context(), organizationId, userId) + if expiredTime == nil { + return nil, true, nil + } + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + iat, ok := claims["iat"].(float64) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("iat is not found"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + + if int64(iat) < expiredTime.ExpiredTime.Unix() { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("token is changed"), "A_UNUSABLE_TOKEN", "토큰이 변경되었습니다.") + } + + return nil, true, nil +} diff --git a/internal/middleware/auth/authenticator/keycloak/keycloak.go b/internal/middleware/auth/authenticator/keycloak/keycloak.go index 89c516c2..dcc649a8 100644 --- a/internal/middleware/auth/authenticator/keycloak/keycloak.go +++ b/internal/middleware/auth/authenticator/keycloak/keycloak.go @@ -2,10 +2,11 @@ package keycloak import ( "fmt" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/pkg/httpErrors" "net/http" "strings" - jwtWithouKey "github.com/dgrijalva/jwt-go" "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" @@ -39,7 +40,7 @@ func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authentic if len(token) == 0 { // The space before the token case if len(parts) == 3 { - log.Warn("the provided Authorization header contains extra space before the bearer token, and is ignored") + log.Warn(r.Context(), "the provided Authorization header contains extra space before the bearer token, and is ignored") } return nil, false, fmt.Errorf("token is empty") } @@ -48,75 +49,89 @@ func (a *keycloakAuthenticator) AuthenticateRequest(r *http.Request) (*authentic } func (a *keycloakAuthenticator) AuthenticateToken(r *http.Request, token string) (*authenticator.Response, bool, error) { - parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(token, jwtWithouKey.MapClaims{}) + parsedToken, err := helper.StringToTokenWithoutVerification(token) if err != nil { - return nil, false, err + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - if parsedToken.Method.Alg() != "RS256" { - return nil, false, fmt.Errorf("invalid token") - } - - if parsedToken.Claims.Valid() != nil { - return nil, false, fmt.Errorf("invalid token") + claims, err := helper.RetrieveClaims(parsedToken) + if err != nil { + return nil, false, httpErrors.NewUnauthorizedError(err, "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - organizationId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["organization"].(string) + organizationId, ok := claims["organization"].(string) if !ok { - return nil, false, fmt.Errorf("organization is not found in token") + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("organization is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - if err := a.kc.VerifyAccessToken(token, organizationId); err != nil { - log.Errorf("failed to verify access token: %v", err) - return nil, false, err + isActive, err := a.kc.VerifyAccessToken(r.Context(), token, organizationId) + if err != nil { + log.Errorf(r.Context(), "failed to verify access token: %v", err) + return nil, false, httpErrors.NewUnauthorizedError(err, "C_INTERNAL_ERROR", "") + } + if !isActive { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("token is deactivated"), "A_EXPIRED_TOKEN", "토큰이 만료되었습니다.") } - roleProjectMapping := make(map[string]string) - for _, role := range parsedToken.Claims.(jwtWithouKey.MapClaims)["tks-role"].([]interface{}) { - slice := strings.Split(role.(string), "@") - if len(slice) != 2 { - log.Errorf("invalid tks-role format: %v", role) + // tks role extraction + roleOrganizationMapping := make(map[string]string) + if roles, ok := claims["tks-role"]; !ok { + log.Errorf(r.Context(), "tks-role is not found in token") + + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("tks-role is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } else { + for _, role := range roles.([]interface{}) { + slice := strings.Split(role.(string), "@") + if len(slice) != 2 { + log.Errorf(r.Context(), "invalid tks-role format: %v", role) - return nil, false, fmt.Errorf("invalid tks-role format") + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("invalid tks-role format"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + // key is projectName and value is roleName + roleOrganizationMapping[slice[1]] = slice[0] } - // key is projectName and value is roleName - roleProjectMapping[slice[1]] = slice[0] - } - userId, err := uuid.Parse(parsedToken.Claims.(jwtWithouKey.MapClaims)["sub"].(string)) - if err != nil { - log.Errorf("failed to verify access token: %v", err) - return nil, false, err } - requestSessionId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["sid"].(string) - if !ok { - return nil, false, fmt.Errorf("session id is not found in token") + // project role extraction + projectIds := make([]string, 0) + roleProjectMapping := make(map[string]string) + if roles, ok := claims["project-role"]; ok { + for _, role := range roles.([]interface{}) { + slice := strings.Split(role.(string), "@") + if len(slice) != 2 { + log.Errorf(r.Context(), "invalid project-role format: %v", role) + + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("invalid project-role format"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") + } + // key is projectId and value is roleName + roleProjectMapping[slice[1]] = slice[0] + projectIds = append(projectIds, slice[1]) + } } - sessionIds, err := a.kc.GetSessions(userId.String(), organizationId) + userId, err := uuid.Parse(claims["sub"].(string)) if err != nil { - log.Errorf("failed to get sessions: %v", err) + log.Errorf(r.Context(), "failed to verify access token: %v", err) - return nil, false, err - } - if len(*sessionIds) == 0 { - return nil, false, fmt.Errorf("invalid session") + return nil, false, httpErrors.NewUnauthorizedError(err, "C_INTERNAL_ERROR", "") } - var matched bool = false - for _, id := range *sessionIds { - if id == requestSessionId { - matched = true - break - } + requestSessionId, ok := claims["sid"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("session id is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } - if !matched { - return nil, false, fmt.Errorf("invalid session") + + userAccountId, ok := claims["preferred_username"].(string) + if !ok { + return nil, false, httpErrors.NewUnauthorizedError(fmt.Errorf("preferred_username is not found in token"), "A_INVALID_TOKEN", "토큰이 유효하지 않습니다.") } userInfo := &user.DefaultInfo{ - UserId: userId, - OrganizationId: organizationId, - RoleProjectMapping: roleProjectMapping, + UserId: userId, + AccountId: userAccountId, + OrganizationId: organizationId, + ProjectIds: projectIds, + RoleOrganizationMapping: roleOrganizationMapping, + RoleProjectMapping: roleProjectMapping, } //r = r.WithContext(request.WithToken(r.Context(), token)) *r = *(r.WithContext(request.WithToken(r.Context(), token))) diff --git a/internal/middleware/auth/authorizer/authorizer.go b/internal/middleware/auth/authorizer/authorizer.go index 79093ed0..1ce71727 100644 --- a/internal/middleware/auth/authorizer/authorizer.go +++ b/internal/middleware/auth/authorizer/authorizer.go @@ -19,7 +19,9 @@ func NewDefaultAuthorization(repo repository.Repository) *defaultAuthorization { repo: repo, } d.addFilters(PasswordFilter) - d.addFilters(RBACFilter) + //d.addFilters(RBACFilter) + //d.addFilters(RBACFilterWithEndpoint) + d.addFilters(AdminApiFilter) return d } diff --git a/internal/middleware/auth/authorizer/password.go b/internal/middleware/auth/authorizer/password.go index 47b474fe..d9dc083c 100644 --- a/internal/middleware/auth/authorizer/password.go +++ b/internal/middleware/auth/authorizer/password.go @@ -20,7 +20,7 @@ func PasswordFilter(handler http.Handler, repo repository.Repository) http.Handl return } - storedUser, err := repo.User.GetByUuid(requestUserInfo.GetUserId()) + storedUser, err := repo.User.GetByUuid(r.Context(), requestUserInfo.GetUserId()) if err != nil { internalHttp.ErrorJSON(w, r, err) return @@ -53,3 +53,17 @@ func urlContains(urls []string, url string) bool { } return false } + +// +//func isBypassEndpoint(endpoint internalApi.Endpoint) bool { +// switch endpoint { +// case internalApi.Login: +// case internalApi.PingToken: +// case internalApi.FindId: +// case internalApi.FindPassword: +// case internalApi.VerifyIdentityForLostId: +// case internalApi.VerifyIdentityForLostPassword: +// return true +// } +// return false +//} diff --git a/internal/middleware/auth/authorizer/rbac.go b/internal/middleware/auth/authorizer/rbac.go index b4ff8a6f..9813558e 100644 --- a/internal/middleware/auth/authorizer/rbac.go +++ b/internal/middleware/auth/authorizer/rbac.go @@ -21,24 +21,24 @@ func RBACFilter(handler http.Handler, repo repository.Repository) http.Handler { internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found"), "", "")) return } - role := requestUserInfo.GetRoleProjectMapping()[requestUserInfo.GetOrganizationId()] + organizationRole := requestUserInfo.GetRoleOrganizationMapping()[requestUserInfo.GetOrganizationId()] // TODO: 추후 tks-admin role 수정 필요 - if role == "tks-admin" { + if organizationRole == "tks-admin" { handler.ServeHTTP(w, r) return } vars := mux.Vars(r) // Organization Filter - if role == "admin" || role == "user" { + if organizationRole == "admin" || organizationRole == "user" { if orgId, ok := vars["organizationId"]; ok { if orgId != requestUserInfo.GetOrganizationId() { internalHttp.ErrorJSON(w, r, httpErrors.NewForbiddenError(fmt.Errorf("permission denied"), "", "")) return } } else { - log.Warn("RBACFilter: organizationId not found. Passing through unsafely.") + log.Warn(r.Context(), "RBACFilter: organizationId not found. Passing through unsafely.") } } @@ -46,7 +46,7 @@ func RBACFilter(handler http.Handler, repo repository.Repository) http.Handler { if strings.HasPrefix(r.URL.Path, internal.API_PREFIX+internal.API_VERSION+"/organizations/"+requestUserInfo.GetOrganizationId()+"/user") { switch r.Method { case http.MethodPost, http.MethodPut, http.MethodDelete: - if role != "admin" { + if organizationRole != "admin" { internalHttp.ErrorJSON(w, r, httpErrors.NewForbiddenError(fmt.Errorf("permission denied"), "", "")) return } @@ -56,3 +56,114 @@ func RBACFilter(handler http.Handler, repo repository.Repository) http.Handler { handler.ServeHTTP(w, r) }) } + +func RBACFilterWithEndpoint(handler http.Handler, repo repository.Repository) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + //requestEndpointInfo, ok := request.EndpointFrom(r.Context()) + //if !ok { + // internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("endpoint not found"), "", "")) + // return + //} + // + //requestUserInfo, ok := request.UserFrom(r.Context()) + //if !ok { + // internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found"), "", "")) + // return + //} + // + //// if requestEndpointInfo.String() is one of ProjectEndpoints, print true + //if internalApi.ApiMap[requestEndpointInfo].Group == "Project" && requestEndpointInfo != internalApi.CreateProject { + // log.Infof("RBACFilterWithEndpoint: %s is ProjectEndpoint", requestEndpointInfo.String()) + // vars := mux.Vars(r) + // var projectRole string + // if projectId, ok := vars["projectId"]; ok { + // projectRole = requestUserInfo.GetRoleProjectMapping()[projectId] + // } else { + // log.Warn("RBACFilterWithEndpoint: projectId not found. Passing through unsafely.") + // } + // if !internalRole.IsRoleAllowed(requestEndpointInfo, internalRole.StrToRole(projectRole)) { + // internalHttp.ErrorJSON(w, r, httpErrors.NewForbiddenError(fmt.Errorf("permission denied"), "", "")) + // return + // + // } + //} + + handler.ServeHTTP(w, r) + }) +} + +func AdminApiFilter(handler http.Handler, repo repository.Repository) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found"), "", "")) + return + } + + endpointInfo, ok := request.EndpointFrom(r.Context()) + if !ok { + internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("endpoint not found"), "", "")) + return + } + + if strings.HasPrefix(endpointInfo.String(), "Admin") { + if requestUserInfo.GetOrganizationId() != "master" { + internalHttp.ErrorJSON(w, r, httpErrors.NewForbiddenError(fmt.Errorf("permission denied"), "", "")) + return + } + } + + handler.ServeHTTP(w, r) + }) +} + +func RequestOrganizationValidationFilter(handler http.Handler, repo repository.Repository) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestUserInfo, ok := request.UserFrom(r.Context()) + if !ok { + internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("user not found"), "", "")) + return + } + + vars := mux.Vars(r) + organizationId, ok := vars["organizationId"] + if !ok { + //internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("organizationId not found"), "", "")) + //return + log.Warn(r.Context(), "RequestOrganizationValidationFilter: organizationId not found. Passing through unsafely.") + } + if organizationId != requestUserInfo.GetOrganizationId() { + internalHttp.ErrorJSON(w, r, httpErrors.NewForbiddenError(fmt.Errorf("permission denied"), "", "")) + return + } + + handler.ServeHTTP(w, r) + }) +} + +//type pair struct { +// regexp string +// method string +//} +// +//var LeaderPair = []pair{ +// {`/organizations/o[A-Za-z0-9]{8}/projects(?:\?.*)?$`, http.MethodPost}, +// {`/organizations/o[A-Za-z0-9]{8}/projects(?:\?.*)?$`, http.MethodGet}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}(?:\?.*)?$`, http.MethodGet}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}(?:\?.*)?$`, http.MethodPut}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}(?:\?.*)?$`, http.MethodDelete}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/members(?:\?.*)?$`, http.MethodPost}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/members(?:\?.*)?$`, http.MethodGet}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/members/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?:\?.*)?$`, http.MethodDelete}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/members/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/role(?:\?.*)?$`, http.MethodPut}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/namespace(?:\?.*)?$`, http.MethodPost}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/namespace(?:\?.*)?$`, http.MethodGet}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/namespace/n[A-Za-z0-9]{8}(?:\?.*)?$`, http.MethodGet}, +// {`/organizations/o[A-Za-z0-9]{8}/projects/p[A-Za-z0-9]{8}/namespace/n[A-Za-z0-9]{8}(?:\?.*)?$`, http.MethodDelete}, +//} +//var roleApiMapper = make(map[string][]pair) +// +//func projectFilter(url string, method string, userInfo user.Info) bool { +// +// return true +//} diff --git a/internal/middleware/auth/middleware.go b/internal/middleware/auth/middleware.go deleted file mode 100644 index f9e6e977..00000000 --- a/internal/middleware/auth/middleware.go +++ /dev/null @@ -1,27 +0,0 @@ -package auth - -import ( - "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" - "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" - "net/http" -) - -type authMiddleware struct { - authenticator authenticator.Interface - authorizer authorizer.Interface -} - -func NewAuthMiddleware(authenticator authenticator.Interface, - authorizer authorizer.Interface) *authMiddleware { - ret := &authMiddleware{ - authenticator: authenticator, - authorizer: authorizer, - } - return ret -} - -func (m *authMiddleware) Handle(handle http.Handler) http.Handler { - handler := m.authorizer.WithAuthorization(handle) - handler = m.authenticator.WithAuthentication(handler) - return handler -} diff --git a/internal/middleware/auth/request/context.go b/internal/middleware/auth/request/context.go index ecefd7b9..d1b8c4c8 100644 --- a/internal/middleware/auth/request/context.go +++ b/internal/middleware/auth/request/context.go @@ -3,6 +3,7 @@ package request import ( "context" + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" "github.com/openinfradev/tks-api/internal/middleware/auth/user" ) @@ -12,6 +13,8 @@ const ( userKey key = iota userToken sessionKey + endpointKey + auditKey ) func WithValue(parent context.Context, key, val interface{}) context.Context { @@ -44,3 +47,21 @@ func SessionFrom(ctx context.Context) (string, bool) { token, ok := ctx.Value(sessionKey).(string) return token, ok } + +func WithEndpoint(parent context.Context, endpoint internalApi.Endpoint) context.Context { + return WithValue(parent, endpointKey, endpoint) +} + +func EndpointFrom(ctx context.Context) (internalApi.Endpoint, bool) { + endpoint, ok := ctx.Value(endpointKey).(internalApi.Endpoint) + return endpoint, ok +} + +func WithAudit(parent context.Context, audit string) context.Context { + return WithValue(parent, auditKey, audit) +} + +func AuditFrom(ctx context.Context) (string, bool) { + audit, ok := ctx.Value(auditKey).(string) + return audit, ok +} diff --git a/internal/middleware/auth/requestRecoder/requestRecoder.go b/internal/middleware/auth/requestRecoder/requestRecoder.go new file mode 100644 index 00000000..34b61944 --- /dev/null +++ b/internal/middleware/auth/requestRecoder/requestRecoder.go @@ -0,0 +1,33 @@ +package requestRecoder + +import ( + "fmt" + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + internalHttp "github.com/openinfradev/tks-api/internal/delivery/http" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "net/http" +) + +type Interface interface { + WithRequestRecoder(endpoint internalApi.Endpoint, handler http.Handler) http.Handler +} + +type defaultRequestRecoder struct { +} + +func NewDefaultRequestRecoder() *defaultRequestRecoder { + return &defaultRequestRecoder{} +} + +func (a *defaultRequestRecoder) WithRequestRecoder(endpoint internalApi.Endpoint, handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r = r.WithContext(request.WithEndpoint(r.Context(), endpoint)) + _, ok := request.EndpointFrom(r.Context()) + if !ok { + internalHttp.ErrorJSON(w, r, httpErrors.NewInternalServerError(fmt.Errorf("endpoint not found"), "", "")) + return + } + handler.ServeHTTP(w, r) + }) +} diff --git a/internal/middleware/auth/role/default.go b/internal/middleware/auth/role/default.go new file mode 100644 index 00000000..70f1f256 --- /dev/null +++ b/internal/middleware/auth/role/default.go @@ -0,0 +1,313 @@ +package role + +import internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + +var defaults = []*defaultPermission{ + &defaultPermissionOfAdminInMaster, + &defaultPermissionOfUserInMaster, + &defaultPermissionOfAdmin, + &defaultPermissionOfUser, + &defaultLeaderPermission, + &defaultMemberPermission, + &defaultViewerPermission, +} + +func getDefaultPermissions() []*defaultPermission { + return defaults +} + +type defaultPermission struct { + role Role + permissions *[]internalApi.Endpoint +} + +var defaultPermissionOfAdminInMaster = defaultPermission{ + role: Admin, + permissions: &[]internalApi.Endpoint{}, +} + +var defaultPermissionOfUserInMaster = defaultPermission{ + role: User, + permissions: &[]internalApi.Endpoint{}, +} + +var defaultPermissionOfAdmin = defaultPermission{ + role: Admin, + permissions: &[]internalApi.Endpoint{ + // Auth + internalApi.Logout, + internalApi.RefreshToken, + internalApi.VerifyToken, + + // User + internalApi.CreateUser, + internalApi.ListUser, + internalApi.GetUser, + internalApi.DeleteUser, + internalApi.UpdateUser, + internalApi.ResetPassword, + internalApi.CheckId, + internalApi.CheckEmail, + + // MyProfile + internalApi.GetMyProfile, + internalApi.UpdateMyProfile, + internalApi.UpdateMyPassword, + internalApi.RenewPasswordExpiredDate, + internalApi.DeleteMyProfile, + + // Organization + internalApi.Admin_CreateOrganization, + internalApi.Admin_DeleteOrganization, + internalApi.GetOrganizations, + internalApi.GetOrganization, + internalApi.UpdateOrganization, + + // Cluster + internalApi.UpdatePrimaryCluster, + internalApi.CreateCluster, + internalApi.GetClusters, + internalApi.ImportCluster, + internalApi.GetCluster, + internalApi.DeleteCluster, + internalApi.GetClusterSiteValues, + internalApi.InstallCluster, + internalApi.CreateBootstrapKubeconfig, + internalApi.GetBootstrapKubeconfig, + internalApi.GetNodes, + + // Appgroup + internalApi.CreateAppgroup, + internalApi.GetAppgroups, + internalApi.GetAppgroup, + internalApi.DeleteAppgroup, + internalApi.GetApplications, + internalApi.CreateApplication, + + // AppServeApp + internalApi.CreateAppServeApp, + internalApi.GetAppServeApps, + internalApi.GetNumOfAppsOnStack, + internalApi.GetAppServeApp, + internalApi.GetAppServeAppLatestTask, + internalApi.IsAppServeAppExist, + internalApi.IsAppServeAppNameExist, + internalApi.DeleteAppServeApp, + internalApi.UpdateAppServeApp, + internalApi.UpdateAppServeAppStatus, + internalApi.UpdateAppServeAppEndpoint, + internalApi.RollbackAppServeApp, + + // CloudAccount + internalApi.GetCloudAccounts, + internalApi.CreateCloudAccount, + internalApi.CheckCloudAccountName, + internalApi.CheckAwsAccountId, + internalApi.GetCloudAccount, + internalApi.UpdateCloudAccount, + internalApi.DeleteCloudAccount, + internalApi.DeleteForceCloudAccount, + internalApi.GetResourceQuota, + + // Dashboard + internalApi.GetChartsDashboard, + internalApi.GetChartDashboard, + internalApi.GetStacksDashboard, + internalApi.GetResourcesDashboard, + + // Stack + internalApi.GetStacks, + internalApi.CreateStack, + internalApi.CheckStackName, + internalApi.GetStack, + internalApi.UpdateStack, + internalApi.DeleteStack, + internalApi.GetStackKubeConfig, + internalApi.GetStackStatus, + internalApi.SetFavoriteStack, + internalApi.DeleteFavoriteStack, + internalApi.InstallStack, + + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +// TODO: check-up the permission of User +var defaultPermissionOfUser = defaultPermission{ + role: User, + permissions: &[]internalApi.Endpoint{ + // Auth + internalApi.Logout, + internalApi.RefreshToken, + internalApi.VerifyToken, + + // User + internalApi.ListUser, + internalApi.GetUser, + internalApi.CheckId, + internalApi.CheckEmail, + + // MyProfile + internalApi.GetMyProfile, + internalApi.UpdateMyProfile, + internalApi.UpdateMyPassword, + internalApi.RenewPasswordExpiredDate, + internalApi.DeleteMyProfile, + + // Organization + internalApi.GetOrganizations, + internalApi.GetOrganization, + + // Cluster + internalApi.GetClusters, + internalApi.GetCluster, + internalApi.GetClusterSiteValues, + internalApi.GetBootstrapKubeconfig, + internalApi.GetNodes, + + // Appgroup + internalApi.CreateAppgroup, + internalApi.GetAppgroups, + internalApi.GetAppgroup, + internalApi.DeleteAppgroup, + internalApi.GetApplications, + internalApi.CreateApplication, + + // AppServeApp + internalApi.CreateAppServeApp, + internalApi.GetAppServeApps, + internalApi.GetNumOfAppsOnStack, + internalApi.GetAppServeApp, + internalApi.GetAppServeAppLatestTask, + internalApi.IsAppServeAppExist, + internalApi.IsAppServeAppNameExist, + internalApi.DeleteAppServeApp, + internalApi.UpdateAppServeApp, + internalApi.UpdateAppServeAppStatus, + internalApi.UpdateAppServeAppEndpoint, + internalApi.RollbackAppServeApp, + + // CloudAccount + internalApi.GetCloudAccounts, + internalApi.GetCloudAccount, + internalApi.GetResourceQuota, + + // StackTemplate + internalApi.Admin_GetStackTemplates, + internalApi.Admin_GetStackTemplate, + + // Dashboard + internalApi.GetChartsDashboard, + internalApi.GetChartDashboard, + internalApi.GetStacksDashboard, + internalApi.GetResourcesDashboard, + + // SystemNotification + internalApi.CreateSystemNotification, + internalApi.GetSystemNotifications, + internalApi.GetSystemNotification, + internalApi.DeleteSystemNotification, + internalApi.UpdateSystemNotification, + internalApi.CreateSystemNotificationAction, + + // Stack + internalApi.GetStacks, + internalApi.GetStack, + internalApi.GetStackKubeConfig, + internalApi.GetStackStatus, + internalApi.SetFavoriteStack, + internalApi.DeleteFavoriteStack, + + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultLeaderPermission = defaultPermission{ + role: leader, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.CreateProject, + internalApi.GetProjects, + internalApi.GetProject, + internalApi.UpdateProject, + internalApi.DeleteProject, + internalApi.AddProjectMember, + internalApi.GetProjectMembers, + internalApi.RemoveProjectMember, + internalApi.UpdateProjectMemberRole, + internalApi.CreateProjectNamespace, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.DeleteProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultMemberPermission = defaultPermission{ + role: member, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.GetProjects, + internalApi.GetProject, + internalApi.GetProjectMembers, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} + +var defaultViewerPermission = defaultPermission{ + role: viewer, + permissions: &[]internalApi.Endpoint{ + // Project + internalApi.GetProjects, + internalApi.GetProject, + internalApi.GetProjectMembers, + internalApi.GetProjectNamespaces, + internalApi.GetProjectNamespace, + internalApi.SetFavoriteProject, + internalApi.SetFavoriteProjectNamespace, + internalApi.UnSetFavoriteProject, + internalApi.UnSetFavoriteProjectNamespace, + }, +} diff --git a/internal/middleware/auth/role/role.go b/internal/middleware/auth/role/role.go new file mode 100644 index 00000000..986de7d1 --- /dev/null +++ b/internal/middleware/auth/role/role.go @@ -0,0 +1,61 @@ +package role + +import internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + +type Role string + +const ( + Admin Role = "admin" + User Role = "user" + leader Role = "leader" + member Role = "member" + viewer Role = "viewer" +) + +func init() { + RBAC = make(map[internalApi.Endpoint]map[Role]struct{}) + + defaultPermissions := getDefaultPermissions() + registerRole(defaultPermissions...) +} + +func (r Role) String() string { + return string(r) +} + +func registerRole(dps ...*defaultPermission) { + for _, dp := range dps { + for _, endpoint := range *dp.permissions { + if RBAC[endpoint] == nil { + RBAC[endpoint] = make(map[Role]struct{}) + } + RBAC[endpoint][dp.role] = struct{}{} + } + } +} + +var RBAC map[internalApi.Endpoint]map[Role]struct{} + +func StrToRole(role string) Role { + switch role { + case "admin": + return Admin + case "user": + return User + case "leader": + return leader + default: + return "" + } +} +func IsRoleAllowed(endpoint internalApi.Endpoint, role Role) bool { + if RBAC[endpoint] == nil { + return false + } + + if _, ok := RBAC[endpoint][role]; ok { + return true + } + + return false +} diff --git a/internal/middleware/auth/user/user.go b/internal/middleware/auth/user/user.go index d853f295..f7117f2a 100644 --- a/internal/middleware/auth/user/user.go +++ b/internal/middleware/auth/user/user.go @@ -7,31 +7,47 @@ import ( // Info describes a user that has been authenticated to the system. type Info interface { GetUserId() uuid.UUID + GetAccountId() string GetOrganizationId() string + GetRoleOrganizationMapping() map[string]string GetRoleProjectMapping() map[string]string } // DefaultInfo provides a simple user information exchange object // for components that implement the UserInfo interface. type DefaultInfo struct { - UserId uuid.UUID - OrganizationId string - RoleProjectMapping map[string]string + UserId uuid.UUID + AccountId string + OrganizationId string + ProjectIds []string + RoleOrganizationMapping map[string]string + RoleProjectMapping map[string]string } func (i *DefaultInfo) GetUserId() uuid.UUID { return i.UserId } +func (i *DefaultInfo) GetAccountId() string { + return i.AccountId +} + func (i *DefaultInfo) GetOrganizationId() string { return i.OrganizationId } -// GetRoleGroupMapping key is project name, value is role name +func (i *DefaultInfo) GetProjectId() []string { + return i.ProjectIds +} + func (i *DefaultInfo) GetRoleProjectMapping() map[string]string { return i.RoleProjectMapping } +func (i *DefaultInfo) GetRoleOrganizationMapping() map[string]string { + return i.RoleOrganizationMapping +} + // well-known user and group names const ( TksAdminRole = "tks_admin" diff --git a/internal/middleware/logging/logging.go b/internal/middleware/logging/logging.go new file mode 100644 index 00000000..31e2c456 --- /dev/null +++ b/internal/middleware/logging/logging.go @@ -0,0 +1,42 @@ +package logging + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal" + "github.com/openinfradev/tks-api/pkg/log" +) + +const MAX_LOG_LEN = 1000 + +func LoggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + r = r.WithContext(context.WithValue(ctx, internal.ContextKeyRequestID, uuid.New().String())) + + log.Infof(r.Context(), fmt.Sprintf("***** START [%s %s] ***** ", r.Method, r.RequestURI)) + + body, err := io.ReadAll(r.Body) + if err == nil { + log.Infof(r.Context(), fmt.Sprintf("REQUEST BODY : %s", bytes.NewBuffer(body).String())) + } + r.Body = io.NopCloser(bytes.NewBuffer(body)) + lrw := NewLoggingResponseWriter(w) + + next.ServeHTTP(lrw, r) + + statusCode := lrw.GetStatusCode() + + if len(lrw.GetBody().String()) > MAX_LOG_LEN { + log.Infof(r.Context(), "[API_RESPONSE] [%d][%s][%s]", statusCode, http.StatusText(statusCode), lrw.GetBody().String()[:MAX_LOG_LEN-1]) + } else { + log.Infof(r.Context(), "[API_RESPONSE] [%d][%s][%s]", statusCode, http.StatusText(statusCode), lrw.GetBody().String()) + } + log.Infof(r.Context(), "***** END [%s %s] *****", r.Method, r.RequestURI) + }) +} diff --git a/internal/middleware/logging/respose-writer.go b/internal/middleware/logging/respose-writer.go new file mode 100644 index 00000000..97ef7d23 --- /dev/null +++ b/internal/middleware/logging/respose-writer.go @@ -0,0 +1,35 @@ +package logging + +import ( + "bytes" + "net/http" +) + +type loggingResponseWriter struct { + http.ResponseWriter + statusCode int + body bytes.Buffer +} + +func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { + var buf bytes.Buffer + return &loggingResponseWriter{w, http.StatusOK, buf} +} + +func (lrw *loggingResponseWriter) WriteHeader(code int) { + lrw.statusCode = code + lrw.ResponseWriter.WriteHeader(code) +} + +func (lrw *loggingResponseWriter) Write(buf []byte) (int, error) { + lrw.body.Write(buf) + return lrw.ResponseWriter.Write(buf) +} + +func (lrw *loggingResponseWriter) GetBody() *bytes.Buffer { + return &lrw.body +} + +func (lrw *loggingResponseWriter) GetStatusCode() int { + return lrw.statusCode +} diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go new file mode 100644 index 00000000..554afc2a --- /dev/null +++ b/internal/middleware/middleware.go @@ -0,0 +1,53 @@ +package middleware + +import ( + "net/http" + + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/middleware/audit" + "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" + "github.com/openinfradev/tks-api/internal/middleware/auth/requestRecoder" +) + +type Middleware struct { + authenticator authenticator.Interface + authorizer authorizer.Interface + requestRecoder requestRecoder.Interface + audit audit.Interface +} + +func NewMiddleware(authenticator authenticator.Interface, + authorizer authorizer.Interface, + requestRecoder requestRecoder.Interface, + audit audit.Interface) *Middleware { + ret := &Middleware{ + authenticator: authenticator, + authorizer: authorizer, + requestRecoder: requestRecoder, + audit: audit, + } + return ret +} + +func (m *Middleware) Handle(endpoint internalApi.Endpoint, handle http.Handler) http.Handler { + // pre-handler + preHandler := m.authorizer.WithAuthorization(handle) + // TODO: this is a temporary solution. check if this is the right place to put audit middleware + preHandler = m.audit.WithAudit(endpoint, preHandler) + preHandler = m.requestRecoder.WithRequestRecoder(endpoint, preHandler) + preHandler = m.authenticator.WithAuthentication(preHandler) + + // post-handler + // append post-handler below + // TODO: this is a temporary solution. check if this is the right place to put audit middleware + + // emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + // postHandler := m.audit.WithAudit(endpoint, emptyHandler) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + preHandler.ServeHTTP(w, r) + + // postHandler.ServeHTTP(w, r) + }) +} diff --git a/internal/model/app-group.go b/internal/model/app-group.go new file mode 100644 index 00000000..72cd1f88 --- /dev/null +++ b/internal/model/app-group.go @@ -0,0 +1,36 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/datatypes" + "gorm.io/gorm" +) + +// Models +type AppGroup struct { + gorm.Model + + ID domain.AppGroupId `gorm:"primarykey"` + AppGroupType domain.AppGroupType + ClusterId domain.ClusterId + Name string + Description string + WorkflowId string + Status domain.AppGroupStatus + StatusDesc string + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} + +type Application struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid"` + AppGroupId domain.AppGroupId + Endpoint string + Metadata datatypes.JSON + Type domain.ApplicationType +} diff --git a/internal/model/app-serve-app.go b/internal/model/app-serve-app.go new file mode 100644 index 00000000..4d86c603 --- /dev/null +++ b/internal/model/app-serve-app.go @@ -0,0 +1,61 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" + "time" +) + +type AppServeApp struct { + ID string `gorm:"primarykey" json:"id"` + Name string `gorm:"index" json:"name"` // application name + Namespace string `json:"namespace"` // application namespace + OrganizationId string `json:"organizationId"` // contractId is a contract ID which this app belongs to + ProjectId string `json:"projectId"` // project ID which this app belongs to + Type string `json:"type"` // type (build/deploy/all) + AppType string `json:"appType"` // appType (spring/springboot) + EndpointUrl string `json:"endpointUrl"` // endpoint URL of deployed app + PreviewEndpointUrl string `json:"previewEndpointUrl"` // preview svc endpoint URL in B/G deployment + TargetClusterId string `json:"targetClusterId"` // target cluster to which the app is deployed + TargetClusterName string `gorm:"-:all" json:"targetClusterName"` // target cluster name + Status string `gorm:"index" json:"status"` // status is status of deployed app + GrafanaUrl string `json:"grafanaUrl"` // grafana dashboard URL for deployed app + Description string `json:"description"` // description for application + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type AppServeAppTask struct { + ID string `gorm:"primarykey" json:"id"` + AppServeAppId string `gorm:"not null" json:"appServeAppId"` // ID for appServeApp that this task belongs to + Version string `json:"version"` // application version + Status string `json:"status"` // status is app status + Output string `json:"output"` // output for task result + ArtifactUrl string `json:"artifactUrl"` // URL of java app artifact (Eg, Jar) + ImageUrl string `json:"imageUrl"` // URL of built image for app + ExecutablePath string `json:"executablePath"` // Executable path of app image + Profile string `json:"profile"` // java app profile + AppConfig string `json:"appConfig"` // java app config + AppSecret string `json:"appSecret"` // java app secret + ExtraEnv string `json:"extraEnv"` // env variable list for java app + Port string `json:"port"` // java app port + ResourceSpec string `json:"resourceSpec"` // resource spec of app pod + HelmRevision int32 `gorm:"default:0" json:"helmRevision"` // revision of deployed helm release + Strategy string `json:"strategy"` // deployment strategy (eg, rolling-update) + RollbackVersion string `json:"rollbackVersion"` // rollback target version + PvEnabled bool `json:"pvEnabled"` + PvStorageClass string `json:"pvStorageClass"` + PvAccessMode string `json:"pvAccessMode"` + PvSize string `json:"pvSize"` + PvMountPath string `json:"pvMountPath"` + AvailableRollback bool `gorm:"-:all" json:"availableRollback"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` // createdAt is a creation timestamp for the application + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +func (t *AppServeAppTask) BeforeCreate(tx *gorm.DB) (err error) { + t.ID = uuid.New().String() + return nil +} diff --git a/internal/model/audit.go b/internal/model/audit.go new file mode 100644 index 00000000..c8e8a5ed --- /dev/null +++ b/internal/model/audit.go @@ -0,0 +1,23 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Models +type Audit struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + OrganizationId string + OrganizationName string + Group string + Message string + Description string + ClientIP string + UserId *uuid.UUID `gorm:"type:uuid"` + UserAccountId string + UserName string + UserRoles string +} diff --git a/internal/model/auth.go b/internal/model/auth.go new file mode 100644 index 00000000..b2866816 --- /dev/null +++ b/internal/model/auth.go @@ -0,0 +1,23 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" + "time" +) + +// Models +type ExpiredTokenTime struct { + gorm.Model + + OrganizationId string `gorm:"index:idx_org_id_subject_id,unique;not null"` + SubjectId string `gorm:"index:idx_org_id_subject_id,unique;not null"` + ExpiredTime time.Time +} + +type CacheEmailCode struct { + gorm.Model + + UserId uuid.UUID `gorm:"not null"` + Code string `gorm:"type:varchar(6);not null"` +} diff --git a/internal/model/cloud-account.go b/internal/model/cloud-account.go new file mode 100644 index 00000000..98bf3f9c --- /dev/null +++ b/internal/model/cloud-account.go @@ -0,0 +1,33 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +// Models +type CloudAccount struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId"` + Name string `gorm:"index"` + Description string `gorm:"index"` + Resource string + CloudService string + WorkflowId string + Status domain.CloudAccountStatus + StatusDesc string + AwsAccountId string + AccessKeyId string `gorm:"-:all"` + SecretAccessKey string `gorm:"-:all"` + SessionToken string `gorm:"-:all"` + Clusters int `gorm:"-:all"` + CreatedIAM bool + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} diff --git a/internal/model/cluster.go b/internal/model/cluster.go new file mode 100644 index 00000000..0166546a --- /dev/null +++ b/internal/model/cluster.go @@ -0,0 +1,81 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +// Models +type Cluster struct { + gorm.Model + + ID domain.ClusterId `gorm:"primarykey"` + Name string `gorm:"index"` + CloudService string `gorm:"default:AWS"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId"` + Description string `gorm:"index"` + WorkflowId string + Status domain.ClusterStatus + StatusDesc string + CloudAccountId *uuid.UUID + CloudAccount CloudAccount `gorm:"foreignKey:CloudAccountId"` + StackTemplateId uuid.UUID + StackTemplate StackTemplate `gorm:"foreignKey:StackTemplateId"` + Favorites *[]ClusterFavorite + ClusterType domain.ClusterType `gorm:"default:0"` + ByoClusterEndpointHost string + ByoClusterEndpointPort int + IsStack bool `gorm:"default:false"` + TksCpNode int + TksCpNodeMax int + TksCpNodeType string + TksInfraNode int + TksInfraNodeMax int + TksInfraNodeType string + TksUserNode int + TksUserNodeMax int + TksUserNodeType string + Kubeconfig []byte `gorm:"-:all"` + PolicyIds []string `gorm:"-:all"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` + Policies []Policy `gorm:"many2many:policy_target_clusters"` +} + +func (m *Cluster) SetDefaultConf() { + m.TksCpNodeMax = m.TksCpNode + + if m.TksInfraNode == 0 { + m.TksInfraNode = 3 + } + m.TksInfraNodeMax = m.TksInfraNode + + if m.TksUserNode == 0 { + m.TksUserNode = 1 + } + m.TksUserNodeMax = m.TksUserNode + + if m.TksCpNodeType == "" { + m.TksCpNodeType = "t3.xlarge" + } + if m.TksInfraNodeType == "" { + m.TksInfraNodeType = "t3.2xlarge" + } + if m.TksUserNodeType == "" { + m.TksUserNodeType = "t3.large" + } +} + +type ClusterFavorite struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid"` + ClusterId domain.ClusterId + Cluster Cluster `gorm:"foreignKey:ClusterId"` + UserId uuid.UUID `gorm:"type:uuid"` + User User `gorm:"foreignKey:UserId"` +} diff --git a/internal/model/dashboard.go b/internal/model/dashboard.go new file mode 100644 index 00000000..cb60fac8 --- /dev/null +++ b/internal/model/dashboard.go @@ -0,0 +1,21 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +type Dashboard struct { + gorm.Model + ID uuid.UUID `gorm:"primarykey;type:uuid"` + OrganizationId string `gorm:"type:varchar(36)"` + UserId uuid.UUID + Key string + Content string + IsAdmin bool `gorm:"default:false"` +} + +func (d *Dashboard) BeforeCreate(tx *gorm.DB) (err error) { + d.ID = uuid.New() + return nil +} diff --git a/internal/model/end-point.go b/internal/model/end-point.go new file mode 100644 index 00000000..912fa0ce --- /dev/null +++ b/internal/model/end-point.go @@ -0,0 +1,11 @@ +package model + +import ( + "time" +) + +type Endpoint struct { + Name string `gorm:"primary_key;type:text;not null;unique" json:"name"` + Group string `gorm:"type:text;" json:"group"` + CreatedAt time.Time +} diff --git a/internal/model/organization.go b/internal/model/organization.go new file mode 100644 index 00000000..ada78d11 --- /dev/null +++ b/internal/model/organization.go @@ -0,0 +1,31 @@ +package model + +import ( + "gorm.io/gorm" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" +) + +type Organization struct { + gorm.Model + + ID string `gorm:"primarykey;type:varchar(36);not null"` + Name string + Description string + Phone string + PrimaryClusterId string + WorkflowId string + Status domain.OrganizationStatus + StatusDesc string + CreatorId *uuid.UUID `gorm:"type:uuid"` + StackTemplates []StackTemplate `gorm:"many2many:stack_template_organizations;"` + StackTemplateIds []uuid.UUID `gorm:"-:all"` + PolicyTemplates []PolicyTemplate `gorm:"many2many:policy_template_permitted_organizations;"` + PolicyTemplateIds []uuid.UUID `gorm:"-:all"` + SystemNotificationTemplates []SystemNotificationTemplate `gorm:"many2many:system_notification_template_organizations"` + SystemNotificationTemplateIds []uuid.UUID `gorm:"-:all"` + ClusterCount int `gorm:"-:all"` + AdminId *uuid.UUID + Admin *User `gorm:"-:all"` +} diff --git a/internal/model/permission.go b/internal/model/permission.go new file mode 100644 index 00000000..3091706a --- /dev/null +++ b/internal/model/permission.go @@ -0,0 +1,1090 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/helper" + "gorm.io/gorm" +) + +type PermissionKind string + +var SortOrder = map[string]int{ + OperationRead: 0, + OperationCreate: 1, + OperationUpdate: 2, + OperationDelete: 3, + OperationDownload: 4, +} + +const ( + DashBoardPermission PermissionKind = "대시보드" + StackPermission PermissionKind = "스택" + PolicyPermission PermissionKind = "정책" + ProjectPermission PermissionKind = "프로젝트" + NotificationPermission PermissionKind = "알림" + ConfigurationPermission PermissionKind = "설정" + + OperationRead = "READ" + OperationCreate = "CREATE" + OperationUpdate = "UPDATE" + OperationDelete = "DELETE" + OperationDownload = "DOWNLOAD" + + // Key + TopDashboardKey = "DASHBOARD" + MiddleDashboardKey = "DASHBOARD-DASHBOARD" + TopStackKey = "STACK" + MiddleStackKey = "STACK-STACK" + TopPolicyKey = "POLICY" + MiddlePolicyKey = "POLICY-POLICY" + TopNotificationKey = "NOTIFICATION" + MiddleNotificationKey = "NOTIFICATION-SYSTEM_NOTIFICATION" + MiddlePolicyNotificationKey = "NOTIFICATION-POLICY_NOTIFICATION" + TopProjectKey = "PROJECT" + MiddleProjectKey = "PROJECT-PROJECT_LIST" + MiddleProjectCommonConfigurationKey = "PROJECT-PROJECT_COMMON_CONFIGURATION" + MiddleProjectMemberConfigurationKey = "PROJECT-PROJECT_MEMBER_CONFIGURATION" + MiddleProjectNamespaceKey = "PROJECT-PROJECT_NAMESPACE" + MiddleProjectAppServeKey = "PROJECT-PROJECT_APP_SERVE" + TopConfigurationKey = "CONFIGURATION" + MiddleConfigurationKey = "CONFIGURATION-CONFIGURATION" + MiddleConfigurationCloudAccountKey = "CONFIGURATION-CLOUD_ACCOUNT" + MiddleConfigurationProjectKey = "CONFIGURATION-PROJECT" + MiddleConfigurationUserKey = "CONFIGURATION-USER" + MiddleConfigurationRoleKey = "CONFIGURATION-ROLE" + MiddleConfigurationSystemNotificationKey = "CONFIGURATION-SYSTEM_NOTIFICATION" +) + +type Permission struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid;" json:"ID"` + Name string `json:"name"` + Key string `gorm:"type:text;" json:"key,omitempty"` + + IsAllowed *bool `gorm:"type:boolean;" json:"is_allowed,omitempty"` + RoleID *string `json:"role_id,omitempty"` + Role *Role `gorm:"foreignKey:RoleID;references:ID;" json:"role,omitempty"` + Endpoints []*Endpoint `gorm:"many2many:permission_endpoints;" json:"endpoints,omitempty"` + // omit empty + + ParentID *uuid.UUID `json:"parent_id,omitempty"` + Parent *Permission `gorm:"foreignKey:ParentID;references:ID;" json:"parent,omitempty"` + Children []*Permission `gorm:"foreignKey:ParentID;references:ID;" json:"children,omitempty"` +} + +type PermissionSet struct { + Dashboard *Permission `gorm:"-:all" json:"dashboard,omitempty"` + Stack *Permission `gorm:"-:all" json:"stack,omitempty"` + Policy *Permission `gorm:"-:all" json:"policy,omitempty"` + ProjectManagement *Permission `gorm:"-:all" json:"project_management,omitempty"` + Notification *Permission `gorm:"-:all" json:"notification,omitempty"` + Configuration *Permission `gorm:"-:all" json:"configuration,omitempty"` + Common *Permission `gorm:"-:all" json:"common,omitempty"` + Admin *Permission `gorm:"-:all" json:"admin,omitempty"` +} + +func NewDefaultPermissionSet() *PermissionSet { + return &PermissionSet{ + Dashboard: newDashboard(), + Stack: newStack(), + Policy: newPolicy(), + ProjectManagement: newProject(), + Notification: newNotification(), + Configuration: newConfiguration(), + Common: newCommon(), + Admin: nil, + } +} + +func NewAdminPermissionSet() *PermissionSet { + return &PermissionSet{ + Admin: newAdmin(), + Dashboard: newDashboard(), + Stack: newStack(), + Policy: newPolicy(), + ProjectManagement: newProject(), + Notification: newNotification(), + Configuration: newConfiguration(), + Common: newCommon(), + } +} + +func GetEdgePermission(root *Permission, edgePermissions []*Permission, f *func(permission Permission) bool) []*Permission { + if root.Children == nil || len(root.Children) == 0 { + if f != nil && !(*f)(*root) { + return edgePermissions + } + return append(edgePermissions, root) + } + + for _, child := range root.Children { + edgePermissions = GetEdgePermission(child, edgePermissions, f) + } + + return edgePermissions +} + +func endpointObjects(eps ...api.Endpoint) []*Endpoint { + var result []*Endpoint + for _, ep := range eps { + result = append(result, &Endpoint{ + Name: api.ApiMap[ep].Name, + Group: api.ApiMap[ep].Group, + }) + } + return result +} + +func newDashboard() *Permission { + dashboard := &Permission{ + ID: uuid.New(), + Name: string(DashBoardPermission), + Key: TopDashboardKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "대시보드", + Key: MiddleDashboardKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetChartsDashboard, + api.GetChartDashboard, + api.GetStacksDashboard, + api.GetResourcesDashboard, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + }, + }, + }, + }, + } + + return dashboard +} + +func newStack() *Permission { + stack := &Permission{ + ID: uuid.New(), + Name: string(StackPermission), + Key: TopStackKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "스택", + Key: MiddleStackKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetStacks, + api.GetStack, + api.CheckStackName, + api.GetStackStatus, + api.GetStackKubeConfig, + + api.SetFavoriteStack, + api.DeleteFavoriteStack, + + // Cluster + api.GetCluster, + api.GetClusters, + api.GetClusterSiteValues, + api.GetBootstrapKubeconfig, + api.GetNodes, + + // AppGroup + api.GetAppgroups, + api.GetAppgroup, + api.GetApplications, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateStack, + api.InstallStack, + api.CreateAppgroup, + + // Cluster + api.CreateCluster, + api.ImportCluster, + api.InstallCluster, + api.CreateBootstrapKubeconfig, + + // AppGroup + api.CreateAppgroup, + api.CreateApplication, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateStack, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteStack, + + // Cluster + api.DeleteCluster, + + // AppGroup + api.DeleteAppgroup, + ), + }, + }, + }, + }, + } + + return stack +} + +func newPolicy() *Permission { + policy := &Permission{ + ID: uuid.New(), + Name: string(PolicyPermission), + Key: TopPolicyKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "정책", + Key: MiddlePolicyKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.Admin_ListPolicyTemplate, + api.Admin_GetPolicyTemplate, + api.Admin_GetPolicyTemplateDeploy, + api.Admin_ListPolicyTemplateStatistics, + api.Admin_ListPolicyTemplateVersions, + api.Admin_GetPolicyTemplateVersion, + api.Admin_ExistsPolicyTemplateName, + api.Admin_ExistsPolicyTemplateKind, + + // StackPolicyStatus + api.ListStackPolicyStatus, + api.GetStackPolicyTemplateStatus, + + // Policy + api.GetMandatoryPolicies, + api.ListPolicy, + api.GetPolicy, + api.ExistsPolicyName, + + // OrganizationPolicyTemplate + api.ListPolicyTemplate, + api.GetPolicyTemplate, + api.GetPolicyTemplateDeploy, + api.ListPolicyTemplateStatistics, + api.ListPolicyTemplateVersions, + api.GetPolicyTemplateVersion, + api.ExistsPolicyTemplateKind, + api.ExistsPolicyTemplateName, + + // PolicyTemplateExample + api.ListPolicyTemplateExample, + api.GetPolicyTemplateExample, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.Admin_CreatePolicyTemplate, + api.Admin_CreatePolicyTemplateVersion, + + // Policy + api.SetMandatoryPolicies, + api.CreatePolicy, + + // OrganizationPolicyTemplate + api.CreatePolicyTemplate, + api.CreatePolicyTemplateVersion, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.Admin_UpdatePolicyTemplate, + + // ClusterPolicyStatus + api.UpdateStackPolicyTemplateStatus, + + // Policy + api.UpdatePolicy, + api.UpdatePolicyTargetClusters, + + // OrganizationPolicyTemplate + api.UpdatePolicyTemplate, + + // PolicyTemplateExample + api.UpdatePolicyTemplateExample, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + // PolicyTemplate + api.Admin_DeletePolicyTemplate, + api.Admin_DeletePolicyTemplateVersion, + + // Policy + api.DeletePolicy, + + // OrganizationPolicyTemplate + api.DeletePolicyTemplate, + api.DeletePolicyTemplateVersion, + + // PolicyTemplateExample + api.DeletePolicyTemplateExample, + ), + }, + }, + }, + }, + } + + return policy +} + +func newNotification() *Permission { + notification := &Permission{ + ID: uuid.New(), + Name: string(NotificationPermission), + Key: TopNotificationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "시스템 알림", + Key: MiddleNotificationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetSystemNotification, + api.GetSystemNotifications, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateSystemNotification, + api.CreateSystemNotificationAction, + ), + }, + { + ID: uuid.New(), + Name: "다운로드", + Key: OperationDownload, + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, + }, + }, + { + ID: uuid.New(), + Name: "정책 알림", + Key: MiddlePolicyNotificationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, + { + ID: uuid.New(), + Name: "다운로드", + Key: OperationDownload, + IsAllowed: helper.BoolP(false), + Children: []*Permission{}, + }, + }, + }, + }, + } + + return notification +} + +func newProject() *Permission { + project := &Permission{ + ID: uuid.New(), + Name: string(ProjectPermission), + Key: TopProjectKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "프로젝트 목록", + Key: MiddleProjectKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjects, + api.GetProject, + api.GetProjectKubeconfig, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateProject, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProject, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteProject, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "일반 설정", + Key: MiddleProjectCommonConfigurationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjects, + api.GetProject, + + api.GetProjectRoles, + api.GetProjectRole, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProject, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "구성원 설정", + Key: MiddleProjectMemberConfigurationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjectMembers, + api.GetProjectMember, + api.GetProjectRoles, + api.GetProjectRole, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.AddProjectMember, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProjectMemberRole, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.RemoveProjectMember, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "네임스페이스", + Key: MiddleProjectNamespaceKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetProjectNamespaces, + api.GetProjectNamespace, + api.GetProjectNamespaceK8sResources, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateProjectNamespace, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateProjectNamespace, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteProjectNamespace, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "앱 서빙", + Key: MiddleProjectAppServeKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetAppServeApps, + api.GetAppServeApp, + api.GetNumOfAppsOnStack, + api.GetAppServeAppLatestTask, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.GetAppServeAppTaskDetail, + api.GetAppServeAppTasksByAppId, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateAppServeApp, + api.IsAppServeAppExist, + api.IsAppServeAppNameExist, + api.UpdateAppServeApp, + api.UpdateAppServeAppEndpoint, + api.UpdateAppServeAppStatus, + api.RollbackAppServeApp, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteAppServeApp, + ), + }, + }, + }, + }, + } + + return project +} + +func newConfiguration() *Permission { + configuration := &Permission{ + ID: uuid.New(), + Name: string(ConfigurationPermission), + Key: TopConfigurationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "일반", + Key: MiddleConfigurationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + ID: uuid.New(), + Name: "클라우드 계정", + Key: MiddleConfigurationCloudAccountKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetCloudAccounts, + api.GetCloudAccount, + api.CheckCloudAccountName, + api.CheckAwsAccountId, + api.GetResourceQuota, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateCloudAccount, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateCloudAccount, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteCloudAccount, + api.DeleteForceCloudAccount, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "프로젝트", + Key: MiddleConfigurationProjectKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + }, + }, + }, + { + ID: uuid.New(), + Name: "사용자", + Key: MiddleConfigurationUserKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.ListUser, + api.GetUser, + api.CheckId, + api.CheckEmail, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateUser, + api.CheckId, + api.CheckEmail, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateUser, + api.ResetPassword, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteUser, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "역할 및 권한", + Key: MiddleConfigurationRoleKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.ListTksRoles, + api.GetTksRole, + api.GetPermissionsByRoleId, + api.GetPermissionTemplates, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateTksRole, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateTksRole, + api.UpdatePermissionsByRoleId, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteTksRole, + ), + }, + }, + }, + { + ID: uuid.New(), + Name: "시스템 알림", + Key: MiddleConfigurationSystemNotificationKey, + Children: []*Permission{ + { + ID: uuid.New(), + Name: "조회", + Key: OperationRead, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.GetSystemNotificationRules, + api.GetSystemNotificationRule, + ), + }, + { + ID: uuid.New(), + Name: "생성", + Key: OperationCreate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.CreateSystemNotificationRule, + ), + }, + { + ID: uuid.New(), + Name: "수정", + Key: OperationUpdate, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.UpdateSystemNotificationRule, + ), + }, + { + ID: uuid.New(), + Name: "삭제", + Key: OperationDelete, + IsAllowed: helper.BoolP(false), + Endpoints: endpointObjects( + api.DeleteSystemNotificationRule, + ), + }, + }, + }, + }, + } + + return configuration +} + +func newCommon() *Permission { + common := &Permission{ + ID: uuid.New(), + Name: "공통", + IsAllowed: helper.BoolP(true), + Endpoints: endpointObjects( + // Auth + api.Login, + api.Logout, + api.RefreshToken, + api.FindId, + api.FindPassword, + api.VerifyIdentityForLostId, + api.VerifyIdentityForLostPassword, + api.VerifyToken, + + // Stack + api.SetFavoriteStack, + api.DeleteFavoriteStack, + + // Project + api.SetFavoriteProject, + api.SetFavoriteProjectNamespace, + api.UnSetFavoriteProject, + api.UnSetFavoriteProjectNamespace, + + // MyProfile + api.GetMyProfile, + api.UpdateMyProfile, + api.UpdateMyPassword, + api.RenewPasswordExpiredDate, + api.DeleteMyProfile, + + // StackTemplate + api.GetOrganizationStackTemplates, + api.GetOrganizationStackTemplate, + + // Utiliy + api.CompileRego, + ), + } + + return common + +} + +func newAdmin() *Permission { + admin := &Permission{ + ID: uuid.New(), + Name: "관리자", + IsAllowed: helper.BoolP(true), + Endpoints: endpointObjects( + // Organization + api.Admin_CreateOrganization, + api.Admin_DeleteOrganization, + api.UpdateOrganization, + api.GetOrganization, + api.GetOrganizations, + api.UpdatePrimaryCluster, + api.CheckOrganizationName, + + // User + api.ResetPassword, + api.CheckId, + api.CheckEmail, + + // StackTemplate + api.Admin_GetStackTemplates, + api.Admin_GetStackTemplate, + api.Admin_GetStackTemplateServices, + api.Admin_CreateStackTemplate, + api.Admin_UpdateStackTemplate, + api.Admin_DeleteStackTemplate, + api.Admin_UpdateStackTemplateOrganizations, + api.Admin_CheckStackTemplateName, + + // Admin + api.Admin_GetUser, + api.Admin_ListUser, + api.Admin_CreateUser, + api.Admin_UpdateUser, + api.Admin_DeleteUser, + api.Admin_GetSystemNotificationTemplate, + api.Admin_CreateSystemNotificationTemplate, + api.Admin_ListUser, + api.Admin_GetTksRole, + api.Admin_GetProjects, + api.Admin_UpdateSystemNotificationTemplate, + api.Admin_ListTksRoles, + api.Admin_GetSystemNotificationTemplates, + + // Audit + api.GetAudits, + api.GetAudit, + api.DeleteAudit, + + api.CreateSystemNotification, + api.DeleteSystemNotification, + ), + } + + return admin +} + +func (p *PermissionSet) SetAllowedPermissionSet() { + edgePermissions := make([]*Permission, 0) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Dashboard, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Stack, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Policy, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.ProjectManagement, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Notification, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Configuration, edgePermissions, nil)...) + + for _, permission := range edgePermissions { + permission.IsAllowed = helper.BoolP(true) + } +} + +func (p *PermissionSet) SetUserPermissionSet() { + f := func(permission Permission) bool { + return permission.Name == "조회" + } + edgePermissions := make([]*Permission, 0) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Dashboard, edgePermissions, nil)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Stack, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Policy, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.ProjectManagement, edgePermissions, &f)...) + edgePermissions = append(edgePermissions, GetEdgePermission(p.Notification, edgePermissions, &f)...) + //edgePermissions = append(edgePermissions, GetEdgePermission(p.Configuration, edgePermissions, &f)...) + + for _, permission := range edgePermissions { + permission.IsAllowed = helper.BoolP(true) + } +} + +func (p *PermissionSet) SetRoleId(roleId string) { + setRoleIdToPermission(p.Dashboard, roleId) + setRoleIdToPermission(p.Stack, roleId) + setRoleIdToPermission(p.Policy, roleId) + setRoleIdToPermission(p.ProjectManagement, roleId) + setRoleIdToPermission(p.Notification, roleId) + setRoleIdToPermission(p.Configuration, roleId) +} + +func setRoleIdToPermission(root *Permission, roleId string) { + root.RoleID = helper.StringP(roleId) + + if root.Children == nil { + return + } + + for _, child := range root.Children { + setRoleIdToPermission(child, roleId) + } +} diff --git a/internal/model/policy-template.go b/internal/model/policy-template.go new file mode 100644 index 00000000..ef9ec66e --- /dev/null +++ b/internal/model/policy-template.go @@ -0,0 +1,161 @@ +package model + +import ( + "encoding/json" + "slices" + "strings" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +const ( + FILE_DELIMETER = "---\n" +) + +type PolicyTemplateSupportedVersion struct { + gorm.Model + + PolicyTemplateId uuid.UUID `gorm:"index:template_version,unique"` + Version string `gorm:"index:template_version,unique"` + + ParameterSchema string `gorm:"type:text"` + Rego string `gorm:"type:text"` + Libs string `gorm:"type:text"` + SyncKinds *string `gorm:"type:text"` + SyncJson *string `gorm:"type:text"` +} + +type PolicyTemplate struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:varchar(36);not null"` + TemplateName string + Type string // Org or Tks + Version string `gorm:"-:all"` // 삭제 예정 + CurrentVersion string `gorm:"-:all"` + LatestVersion string `gorm:"-:all"` + SupportedVersions []PolicyTemplateSupportedVersion `gorm:"foreignKey:PolicyTemplateId"` + OrganizationId *string // Org 인 경우에만 설정 + Organization Organization `gorm:"foreignKey:OrganizationId"` + Description string + Kind string + Deprecated bool + Mandatory bool // Tks 인 경우에는 무시 + Severity string + PermittedOrganizations []Organization `gorm:"many2many:policy_template_permitted_organizations"` + ParametersSchema []*domain.ParameterDef `gorm:"-:all"` + Rego string `gorm:"-:all"` + Libs []string `gorm:"-:all"` + SyncKinds *[]string `gorm:"-:all"` + SyncJson *string `gorm:"-:all"` + PermittedOrganizationIds []string `gorm:"-:all"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} + +func (pt *PolicyTemplate) IsTksTemplate() bool { + return strings.ToLower(pt.Type) == "tks" +} + +func (pt *PolicyTemplate) IsOrganizationTemplate() bool { + return !pt.IsTksTemplate() +} + +func (pt *PolicyTemplate) ResoureName() string { + return strings.ToLower(pt.Kind) +} + +func (pt *PolicyTemplate) IsPermittedToOrganization(organizationId *string) bool { + // tks Admin은 organizationId가 nil + if organizationId == nil { + return true + } + + if pt.IsTksTemplate() { + return len(pt.PermittedOrganizationIds) == 0 || + slices.Contains(pt.PermittedOrganizationIds, *organizationId) + } + + return pt.OrganizationId != nil && *organizationId == *pt.OrganizationId +} + +func (pt *PolicyTemplate) BeforeCreate(tx *gorm.DB) (err error) { + if pt.ID == uuid.Nil { + pt.ID = uuid.New() + } + + jsonByte, err := json.Marshal(pt.ParametersSchema) + + if err != nil { + return err + } + + libs := strings.Join(pt.Libs, FILE_DELIMETER) + + pt.Version = "v1.0.0" + + var syncKindsString *string = nil + + if pt.SyncKinds != nil { + syncJsonBytes, err := json.Marshal(pt.SyncKinds) + + if err == nil { + syncStr := string(syncJsonBytes) + syncKindsString = &syncStr + } + } + + pt.SupportedVersions = []PolicyTemplateSupportedVersion{ + { + Version: "v1.0.0", + ParameterSchema: string(jsonByte), + Rego: pt.Rego, + Libs: libs, + SyncJson: pt.SyncJson, + SyncKinds: syncKindsString, + }, + } + + return nil +} + +func (pt *PolicyTemplate) AfterFind(tx *gorm.DB) (err error) { + // 목록 조회 시 에러가 발생해서 전체 조회가 실패하는 것을 방지하기 위해서 에러는 무시 + if len(pt.SupportedVersions) > 0 { + supportedVersion := pt.SupportedVersions[0] + pt.Version = supportedVersion.Version + pt.Rego = supportedVersion.Rego + pt.SyncJson = supportedVersion.SyncJson + + if len(strings.TrimSpace(supportedVersion.Libs)) == 0 { + pt.Libs = []string{} + } else { + pt.Libs = strings.Split(supportedVersion.Libs, FILE_DELIMETER) + } + + // 마찬가지로 에러 무시 + _ = json.Unmarshal([]byte(supportedVersion.ParameterSchema), &pt.ParametersSchema) + + if supportedVersion.SyncKinds != nil { + syncKinds := []string{} + _ = json.Unmarshal([]byte(*supportedVersion.SyncKinds), &syncKinds) + pt.SyncKinds = &syncKinds + } + } + + pt.PermittedOrganizationIds = make([]string, len(pt.PermittedOrganizations)) + for i, org := range pt.PermittedOrganizations { + pt.PermittedOrganizationIds[i] = org.ID + } + + return +} + +type PolicyTemplatePermittedOrganization struct { + PolicyTemplateId uuid.UUID `gorm:"primarykey"` + OrganizationId string `gorm:"primarykey"` +} diff --git a/internal/model/policy.go b/internal/model/policy.go new file mode 100644 index 00000000..edfd1ae9 --- /dev/null +++ b/internal/model/policy.go @@ -0,0 +1,92 @@ +package model + +import ( + "encoding/json" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +type Policy struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:varchar(36);not null"` + OrganizationId string + + PolicyName string + PolicyResourceName string + Mandatory bool + Description string + + TargetClusterIds []string `gorm:"-:all"` + TargetClusters []Cluster `gorm:"many2many:policy_target_clusters"` + + EnforcementAction string + + Parameters string `gorm:"type:text"` + PolicyMatch string `gorm:"type:text"` + MatchYaml *string `gorm:"type:text"` + Match *domain.Match `gorm:"-:all"` + + TemplateName string `gorm:"-:all"` + TemplateId uuid.UUID `gorm:"type:uuid"` + PolicyTemplate PolicyTemplate `gorm:"foreignKey:TemplateId"` + + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} + +func (p *Policy) BeforeCreate(tx *gorm.DB) (err error) { + if p.ID == uuid.Nil { + p.ID = uuid.New() + } + + if p.Match != nil { + jsonBytes, err := json.Marshal(p.Match) + + if err != nil { + return err + } + + p.PolicyMatch = string(jsonBytes) + } + + return nil +} + +func (p *Policy) AfterFind(tx *gorm.DB) (err error) { + p.TemplateName = p.PolicyTemplate.TemplateName + + if len(p.PolicyMatch) > 0 { + // 목록 조회 시 에러가 발생해서 전체 조회가 실패하는 것을 방지하기 위해서 에러는 무시 + var match domain.Match + err = json.Unmarshal([]byte(p.PolicyMatch), &match) + p.Match = &match + } + + p.TargetClusterIds = make([]string, len(p.TargetClusters)) + for i, cluster := range p.TargetClusters { + p.TargetClusterIds[i] = cluster.ID.String() + } + + return +} + +type PolicyTargetCluster struct { + PolicyId uuid.UUID `gorm:"primarykey"` + ClusterId domain.ClusterId `gorm:"primarykey"` +} + +type UsageCount struct { + OrganizationId string + OrganizationName string + UsageCount int +} + +type PolicyCount struct { + EnforcementAction string + Count int64 +} diff --git a/internal/model/project.go b/internal/model/project.go new file mode 100644 index 00000000..50fe7d59 --- /dev/null +++ b/internal/model/project.go @@ -0,0 +1,85 @@ +package model + +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +func (t *ProjectRole) BeforeCreate(*gorm.DB) (err error) { + t.ID = uuid.New().String() + return nil +} + +type Project struct { + ID string `gorm:"primarykey" json:"id"` + OrganizationId string `json:"organizationId"` + Name string `gorm:"index" json:"name"` + Description string `json:"description,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` + ProjectMembers []ProjectMember `gorm:"foreignKey:ProjectId;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectMembers,omitempty"` + ProjectNamespaces []ProjectNamespace `gorm:"foreignKey:ProjectId;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectNamespaces,omitempty"` +} + +type ProjectRole struct { + ID string `gorm:"primarykey" json:"id"` + Name string `json:"name"` // project-leader, project-member, project-viewer + Description string `json:"description,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type ProjectUser struct { + ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func (ProjectUser) TableName() string { + return "users" +} + +type ProjectMember struct { + ID string `gorm:"primarykey" json:"id"` + ProjectId string `gorm:"not null" json:"projectId"` + ProjectUserId uuid.UUID `json:"projectUserId"` + ProjectUser *ProjectUser `gorm:"foreignKey:ProjectUserId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectUser"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRole *ProjectRole `gorm:"foreignKey:ProjectRoleId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"projectRole"` + IsProjectLeader bool `gorm:"default:false" json:"projectLeader"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type ProjectStack struct { + ID string `gorm:"primarykey" json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` +} + +func (ProjectStack) TableName() string { + return "clusters" +} + +type ProjectNamespace struct { + StackId string `gorm:"primarykey" json:"stackId"` + Namespace string `gorm:"primarykey" json:"namespace"` + Stack *ProjectStack `gorm:"foreignKey:StackId;references:ID;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT" json:"stack"` + ProjectId string `gorm:"not null" json:"projectId"` + Description string `json:"description,omitempty"` + Status string `json:"status,omitempty"` + GrafanaUrl string `gorm:"-:all" json:"grafanaUrl,omitempty"` + CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` + UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} diff --git a/internal/model/role.go b/internal/model/role.go new file mode 100644 index 00000000..981d92d2 --- /dev/null +++ b/internal/model/role.go @@ -0,0 +1,22 @@ +package model + +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +type Role struct { + gorm.Model + + ID string `gorm:"primarykey;" json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + Organization Organization `gorm:"foreignKey:OrganizationID;references:ID;" json:"organization"` + Type string `json:"type"` + Description string `json:"description"` + Creator uuid.UUID `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} diff --git a/internal/model/stack-template.go b/internal/model/stack-template.go new file mode 100644 index 00000000..b67e8ed8 --- /dev/null +++ b/internal/model/stack-template.go @@ -0,0 +1,35 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/datatypes" + "gorm.io/gorm" +) + +type StackTemplate struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + Name string `gorm:"index,unique"` + Description string `gorm:"index"` + Template string + TemplateType string + Version string + CloudService string + Platform string + KubeVersion string + KubeType string + Organizations []Organization `gorm:"many2many:stack_template_organizations"` + Services datatypes.JSON + ServiceIds []string `gorm:"-:all"` + OrganizationIds []string `gorm:"-:all"` + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator User `gorm:"foreignKey:UpdatorId"` +} + +type StackTemplateOrganization struct { + StackTemplateId uuid.UUID `gorm:"primarykey"` + OrganizationId string `gorm:"primarykey"` +} diff --git a/internal/model/stack.go b/internal/model/stack.go new file mode 100644 index 00000000..4d837019 --- /dev/null +++ b/internal/model/stack.go @@ -0,0 +1,48 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/gorm" +) + +type Stack = struct { + gorm.Model + + ID domain.StackId + Name string + Description string + ClusterId string + OrganizationId string + CloudService string + CloudAccountId uuid.UUID + CloudAccount CloudAccount + StackTemplateId uuid.UUID + StackTemplate StackTemplate + Status domain.StackStatus + StatusDesc string + PrimaryCluster bool + GrafanaUrl string + CreatorId *uuid.UUID + Creator User + UpdatorId *uuid.UUID + Updator User + Favorited bool + ClusterEndpoint string + Resource domain.DashboardStack + PolicyIds []string + Conf StackConf + AppServeAppCnt int +} + +type StackConf struct { + TksCpNode int + TksCpNodeMax int + TksCpNodeType string + TksInfraNode int + TksInfraNodeMax int + TksInfraNodeType string + TksUserNode int + TksUserNodeMax int + TksUserNodeType string +} diff --git a/internal/model/system-notification-rule.go b/internal/model/system-notification-rule.go new file mode 100644 index 00000000..f2a890fd --- /dev/null +++ b/internal/model/system-notification-rule.go @@ -0,0 +1,45 @@ +package model + +import ( + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/datatypes" + "gorm.io/gorm" +) + +type SystemNotificationCondition struct { + gorm.Model + + SystemNotificationRuleId uuid.UUID + Severity string + Duration string + Parameter datatypes.JSON + Parameters []domain.SystemNotificationParameter `gorm:"-:all"` + EnableEmail bool + EnablePortal bool +} + +type SystemNotificationRule struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + Name string `gorm:"index,unique"` + Description string + NotificationType string `gorm:"default:SYSTEM_NOTIFICATION"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId"` + IsSystem bool `gorm:"default:false"` + SystemNotificationTemplateId uuid.UUID + SystemNotificationTemplate SystemNotificationTemplate `gorm:"foreignKey:SystemNotificationTemplateId"` + SystemNotificationCondition SystemNotificationCondition `gorm:"foreignKey:SystemNotificationRuleId"` + TargetUsers []User `gorm:"many2many:system_notification_rule_users;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` + TargetUserIds []string `gorm:"-:all"` + MessageTitle string + MessageContent string + MessageActionProposal string + Status domain.SystemNotificationRuleStatus + CreatorId *uuid.UUID `gorm:"type:uuid"` + Creator *User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID `gorm:"type:uuid"` + Updator *User `gorm:"foreignKey:UpdatorId"` +} diff --git a/internal/model/system-notification-template.go b/internal/model/system-notification-template.go new file mode 100644 index 00000000..48d5e427 --- /dev/null +++ b/internal/model/system-notification-template.go @@ -0,0 +1,34 @@ +package model + +import ( + "github.com/google/uuid" + "gorm.io/gorm" +) + +// Models +type SystemNotificationMetricParameter struct { + gorm.Model + + SystemNotificationTemplateId uuid.UUID + Order int + Key string + Value string +} + +type SystemNotificationTemplate struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + Name string `gorm:"index:idx_name,unique"` + NotificationType string `gorm:"default:SYSTEM_NOTIFICATION"` + IsSystem bool `gorm:"default:false"` + Organizations []Organization `gorm:"many2many:system_notification_template_organizations;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` + OrganizationIds []string `gorm:"-:all"` + Description string + MetricQuery string + MetricParameters []SystemNotificationMetricParameter `gorm:"foreignKey:SystemNotificationTemplateId;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` + CreatorId *uuid.UUID + Creator User `gorm:"foreignKey:CreatorId"` + UpdatorId *uuid.UUID + Updator User `gorm:"foreignKey:UpdatorId"` +} diff --git a/internal/model/system-notification.go b/internal/model/system-notification.go new file mode 100644 index 00000000..2be07d47 --- /dev/null +++ b/internal/model/system-notification.go @@ -0,0 +1,54 @@ +package model + +import ( + "time" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/pkg/domain" + "gorm.io/datatypes" + "gorm.io/gorm" +) + +// Models +type SystemNotification struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + Name string + NotificationType string `gorm:"default:SYSTEM_NOTIFICATION"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId"` + ClusterId domain.ClusterId + Cluster Cluster `gorm:"foreignKey:ClusterId"` + Severity string + MessageTitle string + MessageContent string + MessageActionProposal string + Node string + GrafanaUrl string + FiredAt *time.Time `gorm:"-:all"` + TakedAt *time.Time `gorm:"-:all"` + ClosedAt *time.Time `gorm:"-:all"` + TakedSec int `gorm:"-:all"` + ProcessingSec int `gorm:"-:all"` + LastTaker User `gorm:"-:all"` + SystemNotificationActions []SystemNotificationAction `gorm:"foreignKey:SystemNotificationId;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` + Summary string + RawData datatypes.JSON + Status domain.SystemNotificationActionStatus `gorm:"index"` + Read bool `gorm:"-:all"` + Readers []User `gorm:"many2many:system_notification_users;constraint:OnUpdate:RESTRICT,OnDelete:RESTRICT"` + SystemNotificationRuleId *uuid.UUID + PolicyName string +} + +type SystemNotificationAction struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey"` + SystemNotificationId uuid.UUID + Content string + Status domain.SystemNotificationActionStatus + TakerId *uuid.UUID `gorm:"type:uuid"` + Taker User `gorm:"foreignKey:TakerId"` +} diff --git a/internal/model/user.go b/internal/model/user.go new file mode 100644 index 00000000..dfaa0db7 --- /dev/null +++ b/internal/model/user.go @@ -0,0 +1,39 @@ +package model + +import ( + "time" + + "gorm.io/gorm" + + "github.com/google/uuid" +) + +type User struct { + gorm.Model + + ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` + AccountId string `json:"accountId"` + Password string `gorm:"-:all" json:"password"` + Name string `json:"name"` + Token string `json:"token"` + Roles []Role `gorm:"many2many:user_roles;" json:"roles"` + OrganizationId string + Organization Organization `gorm:"foreignKey:OrganizationId;references:ID" json:"organization"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` + PasswordExpired bool `json:"passwordExpired"` + + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` +} + +func (u *User) BeforeDelete(db *gorm.DB) (err error) { + err = db.Table("user_roles").Unscoped().Where("user_id = ?", u.ID).Delete(nil).Error + if err != nil { + return err + } + return nil +} diff --git a/internal/pagination/pagination.go b/internal/pagination/pagination.go index 287da518..4b82ed84 100644 --- a/internal/pagination/pagination.go +++ b/internal/pagination/pagination.go @@ -1,19 +1,34 @@ package pagination import ( - "fmt" + "context" "net/url" "strconv" "strings" + "github.com/iancoleman/strcase" + filter "github.com/openinfradev/tks-api/internal/filter" "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/log" + "gorm.io/gorm" + + "goyave.dev/goyave/v4" + "goyave.dev/goyave/v4/database" ) const SORT_COLUMN = "sortColumn" const SORT_ORDER = "sortOrder" const PAGE_NUMBER = "pageNumber" const PAGE_SIZE = "pageSize" -const COMBINED_FILTER = "combinedFilter" +const FILTER = "filter" +const FILTER_ARRAY = "filter[]" +const OR = "or" +const OR_ARRAY = "or[]" +const COMBINED_FILTER = "combinedFilter" // deprecated + +var DEFAULT_LIMIT = 10000 type Pagination struct { Limit int @@ -21,14 +36,20 @@ type Pagination struct { SortColumn string SortOrder string Filters []Filter - CombinedFilter CombinedFilter + CombinedFilter CombinedFilter // deprecated TotalRows int64 TotalPages int + + PaginationRequest *goyave.Request + Paginator *database.Paginator } type Filter struct { - Column string - Values []string + Or bool + Relation string + Column string + Operator string + Values []string } type CombinedFilter struct { @@ -36,9 +57,6 @@ type CombinedFilter struct { Value string } -var DEFAULT_LIMIT = 10 -var MAX_LIMIT = 1000 - func (p *Pagination) GetOffset() int { return (p.GetPage() - 1) * p.GetLimit() } @@ -69,69 +87,184 @@ func (p *Pagination) GetFilters() []Filter { return p.Filters } -func NewPagination(urlParams *url.Values) (*Pagination, error) { - pg := NewDefaultPagination() +func (p *Pagination) GetFilter(key string) *Filter { + for _, filter := range p.Filters { + if filter.Column == key { + return &filter + } + } + return nil +} - for key, value := range *urlParams { - switch key { - case SORT_COLUMN: - if value[0] == "" { - pg.SortColumn = "created_at" - } else { - pg.SortColumn = value[0] - } - case SORT_ORDER: - if value[0] == "" { - pg.SortOrder = "DESC" - } else { - pg.SortOrder = value[0] - } - case PAGE_NUMBER: - if value[0] == "" { - pg.Page = 1 - } else { - pg.Page, _ = strconv.Atoi(value[0]) - } - case PAGE_SIZE: - if value[0] == "" { - pg.Page = DEFAULT_LIMIT - } else { - if limitNum, err := strconv.Atoi(value[0]); err == nil { - pg.Limit = limitNum +func (p *Pagination) AddFilter(f Filter) { + p.Filters = append(p.Filters, f) +} + +func (p *Pagination) MakePaginationRequest() { + if p.PaginationRequest == nil { + p.PaginationRequest = &goyave.Request{} + } + + pgFilters := make([]*filter.Filter, 0) + pgSorts := make([]*filter.Sort, 0) + + for _, f := range p.Filters { + field := f.Column + if f.Relation != "" { + field = f.Relation + "." + f.Column + } + + pgFilter := filter.Filter{ + Field: field, + Operator: convertOperator(f.Operator), + Args: f.Values, + Or: f.Or, + } + + pgFilters = append(pgFilters, &pgFilter) + } + + pgSort := filter.Sort{ + Field: p.SortColumn, + Order: filter.SortOrder(p.SortOrder), + } + pgSorts = append(pgSorts, &pgSort) + + pgJoins := filter.Join{} + + p.PaginationRequest.Data = map[string]interface{}{ + "filter": pgFilters, + "join": pgJoins, + "page": p.Page, + "per_page": p.Limit, + "sort": pgSorts, + } +} + +func (p *Pagination) Fetch(db *gorm.DB, dest interface{}) (*database.Paginator, *gorm.DB) { + paginator, db := filter.Scope(db, p.PaginationRequest, dest) + + p.Paginator = paginator + + p.Page = paginator.CurrentPage + p.TotalPages = int(paginator.MaxPage) + p.TotalRows = paginator.Total + p.Limit = int(paginator.PageSize) + + return paginator, db +} + +func (p *Pagination) Response(ctx context.Context) (out domain.PaginationResponse, err error) { + if err := serializer.Map(ctx, *p, &out); err != nil { + return out, err + } + out.Filters = make([]domain.FilterResponse, len(p.Filters)) + for i, f := range p.Filters { + if err := serializer.Map(ctx, f, &out.Filters[i]); err != nil { + continue + } + } + + return out, err +} + +func NewPaginationWithFilter(column string, releation string, op string, values []string) *Pagination { + pg := newDefaultPagination() + + pg.Filters = append(pg.Filters, Filter{ + Column: helper.ToSnakeCase(strings.Replace(column, "[]", "", -1)), + Relation: releation, + Operator: op, + Values: values, + Or: false, + }) + pg.MakePaginationRequest() + + return pg +} + +func NewPagination(urlParams *url.Values) *Pagination { + pg := newDefaultPagination() + + if urlParams != nil { + for key, value := range *urlParams { + switch key { + case SORT_COLUMN: + if value[0] != "" { + pg.SortColumn = value[0] } - } - case COMBINED_FILTER: - if len(value[0]) > 0 { - //"combinedFilter=key1,key2:value" - filterArray := strings.Split(value[0], ":") - if len(filterArray) == 2 { - keys := strings.Split(helper.ToSnakeCase(strings.Replace(filterArray[0], "[]", "", -1)), ",") - value := filterArray[1] - - pg.CombinedFilter = CombinedFilter{ - Columns: keys, - Value: value, + case SORT_ORDER: + if value[0] != "" { + pg.SortOrder = value[0] + } + case PAGE_NUMBER: + if value[0] != "" { + pg.Page, _ = strconv.Atoi(value[0]) + } + case PAGE_SIZE: + if value[0] != "" { + if limitNum, err := strconv.Atoi(value[0]); err == nil { + pg.Limit = limitNum + } + } + case COMBINED_FILTER: // deprecated + log.Error(context.TODO(), "DEPRECATED filter scheme. COMBINEND_FILTER") + case FILTER, FILTER_ARRAY, OR, OR_ARRAY: + for _, filterValue := range value { + arr := strings.Split(filterValue, "|") + + columns := strings.Split(arr[0], ",") + for i, column := range columns { + releation := "" + arrColumns := strings.Split(column, ".") + if len(arrColumns) > 1 { + releation = strcase.ToCamel(arrColumns[0]) + column = arrColumns[1] + } + + trimmedStr := strings.Trim(arr[1], "[]") + values := strings.Split(trimmedStr, ",") + + op := "$cont" + if len(arr) == 3 { + op = arr[2] + } + + or := false + if i > 0 || key == OR || key == OR_ARRAY { + or = true + } + + pg.Filters = append(pg.Filters, Filter{ + Column: helper.ToSnakeCase(strings.Replace(column, "[]", "", -1)), + Relation: releation, + Operator: op, + Values: values, + Or: or, + }) + } - } else { - return nil, fmt.Errorf("Invalid query string : combinedFilter ") } } - default: - pg.Filters = append(pg.Filters, Filter{ - Column: helper.ToSnakeCase(strings.Replace(key, "[]", "", -1)), - Values: value, - }) } } + pg.MakePaginationRequest() - return pg, nil + return pg } -func NewDefaultPagination() *Pagination { +func newDefaultPagination() *Pagination { return &Pagination{ SortColumn: "created_at", SortOrder: "DESC", Page: 1, - Limit: MAX_LIMIT, + Limit: DEFAULT_LIMIT, + } +} + +func convertOperator(op string) *filter.Operator { + if _, ok := filter.Operators[op]; ok { + return filter.Operators[op] } + return filter.Operators["$cont"] } diff --git a/internal/policy-template/kind-map.go b/internal/policy-template/kind-map.go new file mode 100644 index 00000000..ba8ee5f7 --- /dev/null +++ b/internal/policy-template/kind-map.go @@ -0,0 +1,73 @@ +package policytemplate + +import "k8s.io/apimachinery/pkg/runtime/schema" + +var KindMap = map[string]schema.GroupVersionKind{ + "Namespace": {Group: "", Version: "v1", Kind: "Namespace"}, + "StatefulSet": {Group: "apps", Version: "v1", Kind: "StatefulSet"}, + "ClusterIssuer": {Group: "cert-manager.io", Version: "v1", Kind: "ClusterIssuer"}, + "LimitRange": {Group: "", Version: "v1", Kind: "LimitRange"}, + "PodAttachOptions": {Group: "", Version: "v1", Kind: "PodAttachOptions"}, + "PersistentVolume": {Group: "", Version: "v1", Kind: "PersistentVolume"}, + "Pod": {Group: "", Version: "v1", Kind: "Pod"}, + "SubjectAccessReview": {Group: "authorization.k8s.io", Version: "v1", Kind: "SubjectAccessReview"}, + "HorizontalPodAutoscaler": {Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}, + "PriorityLevelConfiguration": {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1", Kind: "PriorityLevelConfiguration"}, + "SelfSubjectAccessReview": {Group: "authorization.k8s.io", Version: "v1", Kind: "SelfSubjectAccessReview"}, + "Event": {Group: "events.k8s.io", Version: "v1", Kind: "Event"}, + "Job": {Group: "batch", Version: "v1", Kind: "Job"}, + "Issuer": {Group: "cert-manager.io", Version: "v1", Kind: "Issuer"}, + "Eviction": {Group: "", Version: "v1", Kind: "Eviction"}, + "ConfigMap": {Group: "", Version: "v1", Kind: "ConfigMap"}, + "NodeProxyOptions": {Group: "", Version: "v1", Kind: "NodeProxyOptions"}, + "Node": {Group: "", Version: "v1", Kind: "Node"}, + "CSIDriver": {Group: "storage.k8s.io", Version: "v1", Kind: "CSIDriver"}, + "PriorityClass": {Group: "scheduling.k8s.io", Version: "v1", Kind: "PriorityClass"}, + "ReplicationController": {Group: "", Version: "v1", Kind: "ReplicationController"}, + "ResourceQuota": {Group: "", Version: "v1", Kind: "ResourceQuota"}, + "Ingress": {Group: "networking.k8s.io", Version: "v1", Kind: "Ingress"}, + "ComponentStatus": {Group: "", Version: "v1", Kind: "ComponentStatus"}, + "ReplicaSet": {Group: "apps", Version: "v1", Kind: "ReplicaSet"}, + "RuntimeClass": {Group: "node.k8s.io", Version: "v1", Kind: "RuntimeClass"}, + "PodExecOptions": {Group: "", Version: "v1", Kind: "PodExecOptions"}, + "ServiceProxyOptions": {Group: "", Version: "v1", Kind: "ServiceProxyOptions"}, + "CronJob": {Group: "batch", Version: "v1", Kind: "CronJob"}, + "PersistentVolumeClaim": {Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}, + "Secret": {Group: "", Version: "v1", Kind: "Secret"}, + "ServiceAccount": {Group: "", Version: "v1", Kind: "ServiceAccount"}, + "CertificateSigningRequest": {Group: "certificates.k8s.io", Version: "v1", Kind: "CertificateSigningRequest"}, + "Certificate": {Group: "cert-manager.io", Version: "v1", Kind: "Certificate"}, + "Lease": {Group: "coordination.k8s.io", Version: "v1", Kind: "Lease"}, + "IPPool": {Group: "crd.projectcalico.org", Version: "v1", Kind: "IPPool"}, + "ClusterAnalysisTemplate": {Group: "argoproj.io", Version: "v1alpha1", Kind: "ClusterAnalysisTemplate"}, + "CertificateRequest": {Group: "cert-manager.io", Version: "v1", Kind: "CertificateRequest"}, + "Workflow": {Group: "argoproj.io", Version: "v1alpha1", Kind: "Workflow"}, + "Experiment": {Group: "argoproj.io", Version: "v1alpha1", Kind: "Experiment"}, + "CustomResourceDefinition": {Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}, + "APIService": {Group: "apiregistration.k8s.io", Version: "v1", Kind: "APIService"}, + "ClusterRoleBinding": {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, + "EndpointSlice": {Group: "discovery.k8s.io", Version: "v1", Kind: "EndpointSlice"}, + "CSINode": {Group: "storage.k8s.io", Version: "v1", Kind: "CSINode"}, + "StorageClass": {Group: "storage.k8s.io", Version: "v1", Kind: "StorageClass"}, + "MutatingWebhookConfiguration": {Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"}, + "Deployment": {Group: "apps", Version: "v1", Kind: "Deployment"}, + "PodProxyOptions": {Group: "", Version: "v1", Kind: "PodProxyOptions"}, + "LocalSubjectAccessReview": {Group: "authorization.k8s.io", Version: "v1", Kind: "LocalSubjectAccessReview"}, + "FlowSchema": {Group: "flowcontrol.apiserver.k8s.io", Version: "v1beta1", Kind: "FlowSchema"}, + "Endpoints": {Group: "", Version: "v1", Kind: "Endpoints"}, + "Service": {Group: "", Version: "v1", Kind: "Service"}, + "ControllerRevision": {Group: "apps", Version: "v1", Kind: "ControllerRevision"}, + "DaemonSet": {Group: "apps", Version: "v1", Kind: "DaemonSet"}, + "Role": {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"}, + "VolumeAttachment": {Group: "storage.k8s.io", Version: "v1", Kind: "VolumeAttachment"}, + "RoleBinding": {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}, + "ClusterTriggerBinding": {Group: "triggers.tekton.dev", Version: "v1beta1", Kind: "ClusterTriggerBinding"}, + "PodDisruptionBudget": {Group: "policy", Version: "v1", Kind: "PodDisruptionBudget"}, + "IngressClass": {Group: "networking.k8s.io", Version: "v1", Kind: "IngressClass"}, + "PodTemplate": {Group: "", Version: "v1", Kind: "PodTemplate"}, + "ClusterRole": {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, + "SelfSubjectRulesReview": {Group: "authorization.k8s.io", Version: "v1", Kind: "SelfSubjectRulesReview"}, + "ValidatingWebhookConfiguration": {Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration"}, + "Binding": {Group: "", Version: "v1", Kind: "Binding"}, + "PodPortForwardOptions": {Group: "", Version: "v1", Kind: "PodPortForwardOptions"}, +} diff --git a/internal/policy-template/kind-util.go b/internal/policy-template/kind-util.go new file mode 100644 index 00000000..5fe13747 --- /dev/null +++ b/internal/policy-template/kind-util.go @@ -0,0 +1,157 @@ +package policytemplate + +import ( + "encoding/json" + "fmt" + "slices" + + "github.com/openinfradev/tks-api/pkg/domain" +) + +var KindToApiGroup = map[string]string{ + "Pod": "", + "Node": "", + "Namespace": "", + "Service": "", + "Secret": "", + "ConfigMap": "", + "PersistentVolume": "", + "PersistentVolumeClaim": "", + "ReplicationController": "", //deprecated + "ServiceAccount": "", + "LimitRange": "", + "ResourceQuota": "", + "Deployment": "apps", + "ReplicaSet": "apps", + "StatefulSet": "apps", + "DaemonSet": "apps", + "HorizontalPodAutoscaler": "autoscaling", + "VerticalPodAutoscaler": "autoscaling", // 확인 안됨 + "Job": "batch", + "CronJob": "batch", + "Ingress": "networking.k8s.io", + "NetworkPolicy": "networking.k8s.io", + "StorageClass": "storage.k8s.io", + "VolumeAttachment": "storage.k8s.io", + "Role": "rbac.authorization.k8s.io", + "RoleBinding": "rbac.authorization.k8s.io", + "ClusterRole": "rbac.authorization.k8s.io", + "ClusterRoleBinding": "rbac.authorization.k8s.io", + "ValidatingWebhookConfiguration": "admissionregistration.k8s.io", + "MutatingWebhookConfiguration": "admissionregistration.k8s.io", + "CustomResourceDefinition": "apiextensions.k8s.io", + "Certificate": "cert-manager.io", + "Issuer": "cert-manager.io", + "Lease": "coordination.k8s.io", + "Lock": "coordination.k8s.io", // 안 나옴 + "EndpointSlice": "discovery.k8s.io", + "Event": "events.k8s.io", + "FlowSchema": "flowcontrol.apiserver.k8s.io", + "PriorityLevelConfiguration": "flowcontrol.apiserver.k8s.io", + "ManagedNamespacedResource": "meta.k8s.io", + "PriorityClass": "scheduling.k8s.io", + "PodSecurityPolicy": "policy", + "PodDisruptionBudget": "policy", +} + +func CheckAndNormalizeKinds(kinds []domain.Kinds) ([]domain.Kinds, error) { + if kinds == nil { + return nil, nil + } + + var result = []domain.Kinds{} + var invalidKinds = []string{} + var normalizedMap = map[string]domain.Kinds{} + + for _, kind := range kinds { + for _, kinditem := range kind.Kinds { + if apiGroup, ok := KindToApiGroup[kinditem]; ok { + if ai, ok := normalizedMap[apiGroup]; ok { + if !slices.Contains(ai.Kinds, kinditem) { + ai.Kinds = append(ai.Kinds, kinditem) + normalizedMap[apiGroup] = ai + } + } else { + normalizedMap[apiGroup] = domain.Kinds{ + APIGroups: []string{apiGroup}, + Kinds: []string{kinditem}, + } + } + } else { + invalidKinds = append(invalidKinds, kinditem) + } + } + } + + if len(invalidKinds) > 0 { + return nil, fmt.Errorf("invalid kinds: %v", invalidKinds) + } + + for _, nornormalized := range normalizedMap { + result = append(result, nornormalized) + } + + return result, nil +} + +func CheckAndConvertToSyncData(kinds []string) (*[][]domain.CompactGVKEquivalenceSet, error) { + invalid_kinds := []string{} + results := []domain.CompactGVKEquivalenceSet{} + + for _, kind := range kinds { + gvk, ok := KindMap[kind] + + if ok { + results = append(results, domain.CompactGVKEquivalenceSet{ + Groups: []string{gvk.Group}, Versions: []string{gvk.Version}, Kinds: []string{gvk.Kind}, + }) + } else { + invalid_kinds = append(invalid_kinds, kind) + } + } + + if len(invalid_kinds) > 0 { + return nil, fmt.Errorf("invalid kinds %v", invalid_kinds) + } + + return &[][]domain.CompactGVKEquivalenceSet{ + results, + }, nil +} + +func MarshalSyncData(syncData *[][]domain.CompactGVKEquivalenceSet) (string, error) { + result, err := json.MarshalIndent(syncData, "", " ") + + if err != nil { + return "", err + } + + return string(result), nil +} + +func ParseAndCheckSyncData(syncjson string) (*[][]domain.CompactGVKEquivalenceSet, error) { + result := [][]domain.CompactGVKEquivalenceSet{} + err := json.Unmarshal([]byte(syncjson), &result) + + if err != nil { + return nil, err + } + + invalid_kinds := []string{} + + for _, sets := range result { + for _, set := range sets { + for _, kind := range set.Kinds { + if _, ok := KindMap[kind]; !ok { + invalid_kinds = append(invalid_kinds, kind) + } + } + } + } + + if len(invalid_kinds) > 0 { + return nil, fmt.Errorf("invalid kinds %v", invalid_kinds) + } + + return &result, nil +} diff --git a/internal/policy-template/paramdef-util.go b/internal/policy-template/paramdef-util.go new file mode 100644 index 00000000..a0e4c64d --- /dev/null +++ b/internal/policy-template/paramdef-util.go @@ -0,0 +1,272 @@ +package policytemplate + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + + "github.com/openinfradev/tks-api/pkg/domain" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +func GetNewParamDefs(paramdefs1 []*domain.ParameterDef, paramdefs2 []*domain.ParameterDef) (newParamdefs []*domain.ParameterDef, err error) { + result := []*domain.ParameterDef{} + + if len(paramdefs1) > len(paramdefs2) { + return nil, errors.New("not compatible, parameter number reduced") + } + + for _, paramdef2 := range paramdefs2 { + paramdef1 := findParamDefByName(paramdefs1, paramdef2.Key) + + if paramdef1 == nil { + // Not found, it's new parameter + result = append(result, paramdef2) + } else if !CompareParamDef(paramdef2, paramdef1) { + return nil, fmt.Errorf("not compatible, parameter definition of '%s' is changed", paramdef2.Key) + } + } + + return result, nil +} + +func GetNewExtractedParamDefs(paramdefs []*domain.ParameterDef, extractedParamdefs []*domain.ParameterDef) (newParamdefs []*domain.ParameterDef, err error) { + result := []*domain.ParameterDef{} + + if len(paramdefs) > len(extractedParamdefs) { + return nil, errors.New("not compatible, parameter number reduced") + } + + for _, extractedParamdef := range extractedParamdefs { + paramdef := findParamDefByName(paramdefs, extractedParamdef.Key) + + if paramdef == nil { + // Not found, it's new parameter + extractedParamdef.MarkNewRecursive() + result = append(result, extractedParamdef) + } else if !CompareParamDefAndExtractedParamDef(paramdef, extractedParamdef) { + return nil, fmt.Errorf("not compatible, parameter definition of '%s' is changed", extractedParamdef.Key) + } + } + + return result, nil +} + +func findParamDefByName(paramdefs []*domain.ParameterDef, name string) *domain.ParameterDef { + for _, paramdef := range paramdefs { + if paramdef.Key == name { + return paramdef + } + } + + return nil +} + +func CompareParamDef(paramdef1 *domain.ParameterDef, paramdef2 *domain.ParameterDef) bool { + if paramdef1 == nil || paramdef2 == nil { + return paramdef2 == paramdef1 + } + + if paramdef1.Key != paramdef2.Key { + return false + } + + if paramdef1.IsArray != paramdef2.IsArray { + return false + } + + if paramdef1.Type != paramdef2.Type { + return false + } + + if paramdef1.DefaultValue != paramdef2.DefaultValue { + return false + } + + if len(paramdef1.Children) != len(paramdef2.Children) { + return false + } + + for _, child := range paramdef1.Children { + child2 := paramdef2.GetChildrenByName(child.Key) + + equals := CompareParamDef(child, child2) + + if !equals { + return false + } + } + + return true +} + +func CompareParamDefAndExtractedParamDef(paramdef *domain.ParameterDef, extractedParamdef *domain.ParameterDef) bool { + if paramdef == nil || extractedParamdef == nil { + return extractedParamdef == paramdef + } + + if paramdef.Key != extractedParamdef.Key { + return false + } + + if paramdef.IsArray != extractedParamdef.IsArray { + return false + } + + // object 기반이면 true, string 등 any 기반이면 false + paramDefIsObjectBased := paramdef.Type == "object" || paramdef.Type == "object[]" + + // ovject 기반이면 true, any, anㅛ[] 등 기반이면 false + extractedParamdefIsObjectBased := paramdef.Type == "object" || paramdef.Type == "object[]" + + if paramDefIsObjectBased != extractedParamdefIsObjectBased { + return false + } + + if len(paramdef.Children) != len(extractedParamdef.Children) { + return false + } + + for _, child := range paramdef.Children { + child2 := extractedParamdef.GetChildrenByName(child.Key) + + equals := CompareParamDefAndExtractedParamDef(child, child2) + + if !equals { + return false + } + } + + return true +} + +func ParamDefsToJSONSchemaProeprties(paramdefs []*domain.ParameterDef, forValidation bool) *apiextensionsv1.JSONSchemaProps { + if len(paramdefs) == 0 { + return nil + } + + result := apiextensionsv1.JSONSchemaProps{ + Type: "object", + Properties: convert(paramdefs, forValidation), + } + + // 파라미터 validation인 경우에는 AddtionalProperties로 스키마에 없는 필드가 처리되지 않아야 함 + // Operator에 보낼때는 해당 속성이 없어야 함 + if forValidation { + result.AdditionalProperties = &apiextensionsv1.JSONSchemaPropsOrBool{Allows: false} + } + + return &result +} + +func convert(paramdefs []*domain.ParameterDef, forValidation bool) map[string]apiextensionsv1.JSONSchemaProps { + result := map[string]apiextensionsv1.JSONSchemaProps{} + + for _, paramdef := range paramdefs { + isArary := paramdef.IsArray + isObject := len(paramdef.Children) > 0 + + switch { + case isArary && isObject: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{ + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: ParamDefsToJSONSchemaProeprties(paramdef.Children, forValidation), + }, + } + case isArary: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{ + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: &apiextensionsv1.JSONSchemaProps{Type: strings.TrimSuffix(paramdef.Type, "[]")}, + }, + } + case isObject: + props := ParamDefsToJSONSchemaProeprties(paramdef.Children, forValidation) + + if props != nil { + result[paramdef.Key] = *props + } + default: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{Type: paramdef.Type} + } + + } + + return result +} + +func JSONSchemaProeprtiesToParamDefs(jsschema *apiextensionsv1.JSONSchemaProps) []*domain.ParameterDef { + return convertToParameterDef(jsschema).Children +} + +func convertToParameterDef(jsschema *apiextensionsv1.JSONSchemaProps) *domain.ParameterDef { + // result := []ParameterDef{} + // fmt.Println(jsschema.Properties) + switch jsschema.Type { + case "array": + itemDef := convertToParameterDef(jsschema.Items.Schema) + itemDef.Type = jsschema.Items.Schema.Type + "[]" + itemDef.IsArray = true + + return itemDef + case "object": + children := []*domain.ParameterDef{} + for kc, vc := range jsschema.Properties { + converted := convertToParameterDef(&vc) + converted.Key = kc + children = append(children, converted) + } + return &domain.ParameterDef{Key: jsschema.ID, Type: jsschema.Type, DefaultValue: "", + Children: children} + default: + defaultValue := "" + + if jsschema.Default != nil { + defaultValue = string(jsschema.Default.Raw) + } + + return &domain.ParameterDef{Key: jsschema.ID, Type: jsschema.Type, DefaultValue: defaultValue, Children: []*domain.ParameterDef{}} + } +} + +func FillParamDefFromJsonStr(paramdefs []*domain.ParameterDef, parameters string) (err error) { + var parametersMap map[string]interface{} + + err = json.Unmarshal([]byte(parameters), ¶metersMap) + + if err != nil { + return err + } + + return FillParamDefFromJson(paramdefs, ¶metersMap) +} + +func FillParamDefFromJson(paramdefs []*domain.ParameterDef, parameters *map[string]interface{}) (err error) { + if len(paramdefs) == 0 || parameters == nil { + return nil + } + + for key, value := range *parameters { + paramdef := findParamDefByName(paramdefs, key) + + if nestedMap, ok := value.(map[string]interface{}); ok { + return FillParamDefFromJson(paramdef.Children, &nestedMap) + } else if nestedMapArray, ok := value.([]map[string]interface{}); ok { + jsonByte, err := json.Marshal(nestedMapArray) + + if err == nil { + paramdef.DefaultValue = string(jsonByte) + } + } else if value != nil { + jsonByte, err := json.Marshal(value) + + if err == nil { + paramdef.DefaultValue = string(jsonByte) + } + } + } + + return nil +} diff --git a/internal/policy-template/policy-operator.go b/internal/policy-template/policy-operator.go new file mode 100644 index 00000000..bb617f05 --- /dev/null +++ b/internal/policy-template/policy-operator.go @@ -0,0 +1,166 @@ +package policytemplate + +import ( + "encoding/json" + "strings" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/pkg/domain" + "gopkg.in/yaml.v3" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + PartOfKey = "app.kubernetes.io/part-of" + PartOfVal = "tks-policy-operator" + TksLabelPrefix = "tks/" + PolicyIDLabel = TksLabelPrefix + "policy-id" + TemplateIDLabel = TksLabelPrefix + "policy-template-id" + RequireSyncDataAnnotation = "metadata.gatekeeper.sh/requires-sync-data" +) + +func PolicyToTksPolicyCR(policy *model.Policy) *TKSPolicy { + if policy == nil { + return nil + } + + var params *apiextensionsv1.JSON = &apiextensionsv1.JSON{Raw: []byte("{}")} + + var jsonResult map[string]interface{} + + err := json.Unmarshal([]byte(policy.Parameters), &jsonResult) + + if err == nil && len(jsonResult) > 0 { + jsonParams := apiextensionsv1.JSON{Raw: []byte(policy.Parameters)} + params = &jsonParams + } + + labels := map[string]string{} + labels[PartOfKey] = PartOfVal + labels[PolicyIDLabel] = policy.ID.String() + labels[TemplateIDLabel] = policy.TemplateId.String() + + if policy.MatchYaml != nil { + var match domain.Match + + err := yaml.Unmarshal([]byte(*policy.MatchYaml), &match) + + if err != nil { + policy.Match = &match + } + } + + targetClusterIds := make([]string, 0) + + if policy.TargetClusterIds != nil { + targetClusterIds = policy.TargetClusterIds + } + + return &TKSPolicy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "tkspolicy.openinfradev.github.io/v1", + Kind: "TKSPolicy", + }, + + ObjectMeta: metav1.ObjectMeta{ + Name: policy.PolicyResourceName, + Labels: labels, + }, + + Spec: TKSPolicySpec{ + EnforcementAction: policy.EnforcementAction, + Clusters: targetClusterIds, + Template: policy.PolicyTemplate.Kind, + Match: policy.Match, + Parameters: params, + }, + } +} + +func PolicyTemplateToTksPolicyTemplateCR(policyTemplate *model.PolicyTemplate) *TKSPolicyTemplate { + if policyTemplate == nil { + return nil + } + + labels := map[string]string{} + labels[PartOfKey] = PartOfVal + labels[TemplateIDLabel] = policyTemplate.ID.String() + + annotations := map[string]string{} + + if policyTemplate.SyncJson != nil { + annotations[RequireSyncDataAnnotation] = "\"" + *policyTemplate.SyncJson + "\"" + } else if policyTemplate.SyncKinds != nil { + sync, err := CheckAndConvertToSyncData(*policyTemplate.SyncKinds) + + if err == nil { + syncVal, err := MarshalSyncData(sync) + if err == nil { + annotations[RequireSyncDataAnnotation] = "\"" + syncVal + "\"" + } + } + } + + var validation *Validation = nil + + if len(policyTemplate.ParametersSchema) > 0 { + validation = &Validation{ + OpenAPIV3Schema: ParamDefsToJSONSchemaProeprties(policyTemplate.ParametersSchema, false), + } + } + + return &TKSPolicyTemplate{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "tkspolicy.openinfradev.github.io/v1", + Kind: "TKSPolicyTemplate", + }, + + ObjectMeta: metav1.ObjectMeta{ + Name: strings.ToLower(policyTemplate.Kind), + Labels: labels, + Annotations: annotations, + }, + + Spec: TKSPolicyTemplateSpec{ + CRD: CRD{ + Spec: CRDSpec{ + Names: Names{ + Kind: policyTemplate.Kind, + }, + Validation: validation, + }, + }, + Targets: []Target{{ + Target: "admission.k8s.gatekeeper.sh", + Rego: stripCarriageReturn(AddTksGuardToRego(policyTemplate.Rego)), + Libs: stripCarriageReturns(policyTemplate.Libs), + }}, + Version: policyTemplate.Version, + }, + } +} + +func stripCarriageReturn(str string) string { + return strings.ReplaceAll(str, "\r", "") +} + +func stripCarriageReturns(strs []string) []string { + if strs == nil { + return nil + } + + result := make([]string, len(strs)) + + for i, str := range strs { + result[i] = stripCarriageReturn(str) + } + + return result + +} + +func syncToKubernetes() bool { + return true + // return os.Getenv("SYNC_POLICY_TO_K8S") != "" +} diff --git a/internal/policy-template/policy-template-rego.go b/internal/policy-template/policy-template-rego.go new file mode 100644 index 00000000..de39beb0 --- /dev/null +++ b/internal/policy-template/policy-template-rego.go @@ -0,0 +1,695 @@ +package policytemplate + +import ( + "fmt" + "regexp" + "sort" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/format" + "github.com/open-policy-agent/opa/types" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/pkg/domain" + "golang.org/x/exp/maps" +) + +const ( + rego_var_name_pattern = `[[a-zA-Z_][a-zA-Z0-9_]*` + input_param_prefix = "input.parameters" + input_extract_pattern = `input(\.parameters|\[\"parameters\"\])((\[\"[\w\-]+\"\])|(\[_\])|(\[` + rego_var_name_pattern + `\])|(\.\w+))*` //(\.\w+)*` // (\.\w+\[\"\w+\"\])|(\.\w+\[\w+\])|(\.\w+))*` + // input_extract_pattern = `input\.parameters((\[\".+\"\])?(\.\w+\[\"\w+\"\])|(\.\w+\[\w+\])|(\.\w+))+` + obj_get_list_pattern = `object\.get\((input|input\.parameters|input\.parameters\.[^,]+), \"([^\"]+)\", \[\]\)` + obj_get_pattern = `object\.get\((input|input\.parameters|input\.parameters\.[^,]+), \"([^\"]+)\", [^\)]+\)` + ref_by_key_pattern = `\[\"([\w-]+)\"\]` + package_name_regex = `package ([\w\.]+)[\n\r]+` + import_regex = `import ([\w\.]+)[\n\r]+` +) + +var ( + input_extract_regex = regexp.MustCompile(input_extract_pattern) + obj_get_list_regex = regexp.MustCompile(obj_get_list_pattern) + obj_get_regex = regexp.MustCompile(obj_get_pattern) + ref_by_key_regex = regexp.MustCompile(ref_by_key_pattern) + ref_by_var_regex = regexp.MustCompile(`\[` + rego_var_name_pattern + `\]`) + array_param_map, capabilities = buildArrayParameterMap() +) + +// OPA 내장 함수 중 array 인자를 가진 함수와 array 인자의 위치를 담은 자료구조를 생성한다. +// // OPA 내장 함수 목록은 정책에 상관없으므로 처음 로딩될 때 한 번만 호출해 변수에 담아두고 사용하면 된다. +// OPA 엔진 버전에 따라 달라진다. 0.62 버전 기준으로 이 함수의 결과 값은 다음과 같다. +// map[all:[true] any:[true] array.concat:[true true] array.reverse:[true] array.slice:[true false false] concat:[false true] count:[true] glob.match:[false true false] graph.reachable:[false true] graph.reachable_paths:[false true] internal.print:[true] json.filter:[false true] json.patch:[false true] json.remove:[false true] max:[true] min:[true] net.cidr_contains_matches:[true true] net.cidr_merge:[true] object.filter:[false true] object.remove:[false true] object.subset:[true true] object.union_n:[true] product:[true] sort:[true] sprintf:[false true] strings.any_prefix_match:[true true] strings.any_suffix_match:[true true] sum:[true] time.clock:[true] time.date:[true] time.diff:[true true] time.format:[true] time.weekday:[true]] +func buildArrayParameterMap() (map[string][]bool, *ast.Capabilities) { + compiler := ast.NewCompiler() + + // 아주 단순한 rego 코드를 컴파일해도 컴파일러의 모든 Built-In 함수 정보를 컴파일 할 수 있음 + mod, err := ast.ParseModuleWithOpts("hello", "package hello\n hello {input.message = \"world\"}", ast.ParserOptions{}) + if err != nil { + return nil, nil + } + + // 컴파일을 수행해야 Built-in 함수 정보 로딩할 수 있음 + modules := map[string]*ast.Module{} + modules["hello"] = mod + compiler.Compile(modules) + + var external_data = &ast.Builtin{ + Name: "external_data", + Decl: types.NewFunction( + types.Args( + types.Named("a", types.NewObject( + []*types.StaticProperty{ + types.NewStaticProperty("provider", types.S), + types.NewStaticProperty("keys", types.NewArray([]types.Type{types.S}, types.A)), + }, + nil, + )), + ), + types.Named("output", types.A), + ), // TODO(sr): types.A? ^^^^^^^ (also below) + } + + capabilities := compiler.Capabilities() + capabilities.Builtins = append(capabilities.Builtins, external_data) + + return getArrayParameterMap(compiler), capabilities +} + +func getArrayParameterMap(compiler *ast.Compiler) map[string][]bool { + capabilities := compiler.Capabilities() + + var result = map[string][]bool{} + + if capabilities != nil { + for _, builtin := range capabilities.Builtins { + args := builtin.Decl.FuncArgs().Args + isArrayParam := make([]bool, len(args)) + + arrayCount := 0 + for i, typeVal := range args { + isArrayParam[i] = IsArray(typeVal) + + if isArrayParam[i] { + arrayCount += 1 + } + } + + if arrayCount > 0 { + result[builtin.Name] = isArrayParam + } + } + } + + return result +} + +func IsArray(t types.Type) bool { + switch specific_type := t.(type) { + case *types.Array: + return true + case types.Any: + { + for _, anyType := range specific_type { + if IsArray(anyType) { + return true + } + } + } + } + + return false +} + +func extractInputExprFromModule(module *ast.Module) []string { + rules := module.Rules + + passedInputMap := []string{} + globalAssignMap := map[string]string{} + + violationRule := []*ast.Rule{} + nonViolatonRule := []*ast.Rule{} + + for _, rule := range rules { + if rule.Head.Name == "violation" { + violationRule = append(violationRule, rule) + } else { + nonViolatonRule = append(nonViolatonRule, rule) + + if rule.Head.Assign && rule.Head.Value != nil { + if _, ok := rule.Head.Value.Value.(ast.Call); !ok { + globalAssignMap[string(rule.Head.Name)] = rule.Head.Value.String() + } + } + } + } + + paramRefs := map[string]string{} + + for _, rule := range violationRule { + processRule(rule, globalAssignMap, paramRefs, passedInputMap, nonViolatonRule) + } + + // 중복제거를 위해 사용한 맵을 소팅하기 위해 키 리스트로 변환 + paramRefsList := maps.Keys(paramRefs) + + /* 가장 depth가 깊은 변수부터 우선 처리해서 최초 trverse 시 child가 있는지 효율적으로 확인하기 위해 역순으로 정렬 + 이러한 소팅을 통해서 input.parameters.labels[_] 보다는 input.parameters.labels[_]의 자식들이 리스트의 앞순에 위치하도록 함 + input.parameters.labels[_]가 먼저 오면 input.parameters.labels[_]의 자식들이 존재 여부는 이 후 리스트 목록에 따라 달라지지만 + 역순으로 소팅한 상태에서는 항상 자식들이 리스트 앞에 위치 + + input.parameters.labels[_].key + input.parameters.labels[_].allowedRegex + input.parameters.labels[_] + */ + sort.Sort(sort.Reverse(sort.StringSlice(paramRefsList))) + + return paramRefsList +} + +func processRule(rule *ast.Rule, globalAssignMap map[string]string, paramRefs map[string]string, passedParams []string, nonViolatonRule []*ast.Rule) map[string]string { + localAssignMap := map[string]string{} + + // 규칙이 단순 assign이면 value의 정책 호출을 따라가 봐야 함 + if rule.Head.Assign { + if call, ok := rule.Head.Value.Value.(ast.Call); ok { + ruleName := call[0].String() + + args := call[1:] + + argStrs := make([]string, len(args)) + + for i, arg := range args { + argStrs[i] = arg.String() + } + + for _, nvrule := range nonViolatonRule { + if ruleName == nvrule.Head.Name.String() { + return processRule(nvrule, globalAssignMap, paramRefs, argStrs, nonViolatonRule) + } + } + } else { + value := rule.Head.Value.String() + + if isSubstitutionRequired(value) { + paramRefs[value] = "1" + + localAssignMap[string(rule.Head.Name)] = value + + // fmt.Println("1818181818", rule.Head.Value) + return localAssignMap + } + } + + // 더 처리할 건 없음 + return nil + } + + exprs := rule.Body + + for i, param := range passedParams { + if isSubstitutionRequired(param) { + argName := rule.Head.Args[i].String() + localAssignMap[argName] = param + } + } + + for _, expr := range exprs { + exprString := expr.String() + + if len(localAssignMap) > 0 { + exprString = substituteWithAssignMap(localAssignMap, exprString) + exprString = replaceAllObjectGet(exprString) + exprString = substituteWithAssignMap(localAssignMap, exprString) + } + + if len(globalAssignMap) > 0 { + exprString = substituteWithAssignMap(globalAssignMap, exprString) + exprString = replaceAllObjectGet(exprString) + exprString = substituteWithAssignMap(globalAssignMap, exprString) + } + + matches := input_extract_regex.FindAllString(exprString, -1) + + if len(matches) > 0 { + for _, match := range matches { + paramRefs[match] = "1" + } + } + + updateLocalAssignMap(expr, localAssignMap) + + if expr.IsCall() { + call, _ := expr.Terms.([]*ast.Term) + if len(call) > 1 { + ruleName := call[0].String() + args := call[1:] + + inputPassed, passingParams := processingInputArgs(args, localAssignMap) + + if inputPassed { + if is_arrays, ok := array_param_map[ruleName]; ok { + for i, passingParam := range passingParams { + is_array := is_arrays[i] + + if is_array && strings.HasPrefix(passingParam, "input.parameters.") && + !strings.HasSuffix(passingParam, "[_]") { + + paramRefs[passingParam+"[_]"] = "1" + } + } + } + + for _, nvrule := range nonViolatonRule { + if ruleName == nvrule.Head.Name.String() { + updateLocals := processRule(nvrule, globalAssignMap, paramRefs, passingParams, nonViolatonRule) + for k, v := range updateLocals { + if _, ok := localAssignMap[k]; !ok { + localAssignMap[k] = v + } + } + } + } + } + } + } + + ast.WalkTerms(expr, func(t *ast.Term) bool { + switch t.Value.(type) { + case ast.Ref: // 인자가 없는 정책을 단순히 호출하는 경우, 호출하는 정책을 따라가 봄 + { + ruleRef := ([]*ast.Term)(t.Value.(ast.Ref)) + ruleName := ruleRef[0].Value.String() + + for _, nvrule := range nonViolatonRule { + if ruleName == nvrule.Head.Name.String() { + updateLocals := processRule(nvrule, globalAssignMap, paramRefs, []string{}, nonViolatonRule) + + for k, v := range updateLocals { + if _, ok := localAssignMap[k]; !ok { + localAssignMap[k] = v + } + } + } + } + } + case ast.Call: // 인자가 있는 정책 호출, input 치환되는 인자가 있으면 passingParams으로 전달 + call := ([]*ast.Term)(t.Value.(ast.Call)) + + // 인자가 없으므로 처리 불필요 + if len(call) < 2 { + return false + } + + ruleName := call[0].String() + + args := call[1:] + + inputPassed, passingParams := processingInputArgs(args, localAssignMap) + + if inputPassed { + for _, nvrule := range nonViolatonRule { + if ruleName == nvrule.Head.Name.String() { + updateLocals := processRule(nvrule, globalAssignMap, paramRefs, passingParams, nonViolatonRule) + + for k, v := range updateLocals { + if _, ok := localAssignMap[k]; !ok { + localAssignMap[k] = v + } + } + } + } + } + + return false + default: + for _, nvrule := range nonViolatonRule { + ruleName := nvrule.Head.Name.String() + if t.Value.String() == ruleName { + updateLocals := processRule(nvrule, globalAssignMap, paramRefs, []string{}, nonViolatonRule) + + for k, v := range updateLocals { + if _, ok := localAssignMap[k]; !ok { + localAssignMap[k] = v + } + } + } + } + + return false + } + + return false + }) + } + + headKey := rule.Head.Key + + if headKey != nil { + for _, nvrule := range nonViolatonRule { + ruleName := nvrule.Head.Name.String() + if strings.Contains(headKey.String(), ruleName) { + updateLocals := processRule(nvrule, globalAssignMap, paramRefs, []string{}, nonViolatonRule) + + for k, v := range updateLocals { + if _, ok := localAssignMap[k]; !ok { + localAssignMap[k] = v + } + } + } + } + } + + return nil +} + +// object.get(object.get(input, "parameters", {}), "exemptImages", [])) -> input.parameters.exemptImages와 같은 패턴 변환 +func replaceAllObjectGet(expr string) string { + if !strings.Contains(expr, "object.get") { + return expr + } + + result := obj_get_list_regex.ReplaceAllString(expr, "$1.$2"+`[_]`) + result = obj_get_regex.ReplaceAllString(result, "$1.$2") + + // 정규식의 영향 없음 그냥 리턴 + if result == expr { + return expr + } + + return replaceAllObjectGet(result) +} + +func processingInputArgs(args []*ast.Term, localAssignMap map[string]string) (bool, []string) { + inputPassed := false + passingParams := []string{} + + for i := 0; i < len(args); i++ { + if args[i] != nil { + arg := args[i].String() + + if len(localAssignMap) > 0 { + arg = substituteWithAssignMap(localAssignMap, arg) + arg = replaceAllObjectGet(arg) + arg = substituteWithAssignMap(localAssignMap, arg) + } + + if isSubstitutionRequired(arg) { + passingParams = append(passingParams, arg) + inputPassed = true + } else { + passingParams = append(passingParams, "") + } + } + } + return inputPassed, passingParams +} + +func updateLocalAssignMap(expr *ast.Expr, localAssignMap map[string]string) { + if expr.IsAssignment() { + vars := expr.Operand(0).Vars() + assigned := replaceAllObjectGet(expr.Operand(1).String()) + + if len(vars) == 1 && isSubstitutionRequired(assigned) { + localAssignMap[expr.Operand(0).String()] = assigned + } + } +} + +func substituteWithAssignMap(assignMap map[string]string, exprString string) string { + for k, v := range assignMap { + if strings.Contains(exprString, v) { + continue + } else if exprString == k { + exprString = v + } else { + pattern := `(\W)` + k + `(\W)` + + rx := regexp.MustCompile(pattern) + + exprString = rx.ReplaceAllString(exprString, `${1}`+v+`${2}`) + } + + } + return exprString +} + +func isSubstitutionRequired(expr string) bool { + trimmed := strings.TrimSpace(expr) + + // input.review 등 input.parameters가 아닌 input은 해석할 필요 없음 + // input이 assign 되었으면 input.review가 될지 parameter가 될지 모르므로 일단 input으로 대체 필요 + // input.parameters 자체를 변수로 assign하는 패턴도 처리 필요 + // input.parameters 하위의 속성도 처리 필요 + return trimmed == "input" || + trimmed == "input.parameters" || + strings.HasPrefix(trimmed, "input.parameters.") +} + +func ExtractParameter(modules map[string]*ast.Module) []*domain.ParameterDef { + defStore := NewParamDefStore() + + for _, module := range modules { + inputExprs := extractInputExprFromModule(module) + + for _, inputExpr := range inputExprs { + remainder := inputExpr[len(input_param_prefix):] + + // 문법 변환: aa["a"]["B"][_]->aa.a.B[_] + remainder = ref_by_key_regex.ReplaceAllString(remainder, ".${1}") + + // 문법 변환2: input.parameters.aa[varname]에서 aa는 객체가 아닌 배열이라고 확신할 수 있으므로 + // input.parameters.aa[_]로 변환해도 무방함 + remainder = ref_by_var_regex.ReplaceAllString(remainder, "[_]") + + params := strings.Split(remainder, ".") + + if len(params) == 0 { + continue + } + + defStore.AddDefinition(params) + } + + } + + return defStore.store +} + +type ParamDefStore struct { + store []*domain.ParameterDef +} + +func NewParamDefStore() *ParamDefStore { + return &ParamDefStore{store: []*domain.ParameterDef{}} +} + +func (s *ParamDefStore) GetStore() []*domain.ParameterDef { + return s.store +} + +func (s *ParamDefStore) AddDefinition(params []string) { + init := &s.store + + for i, param := range params { + if param == "" { + continue + } + + isLast := i == len(params)-1 + + key := findKey(*init, param) + + if key == nil { + key = createKey(param, isLast) + + *init = append(*init, key) + } + + init = &key.Children + } +} + +func findKey(defs []*domain.ParameterDef, key string) *domain.ParameterDef { + for _, def := range defs { + if def.Key == key || def.Key+"[_]" == key { + return def + } + } + + return nil +} + +func cutTrailingArrayNote(val string) string { + cut, found := strings.CutSuffix(val, "[_]") + + if found { + return cutTrailingArrayNote(cut) + } + + return val +} + +func createKey(key string, isLast bool) *domain.ParameterDef { + var finalType string + + pKey := key + isArray := false + + if strings.HasSuffix(pKey, "[_]") { + pKey = cutTrailingArrayNote(pKey) + isArray = true + } + + if isLast { + if isArray { + finalType = "any[]" + } else { + finalType = "any" + } + } else { + if isArray { + finalType = "object[]" + } else { + finalType = "object" + } + } + + newDef := &domain.ParameterDef{ + Key: pKey, + Type: finalType, + Children: []*domain.ParameterDef{}, + IsArray: isArray, + } + + return newDef +} + +func processLibs(libs []string) []string { + // libs 에 --- 로 코딩되어 여러 개가 한 번에 들어온 경우 분할 + newLibs := []string{} + for _, lib := range libs { + newLibs = append(newLibs, strings.Split(stripCarriageReturn(lib), model.FILE_DELIMETER)...) + } + + return newLibs +} + +func CompileRegoWithLibs(rego string, libs []string) (compiler *ast.Compiler, err error) { + modules := map[string]*ast.Module{} + + regoPackage := GetPackageFromRegoCode(rego) + + regoModule, err := ast.ParseModuleWithOpts(regoPackage, rego, ast.ParserOptions{Capabilities: capabilities}) + if err != nil { + return nil, err + } + + modules[regoPackage] = regoModule + + for i, lib := range processLibs(libs) { + // Lib이 공백이면 무시 + if len(strings.TrimSpace(lib)) == 0 { + continue + } + + libPackage := GetPackageFromRegoCode(lib) + + // Lib의 패키지 명이 공백이면 rego에서 import 될 수 없기 때문에 에러 처리 + // 패키지 명이 Parse할 때 비어있으면 에러가 나지만, rego인지 lib인지 정확히 알기 어려울 수 있으므로 알려 줌 + if len(strings.TrimSpace(libPackage)) == 0 { + return nil, fmt.Errorf("lib[%d] is not valid, empty package name", i) + } + + libModule, err := ast.ParseModuleWithOpts(libPackage, lib, ast.ParserOptions{Capabilities: capabilities}) + if err != nil { + return nil, err + } + + modules[libPackage] = libModule + } + + compiler = ast.NewCompiler().WithCapabilities(capabilities) + + compiler.Compile(modules) + + return compiler, nil +} + +func MergeRegoAndLibs(rego string, libs []string) string { + if len(libs) == 0 { + return rego + } + + var re = regexp.MustCompile(import_regex) + var re2 = regexp.MustCompile(package_name_regex) + + result := re.ReplaceAllString(rego, "") + + for _, lib := range processLibs(libs) { + result += re2.ReplaceAllString(lib, "") + } + + return result +} + +func MergeAndCompileRegoWithLibs(rego string, libs []string) (modules map[string]*ast.Module, err error) { + modules = map[string]*ast.Module{} + + regoPackage := GetPackageFromRegoCode(rego) + + merged := MergeRegoAndLibs(rego, libs) + + module, err := ast.ParseModuleWithOpts(regoPackage, merged, ast.ParserOptions{Capabilities: capabilities}) + if err != nil { + return modules, err + } + + modules[regoPackage] = module + + compiler := ast.NewCompiler().WithCapabilities(capabilities) + + compiler.Compile(modules) + + return modules, nil +} + +func GetPackageFromRegoCode(regoCode string) string { + packageRegex := regexp.MustCompile(package_name_regex) + + match := packageRegex.FindStringSubmatch(regoCode) + + if len(match) > 1 { + return match[1] + } + + return "" +} + +func FormatRegoCode(rego string) string { + packageName := GetPackageFromRegoCode(rego) + + // 패키지 명을 파싱할 수 없으면 포맷팅할 수 있는 코드가 아닐 것이므로 그냥 리턴 + if packageName == "" { + return rego + } + + bytes, err := format.Source("rego", []byte(rego)) + + if err != nil { + return rego + } + + return strings.Replace(string(bytes), "\t", " ", -1) +} + +func FormatLibCode(libs []string) []string { + processedLibs := processLibs(libs) + + result := make([]string, len(processedLibs)) + + for i, lib := range processedLibs { + result[i] = FormatRegoCode(lib) + } + + return result +} diff --git a/internal/policy-template/tkscluster.go b/internal/policy-template/tkscluster.go new file mode 100644 index 00000000..2a6cd1df --- /dev/null +++ b/internal/policy-template/tkscluster.go @@ -0,0 +1,119 @@ +package policytemplate + +import ( + "context" + + "github.com/openinfradev/tks-api/pkg/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var TKSClusterGVR = schema.GroupVersionResource{ + Group: "tkspolicy.openinfradev.github.io", Version: "v1", + Resource: "tksclusters", +} + +// ============== Copied From Operator Start ============== + +// TemplateReference defines the desired state of TKSCluster +type TemplateReference struct { + Policies map[string]string `json:"polices,omitempty"` + Templates map[string]string `json:"templates,omitempty"` +} + +// TKSClusterSpec defines the desired state of TKSCluster +type TKSClusterSpec struct { + ClusterName string `json:"clusterName" validate:"required"` + Context string `json:"context" validate:"required"` +} + +// DeploymentInfo defines the observed status of the proxy +type DeploymentInfo struct { + Image string `json:"image,omitempty"` + Args []string `json:"args,omitempty"` + TotalReplicas int `json:"totalReplicas,omitempty"` + NumReplicas int `json:"numReplicas,omitempty"` +} + +// TKSProxy defines the observed proxy state for each cluster +type TKSProxy struct { + Status string `json:"status" enums:"ready,warn,error"` + ControllerManager *DeploymentInfo `json:"controllerManager,omitempty"` + Audit *DeploymentInfo `json:"audit,omitempty"` +} + +// TKSClusterStatus defines the observed state of TKSCluster +type TKSClusterStatus struct { + Status string `json:"status" enums:"running,deleting,error"` + Error string `json:"error,omitempty"` + TKSProxy TKSProxy `json:"tksproxy,omitempty"` + LastStatusCheckTime int64 `json:"laststatuschecktime,omitempty"` + Templates map[string][]string `json:"templates,omitempty"` + LastUpdate string `json:"lastUpdate"` + UpdateQueue map[string]bool `json:"updateQueue,omitempty"` +} + +// TKSCluster is the Schema for the tksclusters API +type TKSCluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TKSClusterSpec `json:"spec,omitempty"` + Status TKSClusterStatus `json:"status,omitempty"` +} + +// TKSClusterList contains a list of TKSCluster +type TKSClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TKSCluster `json:"items"` +} + +// ============== Copied From Operator End ============== + +func GetTksClusterCR(ctx context.Context, primaryClusterId string, resourceName string) (*TKSCluster, error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + if err != nil { + return nil, err + } + + resource, err := dynamicClient.Resource(TKSClusterGVR).Namespace(primaryClusterId). + Get(ctx, resourceName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + + var tksCluster TKSCluster + unstructuredObj := resource.UnstructuredContent() + err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredObj, &tksCluster) + if err != nil { + return nil, err + } + + return &tksCluster, nil +} + +func GetTksClusterCRs(ctx context.Context, primaryClusterId string) (tksClusters []TKSCluster, err error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + if err != nil { + return nil, err + } + + resources, err := dynamicClient.Resource(TKSClusterGVR).Namespace(primaryClusterId). + List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + + var tksCluster TKSCluster + for _, resource := range resources.Items { + if err = runtime.DefaultUnstructuredConverter. + FromUnstructured(resource.UnstructuredContent(), &tksCluster); err != nil { + return nil, err + } + tksClusters = append(tksClusters, tksCluster) + } + + return tksClusters, nil +} diff --git a/internal/policy-template/tksguard-rego.go b/internal/policy-template/tksguard-rego.go new file mode 100644 index 00000000..e19a94bc --- /dev/null +++ b/internal/policy-template/tksguard-rego.go @@ -0,0 +1,42 @@ +package policytemplate + +import "regexp" + +// (?m)은 멀티라인 모드로 각 라인의 시작이 ^레 매칭되도록 처리 +// general_violation 등 violation을 포함하지만 violation이 아닌 정책을 매칭하지 않기 위해 멀티라인 모드 필요함 +// OPA 포맷팅하면 violation rule은 공백없이 violation[ 으로 시작하므로 개행 문자 전까지 매칭 +const violation_regex_pattern = `(?m)^violation\[[^\n\r]+[\n\r]+` + +var violation_regex = regexp.MustCompile(violation_regex_pattern) + +// violation 정책 헤드 매칭 후 다음에 삽입할 주석 및 가드 정책 +const tks_guard_rego_rulename = ` # Do not delete following line, added by TKS + ___not_tks_triggered_request___ + +` + +// 가드 정책의 내용 +// 해당 정책이 undefined로 빠지면 violation의 뒷 부분이 평가되지 않음 +// 처음 블럭은 userInfo가 설정되지 않은 audit 모드에서 정책 평가가 스킵되는 것을 방지하기 위한처리 +// 그 다음 블럭은 username이 tks_users 목록에 없고, tks_groups와 groups의 교집합 크기가 0인 경우에 true이며 그 외는 undefined +// 죽 username 및 groups가 정의된 리스트와 매칭되는 것이 하나라도 있으면 정책이 undefined가 됨 +const tks_guard_rego_rulelogic = ` +# Do not delete or edit following rule, managed by TKS +___not_tks_triggered_request___ { + not input.review.userInfo +} { + tks_users := {"kubernetes-admin","system:serviceaccount:kube-system:argocd-manager"} + tks_groups := {"system:masters"} + + not tks_users[input.review.userInfo.username] + + count({g |g := input.review.userInfo.groups[_]; tks_groups[g]}) == 0 +} +# Do not delete or edit end` + +// violation 정책에 가드 정책 추가 +func AddTksGuardToRego(rego string) string { + // 매칭되는 violation 정책의 바디 첫부분에 가드 정책 체크를 추가하고 rego 코드 맨 끝에 실제 코드 내용 추가함 + return violation_regex.ReplaceAllString(rego, `${0}`+tks_guard_rego_rulename) + + tks_guard_rego_rulelogic +} diff --git a/internal/policy-template/tkspolicy.go b/internal/policy-template/tkspolicy.go new file mode 100644 index 00000000..e5be875b --- /dev/null +++ b/internal/policy-template/tkspolicy.go @@ -0,0 +1,300 @@ +package policytemplate + +import ( + "context" + "encoding/json" + + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/kubernetes" + "gopkg.in/yaml.v3" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var TKSPolicyGVR = schema.GroupVersionResource{ + Group: "tkspolicy.openinfradev.github.io", Version: "v1", + Resource: "tkspolicies", +} + +// ============== Copied Fron Operator Start ============== +// match.Match는 domain.Match로 변경해야 함s + +// TKSPolicySpec defines the desired state of TKSPolicy +type TKSPolicySpec struct { + Clusters []string `json:"clusters"` + Template string `json:"template" validate:"required"` + + Parameters *apiextensionsv1.JSON `json:"parameters,omitempty"` + Match *domain.Match `json:"match,omitempty"` + EnforcementAction string `json:"enforcementAction,omitempty"` +} + +// PolicyStatus defines the constraints state on the cluster +type PolicyStatus struct { + ConstraintStatus string `json:"constraintStatus" enums:"ready,applying,deleting,error"` + Reason string `json:"reason,omitempty"` + LastUpdate string `json:"lastUpdate"` + TemplateVersion string `json:"templateVersion"` +} + +// TKSPolicyStatus defines the observed state of TKSPolicy +type TKSPolicyStatus struct { + Clusters map[string]PolicyStatus `json:"clusters,omitempty"` + LastUpdate string `json:"lastUpdate"` + UpdateQueue map[string]bool `json:"updateQueue,omitempty"` + Reason string `json:"reason,omitempty"` +} + +// TKSPolicy is the Schema for the tkspolicies API +type TKSPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TKSPolicySpec `json:"spec,omitempty"` + Status TKSPolicyStatus `json:"status,omitempty"` +} + +// TKSPolicyList contains a list of TKSPolicy +type TKSPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TKSPolicy `json:"items"` +} + +// ============== Copied Fron Operator End ============== + +func (tksPolicy *TKSPolicy) GetPolicyID() string { + return tksPolicy.ObjectMeta.Labels[PolicyIDLabel] +} + +func (tksPolicy *TKSPolicy) GetTemplateID() string { + return tksPolicy.ObjectMeta.Labels[TemplateIDLabel] +} + +func (tksPolicy *TKSPolicy) JSON() (string, error) { + result, err := json.MarshalIndent(tksPolicy, "", " ") + + if err != nil { + return "", err + } + + return string(result), nil +} + +func (tksPolicy *TKSPolicy) YAML() (string, error) { + target := map[string]interface{}{} + + jsonStr, err := tksPolicy.JSON() + + if err != nil { + return "", err + } + + err = json.Unmarshal([]byte(jsonStr), &target) + + if err != nil { + return "", err + } + + result, err := yaml.Marshal(&target) + + if err != nil { + return "", err + } + + return string(result), nil +} + +func (tksPolicy *TKSPolicy) ToUnstructured() (*unstructured.Unstructured, error) { + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tksPolicy) + + if err != nil { + return nil, err + } + + tksPolicyUnstructured := &unstructured.Unstructured{ + Object: obj, + } + + return tksPolicyUnstructured, nil +} + +func ApplyTksPolicyCR(ctx context.Context, primaryClusterId string, tksPolicy *TKSPolicy) error { + if syncToKubernetes() { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + return err + } + + policy, err := GetTksPolicyCR(ctx, primaryClusterId, tksPolicy.Name) + + if err != nil { + if errors.IsNotFound(err) { + tksPolicyUnstructured, err := tksPolicy.ToUnstructured() + + if err != nil { + return err + } + + _, err = dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + Create(ctx, tksPolicyUnstructured, metav1.CreateOptions{}) + return err + } else { + return err + } + } + + policy.Spec = tksPolicy.Spec + tksPolicyUnstructured, err := policy.ToUnstructured() + + if err != nil { + return err + } + + _, err = dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + Update(ctx, tksPolicyUnstructured, metav1.UpdateOptions{}) + + return err + } + return nil +} + +func DeleteTksPolicyCR(ctx context.Context, primaryClusterId string, name string) error { + if syncToKubernetes() { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + return err + } + + err = dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + Delete(ctx, name, metav1.DeleteOptions{}) + + return err + } + return nil +} + +func GetTksPolicyCR(ctx context.Context, primaryClusterId string, name string) (*TKSPolicy, error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + return nil, err + } + + result, err := dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + Get(ctx, name, metav1.GetOptions{}) + + if err != nil { + return nil, err + } + + // Unstructured를 바로 TKSPolicyTemplate으로 컨버팅할 수 없기 때문에 json으로 변환 + jsonBytes, err := json.Marshal(result.Object) + + if err != nil { + return nil, err + } + + var tksPolicy TKSPolicy + err = json.Unmarshal(jsonBytes, &tksPolicy) + + if err != nil { + return nil, err + } + + return &tksPolicy, nil +} + +func ExistsTksPolicyCR(ctx context.Context, primaryClusterId string, name string) (bool, error) { + if syncToKubernetes() { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + return false, err + } + + result, err := dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + Get(ctx, name, metav1.GetOptions{}) + + if err != nil { + if errors.IsNotFound(err) { + return false, nil + } else { + return false, err + } + } + + return result != nil, nil + } + return true, nil +} + +//func ListTksPolicyCR(ctx context.Context, primaryClusterId string) ([]TKSPolicy, error) { +// if syncToKubernetes() { +// dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) +// +// if err != nil { +// return nil, err +// } +// +// results, err := dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). +// List(ctx, metav1.ListOptions{}) +// +// if err != nil { +// return nil, err +// } +// +// tkspolicies := make([]TKSPolicy, len(results.Items)) +// +// for i, result := range results.Items { +// jsonBytes, err := json.Marshal(result.Object) +// +// if err != nil { +// return nil, err +// } +// +// var tksPolicy TKSPolicy +// err = json.Unmarshal(jsonBytes, &tksPolicy) +// +// if err != nil { +// return nil, err +// } +// +// tkspolicies[i] = tksPolicy +// } +// +// return tkspolicies, nil +// } +// +// tkspolicies := make([]TKSPolicy, 0) +// return tkspolicies, nil +//} + +func GetTksPolicyCRs(ctx context.Context, primaryClusterId string) (tksPolicies []TKSPolicy, err error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + if err != nil { + return nil, err + } + + resources, err := dynamicClient.Resource(TKSPolicyGVR).Namespace(primaryClusterId). + List(context.TODO(), metav1.ListOptions{}) + if err != nil { + return nil, err + } + + var tksPolicy TKSPolicy + for _, c := range resources.Items { + if err = runtime.DefaultUnstructuredConverter.FromUnstructured(c.UnstructuredContent(), &tksPolicy); err != nil { + return nil, err + } + tksPolicies = append(tksPolicies, tksPolicy) + } + + return tksPolicies, nil +} diff --git a/internal/policy-template/tkspolicytemplate.go b/internal/policy-template/tkspolicytemplate.go new file mode 100644 index 00000000..9cb8e27f --- /dev/null +++ b/internal/policy-template/tkspolicytemplate.go @@ -0,0 +1,399 @@ +package policytemplate + +import ( + "context" + "encoding/json" + "strings" + + "github.com/openinfradev/tks-api/pkg/kubernetes" + "github.com/openinfradev/tks-api/pkg/log" + "gopkg.in/yaml.v3" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var TKSPolicyTemplateGVR = schema.GroupVersionResource{ + Group: "tkspolicy.openinfradev.github.io", Version: "v1", + Resource: "tkspolicytemplates", +} + +type Anything struct { + Value interface{} `json:"-"` +} + +type CRD struct { + Spec CRDSpec `json:"spec,omitempty"` +} + +type CRDSpec struct { + Names Names `json:"names,omitempty"` + Validation *Validation `json:"validation,omitempty"` +} + +type Names struct { + Kind string `json:"kind,omitempty"` + ShortNames []string `json:"shortNames,omitempty"` +} + +type Validation struct { + OpenAPIV3Schema *apiextensionsv1.JSONSchemaProps `json:"openAPIV3Schema,omitempty"` + LegacySchema *bool `json:"legacySchema,omitempty"` // *bool allows for "unset" state which we need to apply appropriate defaults +} + +type Target struct { + Target string `json:"target,omitempty"` + Rego string `json:"rego,omitempty" yaml:"rego,omitempty"` + Libs []string `json:"libs,omitempty" yaml:"libs,omitempty"` + Code []Code `json:"code,omitempty"` +} + +type Code struct { + Engine string `json:"engine"` + Source *Anything `json:"source"` +} + +// ============== Copied Fron Operator Start ============== +// CRD, Target struct는 이 파일의 것을 사용 + +// TKSPolicyTemplateSpec defines the desired state of TKSPolicyTemplate +type TKSPolicyTemplateSpec struct { + CRD CRD `json:"crd,omitempty"` + Targets []Target `json:"targets,omitempty"` + Clusters []string `json:"clusters,omitempty"` + Version string `json:"version"` + ToLatest []string `json:"toLatest,omitempty"` +} + +// TemplateStatus defines the constraints state of ConstraintTemplate on the cluster +type TemplateStatus struct { + ConstraintTemplateStatus string `json:"constraintTemplateStatus" enums:"ready,applying,deleting,error"` + Reason string `json:"reason,omitempty"` + LastUpdate string `json:"lastUpdate"` + Version string `json:"version"` +} + +// TKSPolicyTemplateStatus defines the observed state of TKSPolicyTemplate +type TKSPolicyTemplateStatus struct { + TemplateStatus map[string]TemplateStatus `json:"templateStatus,omitempty"` + LastUpdate string `json:"lastUpdate"` + UpdateQueue map[string]bool `json:"updateQueue,omitempty"` +} + +// TKSPolicyTemplate is the Schema for the tkspolicytemplates API +type TKSPolicyTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TKSPolicyTemplateSpec `json:"spec,omitempty"` + Status TKSPolicyTemplateStatus `json:"status,omitempty"` +} + +// TKSPolicyTemplateList contains a list of TKSPolicyTemplate +type TKSPolicyTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TKSPolicyTemplate `json:"items"` +} + +// ============== Copied Fron Operator End ============== + +func (tksPolicyTemplate *TKSPolicyTemplate) JSON() (string, error) { + result, err := json.MarshalIndent(tksPolicyTemplate, "", " ") + + if err != nil { + return "", err + } + + return string(result), nil +} + +func (tksPolicyTemplate *TKSPolicyTemplate) YAML() (string, error) { + target := map[string]interface{}{} + + jsonStr, err := tksPolicyTemplate.JSON() + + if err != nil { + return "", err + } + + err = json.Unmarshal([]byte(jsonStr), &target) + + if err != nil { + return "", err + } + + result, err := yaml.Marshal(&target) + + if err != nil { + return "", err + } + + return string(result), nil +} + +func (tksPolicyTemplate *TKSPolicyTemplate) ToUnstructured() (*unstructured.Unstructured, error) { + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tksPolicyTemplate) + + if err != nil { + return nil, err + } + + tksPolicyTemplateUnstructured := &unstructured.Unstructured{ + Object: obj, + } + + return tksPolicyTemplateUnstructured, nil +} + +func (tksPolicyTemplate *TKSPolicyTemplate) GetId() string { + return tksPolicyTemplate.ObjectMeta.Labels[TemplateIDLabel] +} + +func ApplyTksPolicyTemplateCR(ctx context.Context, primaryClusterId string, tksPolicyTemplate *TKSPolicyTemplate) error { + if syncToKubernetes() { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + return err + } + + policyTemplate, err := GetTksPolicyTemplateCR(ctx, primaryClusterId, strings.ToLower(tksPolicyTemplate.Kind)) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + if errors.IsNotFound(err) { + tksPolicyTemplateUnstructured, err := tksPolicyTemplate.ToUnstructured() + + if err != nil { + return err + } + + _, err = dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Create(ctx, tksPolicyTemplateUnstructured, metav1.CreateOptions{}) + return err + } else { + return err + } + } + + policyTemplate.Spec = tksPolicyTemplate.Spec + tksPolicyTemplateUnstructured, err := policyTemplate.ToUnstructured() + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + return err + } + + _, err = dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Update(ctx, tksPolicyTemplateUnstructured, metav1.UpdateOptions{}) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + } + + return err + } + return nil +} + +func DeleteTksPolicyTemplateCR(ctx context.Context, primaryClusterId string, name string) error { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return err + } + + err = dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Delete(ctx, name, metav1.DeleteOptions{}) + + // 삭제할 리소스가 존재하지 않았다면 성공으로 처리 + if errors.IsNotFound(err) { + return nil + } + + return err +} + +func ExistsTksPolicyTemplateCR(ctx context.Context, primaryClusterId string, name string) (bool, error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return false, err + } + + result, err := dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Get(ctx, name, metav1.GetOptions{}) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + + if errors.IsNotFound(err) { + return false, nil + } else { + return false, err + } + } + + return result != nil, nil +} + +func GetTksPolicyTemplateCR(ctx context.Context, primaryClusterId string, name string) (*TKSPolicyTemplate, error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return nil, err + } + + result, err := dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Get(ctx, name, metav1.GetOptions{}) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return nil, err + } + + // Unstructured를 바로 TKSPolicyTemplate으로 컨버팅할 수 없기 때문에 json으로 변환 + jsonBytes, err := json.Marshal(result.Object) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return nil, err + } + + var tksPolicyTemplate TKSPolicyTemplate + err = json.Unmarshal(jsonBytes, &tksPolicyTemplate) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, name) + return nil, err + } + + return &tksPolicyTemplate, nil +} + +func UpdateTksPolicyTemplateCR(ctx context.Context, primaryClusterId string, tksPolicyTemplate *TKSPolicyTemplate) error { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + + return err + } + + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tksPolicyTemplate) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + return err + } + + tksPolicyUnstructured := &unstructured.Unstructured{ + Object: obj, + } + + _, err = dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + Update(ctx, tksPolicyUnstructured, metav1.UpdateOptions{}) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + return err + } + + return err +} + +//func ListTksPolicyTemplateCR(ctx context.Context, primaryClusterId string) ([]TKSPolicyTemplate, error) { +// if syncToKubernetes() { +// dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) +// +// if err != nil { +// return nil, err +// } +// +// results, err := dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). +// List(ctx, metav1.ListOptions{}) +// +// if err != nil { +// return nil, err +// } +// +// tkspolicytemplates := make([]TKSPolicyTemplate, len(results.Items)) +// +// for i, result := range results.Items { +// jsonBytes, err := json.Marshal(result.Object) +// +// if err != nil { +// return nil, err +// } +// +// var tksPolicyTemplate TKSPolicyTemplate +// err = json.Unmarshal(jsonBytes, &tksPolicyTemplate) +// +// if err != nil { +// return nil, err +// } +// +// tkspolicytemplates[i] = tksPolicyTemplate +// } +// +// return tkspolicytemplates, nil +// } +// +// tkspolicytemplates := make([]TKSPolicyTemplate, 0) +// return tkspolicytemplates, nil +//} + +func GetTksPolicyTemplateCRs(ctx context.Context, primaryClusterId string) (tksPolicyTemplates []TKSPolicyTemplate, err error) { + dynamicClient, err := kubernetes.GetDynamicClientAdminCluster(ctx) + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s'", + err.Error(), err, primaryClusterId) + + return nil, err + } + + resources, err := dynamicClient.Resource(TKSPolicyTemplateGVR).Namespace(primaryClusterId). + List(context.TODO(), metav1.ListOptions{}) + if err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s'", + err.Error(), err, primaryClusterId) + + return nil, err + } + + var tksPolicyTemplate TKSPolicyTemplate + for _, c := range resources.Items { + if err = runtime.DefaultUnstructuredConverter.FromUnstructured(c.UnstructuredContent(), &tksPolicyTemplate); err != nil { + log.Errorf(ctx, "error is :%s(%T), primaryClusterId='%s', policyTemplateName='%+v'", + err.Error(), err, primaryClusterId, tksPolicyTemplate.Name) + + return nil, err + } + tksPolicyTemplates = append(tksPolicyTemplates, tksPolicyTemplate) + } + + return tksPolicyTemplates, nil +} diff --git a/internal/policy-template/validation.go b/internal/policy-template/validation.go new file mode 100644 index 00000000..ccb83b00 --- /dev/null +++ b/internal/policy-template/validation.go @@ -0,0 +1,92 @@ +package policytemplate + +import ( + "encoding/json" + "fmt" + "slices" + "strings" + + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/xeipuuv/gojsonschema" +) + +var VALID_PARAM_TYPES = []string{"string", "number", "integer", "object", "boolean", "null"} + +func ValidateParamDef(paramdef *domain.ParameterDef) error { + paramType := paramdef.Type + + baseType := strings.TrimSuffix(paramType, "[]") + + // 타입과 []를 제거한 타입이 다르면 array인데 이것과 isArray 속성이 다르면 에러임 + if (baseType != paramType) != paramdef.IsArray { + return fmt.Errorf("type is '%s', but IsArray=%v", paramType, paramdef.IsArray) + } + + if slices.Contains(VALID_PARAM_TYPES, baseType) { + return nil + } + + return fmt.Errorf("%s is not valid type", paramType) +} + +func ValidateParamDefs(paramdefs []*domain.ParameterDef) error { + for _, paramdef := range paramdefs { + err := ValidateParamDef(paramdef) + if err != nil { + return err + } + + err = ValidateParamDefs(paramdef.Children) + + if err != nil { + return err + } + } + + return nil +} + +func ValidateJSONusingParamdefs(paramdefs []*domain.ParameterDef, jsonStr string) error { + jsonSchema := ParamDefsToJSONSchemaProeprties(paramdefs, true) + + if jsonSchema == nil { + // 파라미터가 없는데 "{}" 이나 ""이면 에러가 아님 + if isEmptyValue(jsonStr) { + return nil + } else { + return fmt.Errorf("schema has no field but value '%v' specified", jsonStr) + } + } + + // Load JSON Schema + schemaLoader := gojsonschema.NewGoLoader(jsonSchema) + + // Load JSON data + documentLoader := gojsonschema.NewStringLoader(jsonStr) + + // Validate JSON against JSON Schema + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + return err + } + + // Check if the result is valid + if result.Valid() { + return nil + } + + schemaBytes, _ := json.Marshal(paramdefs) + jsonSchemaBytes, _ := json.Marshal(jsonSchema) + + return fmt.Errorf("value '%s' is not valid against schemas:\njsonschema='%s',\nparamdefs='%s'", + jsonStr, string(jsonSchemaBytes), string(schemaBytes)) +} + +func isEmptyValue(value string) bool { + val := strings.TrimSpace(value) + val = strings.TrimPrefix(val, "{") + val = strings.TrimSuffix(val, "}") + val = strings.TrimSpace(val) + + return len(val) == 0 +} diff --git a/internal/repository/alert.go b/internal/repository/alert.go deleted file mode 100644 index f1ffd971..00000000 --- a/internal/repository/alert.go +++ /dev/null @@ -1,236 +0,0 @@ -package repository - -import ( - "fmt" - "math" - "time" - - "github.com/google/uuid" - "gorm.io/datatypes" - "gorm.io/gorm" - "gorm.io/gorm/clause" - - "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" - "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/log" -) - -// Interfaces -type IAlertRepository interface { - Get(alertId uuid.UUID) (domain.Alert, error) - GetByName(organizationId string, name string) (domain.Alert, error) - Fetch(organizationId string, pg *pagination.Pagination) ([]domain.Alert, error) - FetchPodRestart(organizationId string, start time.Time, end time.Time) ([]domain.Alert, error) - Create(dto domain.Alert) (alertId uuid.UUID, err error) - Update(dto domain.Alert) (err error) - Delete(dto domain.Alert) (err error) - - CreateAlertAction(dto domain.AlertAction) (alertActionId uuid.UUID, err error) -} - -type AlertRepository struct { - db *gorm.DB -} - -func NewAlertRepository(db *gorm.DB) IAlertRepository { - return &AlertRepository{ - db: db, - } -} - -// Models -type Alert struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string - Code string - Description string - Grade string - Message string - ClusterId domain.ClusterId - Cluster Cluster `gorm:"foreignKey:ClusterId"` - Node string - CheckPoint string - GrafanaUrl string - Summary string - AlertActions []AlertAction - RawData datatypes.JSON - Status domain.AlertActionStatus `gorm:"index"` -} - -func (c *Alert) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil -} - -type AlertAction struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey"` - AlertId uuid.UUID - Content string - Status domain.AlertActionStatus - TakerId *uuid.UUID `gorm:"type:uuid"` - Taker User `gorm:"foreignKey:TakerId"` -} - -func (c *AlertAction) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil -} - -// Logics -func (r *AlertRepository) Get(alertId uuid.UUID) (out domain.Alert, err error) { - var alert Alert - res := r.db.Preload("AlertActions.Taker").Preload(clause.Associations).First(&alert, "id = ?", alertId) - if res.Error != nil { - return domain.Alert{}, res.Error - } - out = reflectAlert(alert) - return -} - -func (r *AlertRepository) GetByName(organizationId string, name string) (out domain.Alert, err error) { - var alert Alert - res := r.db.Preload("AlertActions.Taker").Preload(clause.Associations).First(&alert, "organization_id = ? AND name = ?", organizationId, name) - - if res.Error != nil { - return domain.Alert{}, res.Error - } - out = reflectAlert(alert) - return -} - -func (r *AlertRepository) Fetch(organizationId string, pg *pagination.Pagination) (out []domain.Alert, err error) { - var alerts []Alert - if pg == nil { - pg = pagination.NewDefaultPagination() - } - - filterFunc := CombinedGormFilter("alerts", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Alert{}). - Preload("AlertActions", func(db *gorm.DB) *gorm.DB { - return db.Order("created_at ASC") - }).Preload("AlertActions.Taker"). - Preload("Cluster", "status = 2"). - Preload("Organization"). - Joins("join clusters on clusters.id = alerts.cluster_id AND clusters.status = 2"). - Where("alerts.organization_id = ?", organizationId)) - - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&alerts) - if res.Error != nil { - return nil, res.Error - } - - for _, alert := range alerts { - out = append(out, reflectAlert(alert)) - } - return -} - -func (r *AlertRepository) FetchPodRestart(organizationId string, start time.Time, end time.Time) (out []domain.Alert, err error) { - var alerts []Alert - res := r.db.Preload(clause.Associations).Order("created_at DESC"). - Where("organization_id = ? AND name = 'pod-restart-frequently' AND created_at BETWEEN ? AND ?", organizationId, start, end). - Find(&alerts) - if res.Error != nil { - return nil, res.Error - } - - for _, alert := range alerts { - out = append(out, reflectAlert(alert)) - } - return -} - -func (r *AlertRepository) Create(dto domain.Alert) (alertId uuid.UUID, err error) { - alert := Alert{ - OrganizationId: dto.OrganizationId, - Name: dto.Name, - Code: dto.Code, - Message: dto.Message, - Description: dto.Description, - Grade: dto.Grade, - ClusterId: dto.ClusterId, - Node: dto.Node, - GrafanaUrl: dto.GrafanaUrl, - CheckPoint: dto.CheckPoint, - Summary: dto.Summary, - RawData: dto.RawData, - Status: domain.AlertActionStatus_CREATED, - } - res := r.db.Create(&alert) - if res.Error != nil { - return uuid.Nil, res.Error - } - return alert.ID, nil -} - -func (r *AlertRepository) Update(dto domain.Alert) (err error) { - res := r.db.Model(&Alert{}). - Where("id = ?", dto.ID). - Updates(map[string]interface{}{"Description": dto.Description}) - if res.Error != nil { - return res.Error - } - return nil -} - -func (r *AlertRepository) Delete(dto domain.Alert) (err error) { - res := r.db.Delete(&Alert{}, "id = ?", dto.ID) - if res.Error != nil { - return res.Error - } - return nil -} - -func (r *AlertRepository) CreateAlertAction(dto domain.AlertAction) (alertActionId uuid.UUID, err error) { - alert := AlertAction{ - AlertId: dto.AlertId, - Content: dto.Content, - Status: dto.Status, - TakerId: dto.TakerId, - } - res := r.db.Create(&alert) - if res.Error != nil { - return uuid.Nil, res.Error - } - res = r.db.Model(&Alert{}). - Where("id = ?", dto.AlertId). - Update("status", dto.Status) - if res.Error != nil { - return uuid.Nil, res.Error - } - - return alert.ID, nil -} - -func reflectAlert(alert Alert) (out domain.Alert) { - if err := serializer.Map(alert.Model, &out); err != nil { - log.Error(err) - } - if err := serializer.Map(alert, &out); err != nil { - log.Error(err) - } - - out.AlertActions = make([]domain.AlertAction, len(alert.AlertActions)) - for i, alertAction := range alert.AlertActions { - if err := serializer.Map(alertAction.Model, &out.AlertActions[i]); err != nil { - log.Error(err) - continue - } - if err := serializer.Map(alertAction, &out.AlertActions[i]); err != nil { - log.Error(err) - continue - } - } - return -} diff --git a/internal/repository/app-group.go b/internal/repository/app-group.go index e999ae99..2885155b 100644 --- a/internal/repository/app-group.go +++ b/internal/repository/app-group.go @@ -1,31 +1,31 @@ package repository import ( + "context" "fmt" - "math" - "github.com/google/uuid" "gorm.io/datatypes" "gorm.io/gorm" + "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" ) // Interfaces type IAppGroupRepository interface { - Fetch(clusterId domain.ClusterId, pg *pagination.Pagination) (res []domain.AppGroup, err error) - Get(id domain.AppGroupId) (domain.AppGroup, error) - Create(dto domain.AppGroup) (id domain.AppGroupId, err error) - Update(dto domain.AppGroup) (err error) - Delete(id domain.AppGroupId) error - GetApplications(id domain.AppGroupId, applicationType domain.ApplicationType) (applications []domain.Application, err error) - UpsertApplication(dto domain.Application) error - InitWorkflow(appGroupId domain.AppGroupId, workflowId string, status domain.AppGroupStatus) error - InitWorkflowDescription(clusterId domain.ClusterId) error + Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) (res []model.AppGroup, err error) + Get(ctx context.Context, id domain.AppGroupId) (model.AppGroup, error) + Create(ctx context.Context, dto model.AppGroup) (id domain.AppGroupId, err error) + Update(ctx context.Context, dto model.AppGroup) (err error) + Delete(ctx context.Context, id domain.AppGroupId) error + GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (applications []model.Application, err error) + UpsertApplication(ctx context.Context, dto model.Application) error + InitWorkflow(ctx context.Context, appGroupId domain.AppGroupId, workflowId string, status domain.AppGroupStatus) error + InitWorkflowDescription(ctx context.Context, clusterId domain.ClusterId) error } type AppGroupRepository struct { @@ -38,85 +38,32 @@ func NewAppGroupRepository(db *gorm.DB) IAppGroupRepository { } } -// Models -type AppGroup struct { - gorm.Model - - ID domain.AppGroupId `gorm:"primarykey"` - AppGroupType domain.AppGroupType - ClusterId domain.ClusterId - Name string - Description string - WorkflowId string - Status domain.AppGroupStatus - StatusDesc string - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` -} - -func (c *AppGroup) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = domain.AppGroupId(helper.GenerateApplicaionGroupId()) - return nil -} - -type Application struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid"` - AppGroupId domain.AppGroupId - Endpoint string - Metadata datatypes.JSON - Type domain.ApplicationType -} - -func (c *Application) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil -} - // Logics -func (r *AppGroupRepository) Fetch(clusterId domain.ClusterId, pg *pagination.Pagination) (out []domain.AppGroup, err error) { - var appGroups []AppGroup +func (r *AppGroupRepository) Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) (out []model.AppGroup, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("app_groups", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&AppGroup{}). - Where("cluster_id = ?", clusterId)) - db.Count(&pg.TotalRows) - - r.db.Model(&AppGroup{}). - Where("cluster_id = ?", clusterId).Where("id").Where("app_groups.status").Where("app_groups.deleted") - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&appGroups) + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.AppGroup{}). + Where("cluster_id = ?", clusterId), &out) if res.Error != nil { return nil, res.Error } - for _, appGroup := range appGroups { - outAppGroup := reflectAppGroup(appGroup) - out = append(out, outAppGroup) - } return out, nil } -func (r *AppGroupRepository) Get(id domain.AppGroupId) (domain.AppGroup, error) { - var appGroup AppGroup - res := r.db.First(&appGroup, "id = ?", id) +func (r *AppGroupRepository) Get(ctx context.Context, id domain.AppGroupId) (out model.AppGroup, err error) { + res := r.db.WithContext(ctx).First(&out, "id = ?", id) if res.RowsAffected == 0 || res.Error != nil { - return domain.AppGroup{}, fmt.Errorf("Not found appGroup for %s", id) + return model.AppGroup{}, fmt.Errorf("Not found appGroup for %s", id) } - resAppGroup := reflectAppGroup(appGroup) - return resAppGroup, nil + return out, nil } -func (r *AppGroupRepository) Create(dto domain.AppGroup) (appGroupId domain.AppGroupId, err error) { - appGroup := AppGroup{ +func (r *AppGroupRepository) Create(ctx context.Context, dto model.AppGroup) (appGroupId domain.AppGroupId, err error) { + appGroup := model.AppGroup{ + ID: domain.AppGroupId(helper.GenerateApplicaionGroupId()), ClusterId: dto.ClusterId, AppGroupType: dto.AppGroupType, Name: dto.Name, @@ -125,17 +72,17 @@ func (r *AppGroupRepository) Create(dto domain.AppGroup) (appGroupId domain.AppG CreatorId: dto.CreatorId, UpdatorId: nil, } - res := r.db.Create(&appGroup) + res := r.db.WithContext(ctx).Create(&appGroup) if res.Error != nil { - log.Error(res.Error) + log.Error(ctx, res.Error) return "", res.Error } return appGroup.ID, nil } -func (r *AppGroupRepository) Update(dto domain.AppGroup) (err error) { - res := r.db.Model(&AppGroup{}). +func (r *AppGroupRepository) Update(ctx context.Context, dto model.AppGroup) (err error) { + res := r.db.WithContext(ctx).Model(&model.AppGroup{}). Where("id = ?", dto.ID). Updates(map[string]interface{}{ "ClusterId": dto.ClusterId, @@ -150,36 +97,32 @@ func (r *AppGroupRepository) Update(dto domain.AppGroup) (err error) { return nil } -func (r *AppGroupRepository) Delete(id domain.AppGroupId) error { - res := r.db.Unscoped().Delete(&AppGroup{}, "id = ?", id) +func (r *AppGroupRepository) Delete(ctx context.Context, id domain.AppGroupId) error { + res := r.db.WithContext(ctx).Unscoped().Delete(&model.AppGroup{}, "id = ?", id) if res.Error != nil { return fmt.Errorf("could not delete appGroup %s", id) } return nil } -func (r *AppGroupRepository) GetApplications(id domain.AppGroupId, applicationType domain.ApplicationType) (out []domain.Application, err error) { - var applications []Application - res := r.db.Where("app_group_id = ? AND type = ?", id, applicationType).Find(&applications) +func (r *AppGroupRepository) GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []model.Application, err error) { + res := r.db.WithContext(ctx).Where("app_group_id = ? AND type = ?", id, applicationType).Find(&out) if res.Error != nil { return nil, res.Error } - for _, application := range applications { - outApplication := reflectApplication(application) - out = append(out, outApplication) - } return out, nil } -func (r *AppGroupRepository) UpsertApplication(dto domain.Application) error { - res := r.db.Where(Application{ +func (r *AppGroupRepository) UpsertApplication(ctx context.Context, dto model.Application) error { + res := r.db.WithContext(ctx).Where(model.Application{ AppGroupId: dto.AppGroupId, - Type: dto.ApplicationType, + Type: dto.Type, }). - Assign(Application{ + Assign(model.Application{ + ID: uuid.New(), Endpoint: dto.Endpoint, Metadata: datatypes.JSON([]byte(dto.Metadata))}). - FirstOrCreate(&Application{}) + FirstOrCreate(&model.Application{}) if res.Error != nil { return res.Error } @@ -187,8 +130,8 @@ func (r *AppGroupRepository) UpsertApplication(dto domain.Application) error { return nil } -func (r *AppGroupRepository) InitWorkflow(appGroupId domain.AppGroupId, workflowId string, status domain.AppGroupStatus) error { - res := r.db.Model(&AppGroup{}). +func (r *AppGroupRepository) InitWorkflow(ctx context.Context, appGroupId domain.AppGroupId, workflowId string, status domain.AppGroupStatus) error { + res := r.db.WithContext(ctx).Model(&model.AppGroup{}). Where("ID = ?", appGroupId). Updates(map[string]interface{}{"Status": status, "WorkflowId": workflowId, "StatusDesc": ""}) @@ -199,8 +142,8 @@ func (r *AppGroupRepository) InitWorkflow(appGroupId domain.AppGroupId, workflow return nil } -func (r *AppGroupRepository) InitWorkflowDescription(clusterId domain.ClusterId) error { - res := r.db.Model(&AppGroup{}). +func (r *AppGroupRepository) InitWorkflowDescription(ctx context.Context, clusterId domain.ClusterId) error { + res := r.db.WithContext(ctx).Model(&model.AppGroup{}). Where("cluster_id = ?", clusterId). Updates(map[string]interface{}{"WorkflowId": "", "StatusDesc": ""}) @@ -210,22 +153,3 @@ func (r *AppGroupRepository) InitWorkflowDescription(clusterId domain.ClusterId) return nil } - -func reflectAppGroup(appGroup AppGroup) (out domain.AppGroup) { - if err := serializer.Map(appGroup.Model, &out); err != nil { - log.Error(err) - } - if err := serializer.Map(appGroup, &out); err != nil { - log.Error(err) - } - return -} - -func reflectApplication(application Application) (out domain.Application) { - if err := serializer.Map(application, &out); err != nil { - log.Error(err) - } - out.Metadata = application.Metadata.String() - return - -} diff --git a/internal/repository/app-serve-app.go b/internal/repository/app-serve-app.go index d27adfb6..2267fd94 100644 --- a/internal/repository/app-serve-app.go +++ b/internal/repository/app-serve-app.go @@ -1,10 +1,12 @@ package repository import ( + "context" "fmt" - "math" "time" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" @@ -12,18 +14,22 @@ import ( ) type IAppServeAppRepository interface { - CreateAppServeApp(app *domain.AppServeApp) (appId string, taskId string, err error) - GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) ([]domain.AppServeApp, error) - GetAppServeAppById(appId string) (*domain.AppServeApp, error) - GetAppServeAppLatestTask(appId string) (*domain.AppServeAppTask, error) - GetNumOfAppsOnStack(organizationId string, clusterId string) (int64, error) - IsAppServeAppExist(appId string) (int64, error) - IsAppServeAppNameExist(orgId string, appName string) (int64, error) - CreateTask(task *domain.AppServeAppTask) (taskId string, err error) - UpdateStatus(appId string, taskId string, status string, output string) error - UpdateEndpoint(appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) error - GetAppServeAppTaskById(taskId string) (*domain.AppServeAppTask, error) - GetTaskCountById(appId string) (int64, error) + CreateAppServeApp(ctx context.Context, app *model.AppServeApp) (appId string, err error) + GetAppServeApps(ctx context.Context, organizationId string, projectId string, showAll bool, pg *pagination.Pagination) ([]model.AppServeApp, error) + GetAppServeAppById(ctx context.Context, appId string) (*model.AppServeApp, error) + + GetAppServeAppTasksByAppId(ctx context.Context, appId string, pg *pagination.Pagination) ([]model.AppServeAppTask, error) + GetAppServeAppTaskById(ctx context.Context, taskId string) (*model.AppServeAppTask, error) + GetAppServeAppLatestTask(ctx context.Context, appId string) (*model.AppServeAppTask, error) + + GetNumOfAppsOnStack(ctx context.Context, organizationId string, clusterId string) (int64, error) + + IsAppServeAppExist(ctx context.Context, appId string) (int64, error) + IsAppServeAppNameExist(ctx context.Context, orgId string, appName string) (int64, error) + CreateTask(ctx context.Context, task *model.AppServeAppTask, appId string) (taskId string, err error) + UpdateStatus(ctx context.Context, appId string, taskId string, status string, output string) error + UpdateEndpoint(ctx context.Context, appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) error + GetTaskCountById(ctx context.Context, appId string) (int64, error) } type AppServeAppRepository struct { @@ -36,20 +42,23 @@ func NewAppServeAppRepository(db *gorm.DB) IAppServeAppRepository { } } -func (r *AppServeAppRepository) CreateAppServeApp(app *domain.AppServeApp) (appId string, taskId string, err error) { - - res := r.db.Create(&app) +func (r *AppServeAppRepository) CreateAppServeApp(ctx context.Context, app *model.AppServeApp) (appId string, err error) { + app.ID = uuid.New().String() + res := r.db.WithContext(ctx).Create(&app) if res.Error != nil { - return "", "", res.Error + return "", res.Error } - return app.ID, app.AppServeAppTasks[0].ID, nil + return app.ID, nil } // Update creates new appServeApp task for existing appServeApp. -func (r *AppServeAppRepository) CreateTask( - task *domain.AppServeAppTask) (string, error) { - res := r.db.Create(task) +func (r *AppServeAppRepository) CreateTask(ctx context.Context, task *model.AppServeAppTask, appId string) (string, error) { + task.ID = uuid.New().String() + if len(appId) > 0 { + task.AppServeAppId = appId + } + res := r.db.WithContext(ctx).Create(task) if res.Error != nil { return "", res.Error } @@ -57,22 +66,17 @@ func (r *AppServeAppRepository) CreateTask( return task.ID, nil } -func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) (apps []domain.AppServeApp, err error) { - var clusters []Cluster +func (r *AppServeAppRepository) GetAppServeApps(ctx context.Context, organizationId string, projectId string, showAll bool, pg *pagination.Pagination) (apps []model.AppServeApp, err error) { + var clusters []model.Cluster if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("app_serve_apps", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&domain.AppServeApp{}). - Where("app_serve_apps.organization_id = ? AND status <> 'DELETE_SUCCESS'", organizationId)) - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&apps) + // TODO: should return different records based on showAll param + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.AppServeApp{}). + Where("app_serve_apps.project_id = ? AND status <> 'DELETE_SUCCESS'", projectId), &apps) if res.Error != nil { - return nil, fmt.Errorf("error while finding appServeApps with organizationId: %s", organizationId) + return nil, fmt.Errorf("error while finding appServeApps with projectId: %s", projectId) } // If no record is found, just return empty array. @@ -82,7 +86,7 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b // Add cluster names to apps list queryStr := fmt.Sprintf("organization_id = '%s' AND status <> '%d'", organizationId, domain.ClusterStatus_DELETED) - res = r.db.Find(&clusters, queryStr) + res = r.db.WithContext(ctx).Find(&clusters, queryStr) if res.Error != nil { return nil, fmt.Errorf("error while fetching clusters with organizationId: %s", organizationId) } @@ -99,51 +103,82 @@ func (r *AppServeAppRepository) GetAppServeApps(organizationId string, showAll b return } -func (r *AppServeAppRepository) GetAppServeAppById(appId string) (*domain.AppServeApp, error) { - var app domain.AppServeApp - var cluster Cluster +func (r *AppServeAppRepository) GetAppServeAppById(ctx context.Context, appId string) (*model.AppServeApp, error) { + var app model.AppServeApp + var cluster model.Cluster - res := r.db.Where("id = ?", appId).First(&app) + res := r.db.WithContext(ctx).Where("id = ?", appId).First(&app) if res.Error != nil { - log.Debug(res.Error) + log.Debug(ctx, res.Error) return nil, res.Error } if res.RowsAffected == 0 { - return nil, nil - } - - if err := r.db.Model(&app).Order("created_at desc").Association("AppServeAppTasks").Find(&app.AppServeAppTasks); err != nil { - log.Debug(err) - return nil, err + return nil, fmt.Errorf("No app with ID %s", appId) } // Add cluster name to app object - r.db.Select("name").Where("id = ?", app.TargetClusterId).First(&cluster) + r.db.WithContext(ctx).Select("name").Where("id = ?", app.TargetClusterId).First(&cluster) app.TargetClusterName = cluster.Name return &app, nil } -func (r *AppServeAppRepository) GetAppServeAppLatestTask(appId string) (*domain.AppServeAppTask, error) { - var task domain.AppServeAppTask +func (r *AppServeAppRepository) GetAppServeAppTasksByAppId(ctx context.Context, appId string, pg *pagination.Pagination) (tasks []model.AppServeAppTask, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.AppServeAppTask{}). + Where("app_serve_app_tasks.app_serve_app_id = ?", appId), &tasks) + if res.Error != nil { + return nil, fmt.Errorf("Error while finding tasks with appId: %s", appId) + } + + // If no record is found, just return empty array. + if res.RowsAffected == 0 { + return tasks, nil + } + + return +} + +// Return single task info along with its parent app info +func (r *AppServeAppRepository) GetAppServeAppTaskById(ctx context.Context, taskId string) (*model.AppServeAppTask, error) { + var task model.AppServeAppTask - res := r.db.Order("created_at desc").First(&task) + // Retrieve task info + res := r.db.WithContext(ctx).Where("id = ?", taskId).First(&task) if res.Error != nil { - log.Debug(res.Error) + log.Debug(ctx, res.Error) return nil, res.Error } if res.RowsAffected == 0 { - return nil, nil + return nil, fmt.Errorf("No task with ID %s", taskId) } return &task, nil } -func (r *AppServeAppRepository) GetNumOfAppsOnStack(organizationId string, clusterId string) (int64, error) { - var apps []domain.AppServeApp +func (r *AppServeAppRepository) GetAppServeAppLatestTask(ctx context.Context, appId string) (*model.AppServeAppTask, error) { + var task model.AppServeAppTask + + res := r.db.WithContext(ctx).Order("created_at desc").Where("app_serve_app_id = ?", appId).First(&task) + if res.Error != nil { + log.Debug(ctx, res.Error) + return nil, res.Error + } + if res.RowsAffected == 0 { + return nil, fmt.Errorf("No task with App ID %s", appId) + } + + return &task, nil +} + +func (r *AppServeAppRepository) GetNumOfAppsOnStack(ctx context.Context, organizationId string, clusterId string) (int64, error) { + var apps []model.AppServeApp queryStr := fmt.Sprintf("organization_id = '%s' AND target_cluster_id = '%s' AND status <> 'DELETE_SUCCESS'", organizationId, clusterId) - res := r.db.Find(&apps, queryStr) + res := r.db.WithContext(ctx).Find(&apps, queryStr) if res.Error != nil { return -1, fmt.Errorf("Error while finding appServeApps with organizationId: %s", organizationId) } @@ -151,67 +186,67 @@ func (r *AppServeAppRepository) GetNumOfAppsOnStack(organizationId string, clust return res.RowsAffected, nil } -func (r *AppServeAppRepository) IsAppServeAppExist(appId string) (int64, error) { +func (r *AppServeAppRepository) IsAppServeAppExist(ctx context.Context, appId string) (int64, error) { var result int64 - res := r.db.Table("app_serve_apps").Where("id = ? AND status <> 'DELETE_SUCCESS'", appId).Count(&result) + res := r.db.WithContext(ctx).Table("app_serve_apps").Where("id = ? AND status <> 'DELETE_SUCCESS'", appId).Count(&result) if res.Error != nil { - log.Debug(res.Error) + log.Debug(ctx, res.Error) return 0, res.Error } return result, nil } -func (r *AppServeAppRepository) IsAppServeAppNameExist(orgId string, appName string) (int64, error) { +func (r *AppServeAppRepository) IsAppServeAppNameExist(ctx context.Context, orgId string, appName string) (int64, error) { var result int64 queryString := fmt.Sprintf("organization_id = '%v' "+ "AND name = '%v' "+ "AND status <> 'DELETE_SUCCESS'", orgId, appName) - log.Info("query = ", queryString) - res := r.db.Table("app_serve_apps").Where(queryString).Count(&result) + log.Info(ctx, "query = ", queryString) + res := r.db.WithContext(ctx).Table("app_serve_apps").Where(queryString).Count(&result) if res.Error != nil { - log.Debug(res.Error) + log.Debug(ctx, res.Error) return 0, res.Error } return result, nil } -func (r *AppServeAppRepository) UpdateStatus(appId string, taskId string, status string, output string) error { +func (r *AppServeAppRepository) UpdateStatus(ctx context.Context, appId string, taskId string, status string, output string) error { now := time.Now() - app := domain.AppServeApp{ + app := model.AppServeApp{ ID: appId, Status: status, UpdatedAt: &now, } - res := r.db.Model(&app).Select("Status", "UpdatedAt").Updates(app) + res := r.db.WithContext(ctx).Model(&app).Select("Status", "UpdatedAt").Updates(app) if res.Error != nil || res.RowsAffected == 0 { return fmt.Errorf("UpdateStatus: nothing updated in AppServeApp with ID %s", appId) } - task := domain.AppServeAppTask{ + task := model.AppServeAppTask{ ID: taskId, Status: status, Output: output, UpdatedAt: &now, } - res = r.db.Model(&task).Select("Status", "Output", "UpdatedAt").Updates(task) + res = r.db.WithContext(ctx).Model(&task).Select("Status", "Output", "UpdatedAt").Updates(task) if res.Error != nil || res.RowsAffected == 0 { return fmt.Errorf("UpdateStatus: nothing updated in AppServeAppTask with ID %s", taskId) } //// Update task status - //res := r.db.Model(&domain.AppServeAppTask{}). + //res := r.db.Model(&model.AppServeAppTask{}). // Where("ID = ?", taskId). - // Updates(domain.AppServeAppTask{Status: status, Output: output}) + // Updates(model.AppServeAppTask{Status: status, Output: output}) // //if res.Error != nil || res.RowsAffected == 0 { // return fmt.Errorf("UpdateStatus: nothing updated in AppServeAppTask with ID %s", taskId) //} // //// Update status of the app. - //res = r.db.Model(&domain.AppServeApp{}). + //res = r.db.Model(&model.AppServeApp{}). // Where("ID = ?", appId). // Update("Status", status) //if res.Error != nil || res.RowsAffected == 0 { @@ -221,16 +256,16 @@ func (r *AppServeAppRepository) UpdateStatus(appId string, taskId string, status return nil } -func (r *AppServeAppRepository) UpdateEndpoint(appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) error { +func (r *AppServeAppRepository) UpdateEndpoint(ctx context.Context, appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) error { now := time.Now() - app := domain.AppServeApp{ + app := model.AppServeApp{ ID: appId, EndpointUrl: endpoint, PreviewEndpointUrl: previewEndpoint, UpdatedAt: &now, } - task := domain.AppServeAppTask{ + task := model.AppServeAppTask{ ID: taskId, HelmRevision: helmRevision, UpdatedAt: &now, @@ -239,13 +274,13 @@ func (r *AppServeAppRepository) UpdateEndpoint(appId string, taskId string, endp var res *gorm.DB if endpoint != "" && previewEndpoint != "" { // Both endpoints are valid - res = r.db.Model(&app).Select("EndpointUrl", "PreviewEndpointUrl", "UpdatedAt").Updates(app) + res = r.db.WithContext(ctx).Model(&app).Select("EndpointUrl", "PreviewEndpointUrl", "UpdatedAt").Updates(app) } else if endpoint != "" { // endpoint-only case - res = r.db.Model(&app).Select("EndpointUrl", "UpdatedAt").Updates(app) + res = r.db.WithContext(ctx).Model(&app).Select("EndpointUrl", "UpdatedAt").Updates(app) } else if previewEndpoint != "" { // previewEndpoint-only case - res = r.db.Model(&app).Select("PreviewEndpointUrl", "UpdatedAt").Updates(app) + res = r.db.WithContext(ctx).Model(&app).Select("PreviewEndpointUrl", "UpdatedAt").Updates(app) } else { return fmt.Errorf("updateEndpoint: No endpoint provided. " + "At least one of [endpoint, preview_endpoint] should be provided") @@ -257,7 +292,7 @@ func (r *AppServeAppRepository) UpdateEndpoint(appId string, taskId string, endp // Update helm revision // Ignore if the value is less than 0 if helmRevision > 0 { - res = r.db.Model(&task).Select("HelmRevision", "UpdatedAt").Updates(task) + res = r.db.WithContext(ctx).Model(&task).Select("HelmRevision", "UpdatedAt").Updates(task) if res.Error != nil || res.RowsAffected == 0 { return fmt.Errorf("UpdateEndpoint: "+ "helm revision was not updated for AppServeAppTask with task ID %s", taskId) @@ -267,19 +302,9 @@ func (r *AppServeAppRepository) UpdateEndpoint(appId string, taskId string, endp return nil } -func (r *AppServeAppRepository) GetAppServeAppTaskById(taskId string) (*domain.AppServeAppTask, error) { - var task domain.AppServeAppTask - - if err := r.db.Where("id = ?", taskId).First(&task).Error; err != nil { - return nil, fmt.Errorf("could not find AppServeAppTask with ID: %s", taskId) - } - - return &task, nil -} - -func (r *AppServeAppRepository) GetTaskCountById(appId string) (int64, error) { +func (r *AppServeAppRepository) GetTaskCountById(ctx context.Context, appId string) (int64, error) { var count int64 - if err := r.db.Model(&domain.AppServeAppTask{}).Where("AppServeAppId = ?", appId).Count(&count); err != nil { + if err := r.db.WithContext(ctx).Model(&model.AppServeAppTask{}).Where("AppServeAppId = ?", appId).Count(&count); err != nil { return 0, fmt.Errorf("could not select count AppServeAppTask with ID: %s", appId) } return count, nil diff --git a/internal/repository/audit.go b/internal/repository/audit.go new file mode 100644 index 00000000..f33f4835 --- /dev/null +++ b/internal/repository/audit.go @@ -0,0 +1,66 @@ +package repository + +import ( + "context" + "fmt" + + "github.com/google/uuid" + "gorm.io/gorm" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" +) + +// Interfaces +type IAuditRepository interface { + Get(ctx context.Context, auditId uuid.UUID) (model.Audit, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.Audit, error) + Create(ctx context.Context, dto model.Audit) (auditId uuid.UUID, err error) + Delete(ctx context.Context, auditId uuid.UUID) (err error) +} + +type AuditRepository struct { + db *gorm.DB +} + +func NewAuditRepository(db *gorm.DB) IAuditRepository { + return &AuditRepository{ + db: db, + } +} + +// Logics +func (r *AuditRepository) Get(ctx context.Context, auditId uuid.UUID) (out model.Audit, err error) { + res := r.db.WithContext(ctx).First(&out, "id = ?", auditId) + if res.Error != nil { + return + } + return +} + +func (r *AuditRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.Audit, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + db := r.db.WithContext(ctx).Model(&model.Audit{}) + + _, res := pg.Fetch(db, &out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *AuditRepository) Create(ctx context.Context, dto model.Audit) (auditId uuid.UUID, err error) { + dto.ID = uuid.New() + res := r.db.WithContext(ctx).Create(&dto) + if res.Error != nil { + return uuid.Nil, res.Error + } + return dto.ID, nil +} + +func (r *AuditRepository) Delete(ctx context.Context, auditId uuid.UUID) (err error) { + return fmt.Errorf("to be implemented") +} diff --git a/internal/repository/auth.go b/internal/repository/auth.go index 3c2c980a..c59d4c7f 100644 --- a/internal/repository/auth.go +++ b/internal/repository/auth.go @@ -1,56 +1,72 @@ package repository import ( + "context" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "gorm.io/gorm" + "gorm.io/gorm/clause" + "time" ) type IAuthRepository interface { - CreateEmailCode(userId uuid.UUID, code string) error - GetEmailCode(userId uuid.UUID) (CacheEmailCode, error) - UpdateEmailCode(userId uuid.UUID, code string) error - DeleteEmailCode(userId uuid.UUID) error + CreateEmailCode(ctx context.Context, userId uuid.UUID, code string) error + GetEmailCode(ctx context.Context, userId uuid.UUID) (model.CacheEmailCode, error) + UpdateEmailCode(ctx context.Context, userId uuid.UUID, code string) error + DeleteEmailCode(ctx context.Context, userId uuid.UUID) error + GetExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) (*model.ExpiredTokenTime, error) + UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error } type AuthRepository struct { db *gorm.DB } -// Models - -type CacheEmailCode struct { - gorm.Model - - UserId uuid.UUID `gorm:"not null"` - Code string `gorm:"type:varchar(6);not null"` -} - func NewAuthRepository(db *gorm.DB) IAuthRepository { return &AuthRepository{ db: db, } } -func (r *AuthRepository) CreateEmailCode(userId uuid.UUID, code string) error { - cacheEmailCode := CacheEmailCode{ +func (r *AuthRepository) CreateEmailCode(ctx context.Context, userId uuid.UUID, code string) error { + cacheEmailCode := model.CacheEmailCode{ UserId: userId, Code: code, } - return r.db.Create(&cacheEmailCode).Error + return r.db.WithContext(ctx).Create(&cacheEmailCode).Error } -func (r *AuthRepository) GetEmailCode(userId uuid.UUID) (CacheEmailCode, error) { - var cacheEmailCode CacheEmailCode - if err := r.db.Where("user_id = ?", userId).First(&cacheEmailCode).Error; err != nil { - return CacheEmailCode{}, err +func (r *AuthRepository) GetEmailCode(ctx context.Context, userId uuid.UUID) (model.CacheEmailCode, error) { + var cacheEmailCode model.CacheEmailCode + if err := r.db.WithContext(ctx).Where("user_id = ?", userId).First(&cacheEmailCode).Error; err != nil { + return model.CacheEmailCode{}, err } return cacheEmailCode, nil } -func (r *AuthRepository) UpdateEmailCode(userId uuid.UUID, code string) error { - return r.db.Model(&CacheEmailCode{}).Where("user_id = ?", userId).Update("code", code).Error +func (r *AuthRepository) UpdateEmailCode(ctx context.Context, userId uuid.UUID, code string) error { + return r.db.WithContext(ctx).Model(&model.CacheEmailCode{}).Where("user_id = ?", userId).Update("code", code).Error } -func (r *AuthRepository) DeleteEmailCode(userId uuid.UUID) error { - return r.db.Unscoped().Where("user_id = ?", userId).Delete(&CacheEmailCode{}).Error +func (r *AuthRepository) DeleteEmailCode(ctx context.Context, userId uuid.UUID) error { + return r.db.WithContext(ctx).Unscoped().Where("user_id = ?", userId).Delete(&model.CacheEmailCode{}).Error +} + +func (r *AuthRepository) GetExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) (*model.ExpiredTokenTime, error) { + var expiredTokenTime model.ExpiredTokenTime + if err := r.db.WithContext(ctx).Where("organization_id = ? AND subject_id = ?", organizationId, userId).First(&expiredTokenTime).Error; err != nil { + return nil, err + } + return &expiredTokenTime, nil +} +func (r *AuthRepository) UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error { + // set expired time to now + return r.db.WithContext(ctx).Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "subject_id"}, {Name: "organization_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"expired_time"}), + }).Create(&model.ExpiredTokenTime{ + SubjectId: userId, + ExpiredTime: time.Now(), + OrganizationId: organizationId, + }).Error } diff --git a/internal/repository/cloud-account.go b/internal/repository/cloud-account.go index 786e9f53..b3dd6efc 100644 --- a/internal/repository/cloud-account.go +++ b/internal/repository/cloud-account.go @@ -1,29 +1,28 @@ package repository import ( + "context" "fmt" - "math" "github.com/google/uuid" "gorm.io/gorm" "gorm.io/gorm/clause" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/log" ) // Interfaces type ICloudAccountRepository interface { - Get(cloudAccountId uuid.UUID) (domain.CloudAccount, error) - GetByName(organizationId string, name string) (domain.CloudAccount, error) - GetByAwsAccountId(awsAccountId string) (domain.CloudAccount, error) - Fetch(organizationId string, pg *pagination.Pagination) ([]domain.CloudAccount, error) - Create(dto domain.CloudAccount) (cloudAccountId uuid.UUID, err error) - Update(dto domain.CloudAccount) (err error) - Delete(cloudAccountId uuid.UUID) (err error) - InitWorkflow(cloudAccountId uuid.UUID, workflowId string, status domain.CloudAccountStatus) (err error) + Get(ctx context.Context, cloudAccountId uuid.UUID) (model.CloudAccount, error) + GetByName(ctx context.Context, organizationId string, name string) (model.CloudAccount, error) + GetByAwsAccountId(ctx context.Context, awsAccountId string) (model.CloudAccount, error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.CloudAccount, error) + Create(ctx context.Context, dto model.CloudAccount) (cloudAccountId uuid.UUID, err error) + Update(ctx context.Context, dto model.CloudAccount) (err error) + Delete(ctx context.Context, cloudAccountId uuid.UUID) (err error) + InitWorkflow(ctx context.Context, cloudAccountId uuid.UUID, workflowId string, status domain.CloudAccountStatus) (err error) } type CloudAccountRepository struct { @@ -36,92 +35,47 @@ func NewCloudAccountRepository(db *gorm.DB) ICloudAccountRepository { } } -// Models -type CloudAccount struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string `gorm:"index"` - Description string `gorm:"index"` - Resource string - CloudService string - WorkflowId string - Status domain.CloudAccountStatus - StatusDesc string - AwsAccountId string - CreatedIAM bool - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` -} - -func (c *CloudAccount) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil -} - // Logics -func (r *CloudAccountRepository) Get(cloudAccountId uuid.UUID) (out domain.CloudAccount, err error) { - var cloudAccount CloudAccount - res := r.db.Preload(clause.Associations).First(&cloudAccount, "id = ?", cloudAccountId) +func (r *CloudAccountRepository) Get(ctx context.Context, cloudAccountId uuid.UUID) (out model.CloudAccount, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "id = ?", cloudAccountId) if res.Error != nil { - return domain.CloudAccount{}, res.Error + return model.CloudAccount{}, res.Error } - out = reflectCloudAccount(cloudAccount) return } -func (r *CloudAccountRepository) GetByName(organizationId string, name string) (out domain.CloudAccount, err error) { - var cloudAccount CloudAccount - res := r.db.Preload(clause.Associations).First(&cloudAccount, "organization_id = ? AND name = ?", organizationId, name) - +func (r *CloudAccountRepository) GetByName(ctx context.Context, organizationId string, name string) (out model.CloudAccount, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "organization_id = ? AND name = ?", organizationId, name) if res.Error != nil { - return domain.CloudAccount{}, res.Error + return model.CloudAccount{}, res.Error } - out = reflectCloudAccount(cloudAccount) return } -func (r *CloudAccountRepository) GetByAwsAccountId(awsAccountId string) (out domain.CloudAccount, err error) { - var cloudAccount CloudAccount - res := r.db.Preload(clause.Associations).First(&cloudAccount, "aws_account_id = ? AND status != ?", awsAccountId, domain.CloudAccountStatus_DELETED) - +func (r *CloudAccountRepository) GetByAwsAccountId(ctx context.Context, awsAccountId string) (out model.CloudAccount, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "aws_account_id = ? AND status != ?", awsAccountId, domain.CloudAccountStatus_DELETED) if res.Error != nil { - return domain.CloudAccount{}, res.Error + return model.CloudAccount{}, res.Error } - out = reflectCloudAccount(cloudAccount) return } -func (r *CloudAccountRepository) Fetch(organizationId string, pg *pagination.Pagination) (out []domain.CloudAccount, err error) { - var cloudAccounts []CloudAccount +func (r *CloudAccountRepository) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.CloudAccount, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("cloud_accounts", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&CloudAccount{}). + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.CloudAccount{}). Preload(clause.Associations). - Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED)) - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&cloudAccounts) + Where("organization_id = ? AND status != ?", organizationId, domain.CloudAccountStatus_DELETED), &out) if res.Error != nil { return nil, res.Error } - - for _, cloudAccount := range cloudAccounts { - out = append(out, reflectCloudAccount(cloudAccount)) - } return } -func (r *CloudAccountRepository) Create(dto domain.CloudAccount) (cloudAccountId uuid.UUID, err error) { - cloudAccount := CloudAccount{ +func (r *CloudAccountRepository) Create(ctx context.Context, dto model.CloudAccount) (cloudAccountId uuid.UUID, err error) { + cloudAccount := model.CloudAccount{ + ID: uuid.New(), OrganizationId: dto.OrganizationId, Name: dto.Name, Description: dto.Description, @@ -130,16 +84,16 @@ func (r *CloudAccountRepository) Create(dto domain.CloudAccount) (cloudAccountId AwsAccountId: dto.AwsAccountId, CreatedIAM: false, Status: domain.CloudAccountStatus_PENDING, - CreatorId: &dto.CreatorId} - res := r.db.Create(&cloudAccount) + CreatorId: dto.CreatorId} + res := r.db.WithContext(ctx).Create(&cloudAccount) if res.Error != nil { return uuid.Nil, res.Error } return cloudAccount.ID, nil } -func (r *CloudAccountRepository) Update(dto domain.CloudAccount) (err error) { - res := r.db.Model(&CloudAccount{}). +func (r *CloudAccountRepository) Update(ctx context.Context, dto model.CloudAccount) (err error) { + res := r.db.WithContext(ctx).Model(&model.CloudAccount{}). Where("id = ?", dto.ID). Updates(map[string]interface{}{"Description": dto.Description, "Resource": dto.Resource, "UpdatorId": dto.UpdatorId}) if res.Error != nil { @@ -148,16 +102,16 @@ func (r *CloudAccountRepository) Update(dto domain.CloudAccount) (err error) { return nil } -func (r *CloudAccountRepository) Delete(cloudAccountId uuid.UUID) (err error) { - res := r.db.Delete(&CloudAccount{}, "id = ?", cloudAccountId) +func (r *CloudAccountRepository) Delete(ctx context.Context, cloudAccountId uuid.UUID) (err error) { + res := r.db.WithContext(ctx).Delete(&model.CloudAccount{}, "id = ?", cloudAccountId) if res.Error != nil { return res.Error } return nil } -func (r *CloudAccountRepository) InitWorkflow(cloudAccountId uuid.UUID, workflowId string, status domain.CloudAccountStatus) error { - res := r.db.Model(&CloudAccount{}). +func (r *CloudAccountRepository) InitWorkflow(ctx context.Context, cloudAccountId uuid.UUID, workflowId string, status domain.CloudAccountStatus) error { + res := r.db.WithContext(ctx).Model(&model.CloudAccount{}). Where("ID = ?", cloudAccountId). Updates(map[string]interface{}{"Status": status, "WorkflowId": workflowId}) @@ -167,13 +121,3 @@ func (r *CloudAccountRepository) InitWorkflow(cloudAccountId uuid.UUID, workflow return nil } - -func reflectCloudAccount(cloudAccount CloudAccount) (out domain.CloudAccount) { - if err := serializer.Map(cloudAccount.Model, &out); err != nil { - log.Error(err) - } - if err := serializer.Map(cloudAccount, &out); err != nil { - log.Error(err) - } - return -} diff --git a/internal/repository/cluster.go b/internal/repository/cluster.go index df6a7213..88c786ee 100644 --- a/internal/repository/cluster.go +++ b/internal/repository/cluster.go @@ -1,16 +1,16 @@ package repository import ( + "context" "fmt" - "math" "github.com/google/uuid" "gorm.io/gorm" "gorm.io/gorm/clause" "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" ) @@ -18,20 +18,20 @@ import ( // Interfaces type IClusterRepository interface { WithTrx(*gorm.DB) IClusterRepository - Fetch(pg *pagination.Pagination) (res []domain.Cluster, err error) - FetchByCloudAccountId(cloudAccountId uuid.UUID, pg *pagination.Pagination) (res []domain.Cluster, err error) - FetchByOrganizationId(organizationId string, userId uuid.UUID, pg *pagination.Pagination) (res []domain.Cluster, err error) - Get(id domain.ClusterId) (domain.Cluster, error) - GetByName(organizationId string, name string) (domain.Cluster, error) - Create(dto domain.Cluster) (clusterId domain.ClusterId, err error) - Update(dto domain.Cluster) (err error) - Delete(id domain.ClusterId) error - - InitWorkflow(clusterId domain.ClusterId, workflowId string, status domain.ClusterStatus) error - InitWorkflowDescription(clusterId domain.ClusterId) error - - SetFavorite(clusterId domain.ClusterId, userId uuid.UUID) error - DeleteFavorite(clusterId domain.ClusterId, userId uuid.UUID) error + Fetch(ctx context.Context, pg *pagination.Pagination) (res []model.Cluster, err error) + FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (res []model.Cluster, err error) + FetchByOrganizationId(ctx context.Context, organizationId string, userId uuid.UUID, pg *pagination.Pagination) (res []model.Cluster, err error) + Get(ctx context.Context, id domain.ClusterId) (model.Cluster, error) + GetByName(ctx context.Context, organizationId string, name string) (model.Cluster, error) + Create(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) + Update(ctx context.Context, dto model.Cluster) (err error) + Delete(ctx context.Context, id domain.ClusterId) error + + InitWorkflow(ctx context.Context, clusterId domain.ClusterId, workflowId string, status domain.ClusterStatus) error + InitWorkflowDescription(ctx context.Context, clusterId domain.ClusterId) error + + SetFavorite(ctx context.Context, clusterId domain.ClusterId, userId uuid.UUID) error + DeleteFavorite(ctx context.Context, clusterId domain.ClusterId, userId uuid.UUID) error } type ClusterRepository struct { @@ -46,174 +46,80 @@ func NewClusterRepository(db *gorm.DB) IClusterRepository { } } -// Models -type Cluster struct { - gorm.Model - - ID domain.ClusterId `gorm:"primarykey"` - Name string `gorm:"index"` - CloudService string `gorm:"default:AWS"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Description string `gorm:"index"` - WorkflowId string - Status domain.ClusterStatus - StatusDesc string - CloudAccountId *uuid.UUID - CloudAccount CloudAccount `gorm:"foreignKey:CloudAccountId"` - StackTemplateId uuid.UUID - StackTemplate StackTemplate `gorm:"foreignKey:StackTemplateId"` - Favorites *[]ClusterFavorite - ClusterType domain.ClusterType `gorm:"default:0"` - ByoClusterEndpointHost string - ByoClusterEndpointPort int - IsStack bool `gorm:"default:false"` - TksCpNode int - TksCpNodeMax int - TksCpNodeType string - TksInfraNode int - TksInfraNodeMax int - TksInfraNodeType string - TksUserNode int - TksUserNodeMax int - TksUserNodeType string - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` -} - -type ClusterFavorite struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid"` - ClusterId domain.ClusterId - Cluster Cluster `gorm:"foreignKey:ClusterId"` - UserId uuid.UUID `gorm:"type:uuid"` - User User `gorm:"foreignKey:UserId"` -} - -func (c *ClusterFavorite) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil -} - // Logics func (r *ClusterRepository) WithTrx(trxHandle *gorm.DB) IClusterRepository { if trxHandle == nil { - log.Info("Transaction Database not found") + log.Info(context.TODO(), "Transaction Database not found") return r } r.tx = trxHandle return r } -func (r *ClusterRepository) Fetch(pg *pagination.Pagination) (out []domain.Cluster, err error) { - var clusters []Cluster +func (r *ClusterRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.Cluster, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Cluster{})) - - db.Count(&pg.TotalRows) - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&clusters) + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.Cluster{}).Preload(clause.Associations), &out) if res.Error != nil { return nil, res.Error } - for _, cluster := range clusters { - outCluster := reflectCluster(cluster) - out = append(out, outCluster) - } + return } -func (r *ClusterRepository) FetchByOrganizationId(organizationId string, userId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) { - var clusters []Cluster +func (r *ClusterRepository) FetchByOrganizationId(ctx context.Context, organizationId string, userId uuid.UUID, pg *pagination.Pagination) (out []model.Cluster, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - pg.SortColumn = "created_at" - pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Cluster{}). + + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.Cluster{}). Preload(clause.Associations). Joins("left outer join cluster_favorites on clusters.id = cluster_favorites.cluster_id AND cluster_favorites.user_id = ?", userId). - Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED)) - - db.Count(&pg.TotalRows) - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order("cluster_favorites.cluster_id").Order(orderQuery).Find(&clusters) + Where("organization_id = ? AND status != ?", organizationId, domain.ClusterStatus_DELETED). + Order("cluster_favorites.cluster_id"), &out) if res.Error != nil { return nil, res.Error } - for _, cluster := range clusters { - outCluster := reflectCluster(cluster) - out = append(out, outCluster) - } - - //log.Info(helper.ModelToJson(clusters)) return } -func (r *ClusterRepository) FetchByCloudAccountId(cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) { - var clusters []Cluster +func (r *ClusterRepository) FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []model.Cluster, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - pg.SortColumn = "created_at" - pg.SortOrder = "DESC" - filterFunc := CombinedGormFilter("clusters", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Cluster{}).Preload("CloudAccount"). - Where("cloud_account_id = ?", cloudAccountId)) - - db.Count(&pg.TotalRows) - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&clusters) + _, res := pg.Fetch(r.db.WithContext(ctx).Model(&model.Cluster{}).Preload("CloudAccount"). + Where("cloud_account_id = ?", cloudAccountId), &out) if res.Error != nil { return nil, res.Error } - for _, cluster := range clusters { - outCluster := reflectCluster(cluster) - out = append(out, outCluster) - } return } -func (r *ClusterRepository) Get(id domain.ClusterId) (out domain.Cluster, err error) { - var cluster Cluster - res := r.db.Preload(clause.Associations).First(&cluster, "id = ?", id) +func (r *ClusterRepository) Get(ctx context.Context, id domain.ClusterId) (out model.Cluster, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "id = ?", id) if res.Error != nil { - return domain.Cluster{}, res.Error + return model.Cluster{}, res.Error } - out = reflectCluster(cluster) return } -func (r *ClusterRepository) GetByName(organizationId string, name string) (out domain.Cluster, err error) { - var cluster Cluster - res := r.db.Preload(clause.Associations).First(&cluster, "organization_id = ? AND name = ?", organizationId, name) +func (r *ClusterRepository) GetByName(ctx context.Context, organizationId string, name string) (out model.Cluster, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "organization_id = ? AND name = ?", organizationId, name) if res.Error != nil { - return domain.Cluster{}, res.Error + return model.Cluster{}, res.Error } - out = reflectCluster(cluster) return } -func (r *ClusterRepository) Create(dto domain.Cluster) (clusterId domain.ClusterId, err error) { +func (r *ClusterRepository) Create(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) { var cloudAccountId *uuid.UUID - cloudAccountId = &dto.CloudAccountId - if dto.CloudService == domain.CloudService_BYOH || dto.CloudAccountId == uuid.Nil { + cloudAccountId = dto.CloudAccountId + if dto.CloudService == domain.CloudService_BYOH || *dto.CloudAccountId == uuid.Nil { cloudAccountId = nil } - cluster := Cluster{ + cluster := model.Cluster{ ID: domain.ClusterId(helper.GenerateClusterId()), OrganizationId: dto.OrganizationId, Name: dto.Name, @@ -228,39 +134,39 @@ func (r *ClusterRepository) Create(dto domain.Cluster) (clusterId domain.Cluster ByoClusterEndpointHost: dto.ByoClusterEndpointHost, ByoClusterEndpointPort: dto.ByoClusterEndpointPort, IsStack: dto.IsStack, - TksCpNode: dto.Conf.TksCpNode, - TksCpNodeMax: dto.Conf.TksCpNodeMax, - TksCpNodeType: dto.Conf.TksCpNodeType, - TksInfraNode: dto.Conf.TksInfraNode, - TksInfraNodeMax: dto.Conf.TksInfraNodeMax, - TksInfraNodeType: dto.Conf.TksInfraNodeType, - TksUserNode: dto.Conf.TksUserNode, - TksUserNodeMax: dto.Conf.TksUserNodeMax, - TksUserNodeType: dto.Conf.TksUserNodeType, + TksCpNode: dto.TksCpNode, + TksCpNodeMax: dto.TksCpNodeMax, + TksCpNodeType: dto.TksCpNodeType, + TksInfraNode: dto.TksInfraNode, + TksInfraNodeMax: dto.TksInfraNodeMax, + TksInfraNodeType: dto.TksInfraNodeType, + TksUserNode: dto.TksUserNode, + TksUserNodeMax: dto.TksUserNodeMax, + TksUserNodeType: dto.TksUserNodeType, } if dto.ID != "" { cluster.ID = dto.ID } - res := r.db.Create(&cluster) + res := r.db.WithContext(ctx).Create(&cluster) if res.Error != nil { - log.Error(res.Error) + log.Error(ctx, res.Error) return "", res.Error } return cluster.ID, nil } -func (r *ClusterRepository) Delete(clusterId domain.ClusterId) error { - res := r.db.Unscoped().Delete(&Cluster{}, "id = ?", clusterId) +func (r *ClusterRepository) Delete(ctx context.Context, clusterId domain.ClusterId) error { + res := r.db.WithContext(ctx).Unscoped().Delete(&model.Cluster{}, "id = ?", clusterId) if res.Error != nil { return fmt.Errorf("could not delete cluster for clusterId %s", clusterId) } return nil } -func (r *ClusterRepository) Update(dto domain.Cluster) error { - res := r.db.Model(&Cluster{}). +func (r *ClusterRepository) Update(ctx context.Context, dto model.Cluster) error { + res := r.db.WithContext(ctx).Model(&model.Cluster{}). Where("id = ?", dto.ID). Updates(map[string]interface{}{"Description": dto.Description, "UpdatorId": dto.UpdatorId}) if res.Error != nil { @@ -269,8 +175,8 @@ func (r *ClusterRepository) Update(dto domain.Cluster) error { return nil } -func (r *ClusterRepository) InitWorkflow(clusterId domain.ClusterId, workflowId string, status domain.ClusterStatus) error { - res := r.db.Model(&Cluster{}). +func (r *ClusterRepository) InitWorkflow(ctx context.Context, clusterId domain.ClusterId, workflowId string, status domain.ClusterStatus) error { + res := r.db.WithContext(ctx).Model(&model.Cluster{}). Where("ID = ?", clusterId). Updates(map[string]interface{}{"Status": status, "WorkflowId": workflowId, "StatusDesc": ""}) @@ -281,8 +187,8 @@ func (r *ClusterRepository) InitWorkflow(clusterId domain.ClusterId, workflowId return nil } -func (r *ClusterRepository) InitWorkflowDescription(clusterId domain.ClusterId) error { - res := r.db.Model(&AppGroup{}). +func (r *ClusterRepository) InitWorkflowDescription(ctx context.Context, clusterId domain.ClusterId) error { + res := r.db.WithContext(ctx).Model(&model.AppGroup{}). Where("id = ?", clusterId). Updates(map[string]interface{}{"WorkflowId": "", "StatusDesc": ""}) @@ -293,11 +199,11 @@ func (r *ClusterRepository) InitWorkflowDescription(clusterId domain.ClusterId) return nil } -func (r *ClusterRepository) SetFavorite(clusterId domain.ClusterId, userId uuid.UUID) error { - var clusterFavorites []ClusterFavorite - res := r.db.Where("cluster_id = ? AND user_id = ?", clusterId, userId).Find(&clusterFavorites) +func (r *ClusterRepository) SetFavorite(ctx context.Context, clusterId domain.ClusterId, userId uuid.UUID) error { + var clusterFavorites []model.ClusterFavorite + res := r.db.WithContext(ctx).Where("cluster_id = ? AND user_id = ?", clusterId, userId).Find(&clusterFavorites) if res.Error != nil { - log.Info(res.Error) + log.Info(ctx, res.Error) return res.Error } @@ -305,48 +211,25 @@ func (r *ClusterRepository) SetFavorite(clusterId domain.ClusterId, userId uuid. return nil } - clusterFavorite := ClusterFavorite{ + clusterFavorite := model.ClusterFavorite{ + ID: uuid.New(), ClusterId: clusterId, UserId: userId, } resCreate := r.db.Create(&clusterFavorite) if resCreate.Error != nil { - log.Error(resCreate.Error) + log.Error(ctx, resCreate.Error) return fmt.Errorf("could not create cluster favorite for clusterId %s, userId %s", clusterId, userId) } return nil } -func (r *ClusterRepository) DeleteFavorite(clusterId domain.ClusterId, userId uuid.UUID) error { - res := r.db.Unscoped().Delete(&ClusterFavorite{}, "cluster_id = ? AND user_id = ?", clusterId, userId) +func (r *ClusterRepository) DeleteFavorite(ctx context.Context, clusterId domain.ClusterId, userId uuid.UUID) error { + res := r.db.WithContext(ctx).Unscoped().Delete(&model.ClusterFavorite{}, "cluster_id = ? AND user_id = ?", clusterId, userId) if res.Error != nil { - log.Error(res.Error) + log.Error(ctx, res.Error) return fmt.Errorf("could not delete cluster favorite for clusterId %s, userId %s", clusterId, userId) } return nil } - -func reflectCluster(cluster Cluster) (out domain.Cluster) { - if err := serializer.Map(cluster.Model, &out); err != nil { - log.Error(err) - } - - if err := serializer.Map(cluster, &out); err != nil { - log.Error(err) - } - - if err := serializer.Map(cluster, &out.Conf); err != nil { - log.Error(err) - } - out.StackTemplate.Services = cluster.StackTemplate.Services - - if cluster.Favorites != nil && len(*cluster.Favorites) > 0 { - out.Favorited = true - - } else { - out.Favorited = false - } - - return -} diff --git a/internal/repository/dashboard.go b/internal/repository/dashboard.go new file mode 100644 index 00000000..3b2b5713 --- /dev/null +++ b/internal/repository/dashboard.go @@ -0,0 +1,78 @@ +package repository + +import ( + "context" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type IDashboardRepository interface { + CreateDashboard(ctx context.Context, d *model.Dashboard) (string, error) + GetDashboardById(ctx context.Context, organizationId string, dashboardId string) (*model.Dashboard, error) + GetDashboardByUserId(ctx context.Context, organizationId string, userId string, dashboardKey string) (*model.Dashboard, error) + UpdateDashboard(ctx context.Context, d *model.Dashboard) error +} + +type DashboardRepository struct { + db *gorm.DB +} + +func NewDashboardRepository(db *gorm.DB) IDashboardRepository { + return &DashboardRepository{ + db: db, + } +} + +func (dr DashboardRepository) CreateDashboard(ctx context.Context, d *model.Dashboard) (string, error) { + res := dr.db.WithContext(ctx).Create(&d) + if res.Error != nil { + log.Error(ctx, res.Error) + return "", res.Error + } + + return d.ID.String(), nil +} + +func (dr DashboardRepository) GetDashboardById(ctx context.Context, organizationId string, dashboardId string) (d *model.Dashboard, err error) { + res := dr.db.WithContext(ctx).Limit(1). + Where("organization_id = ? and id = ?", organizationId, dashboardId). + First(&d) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find dashboard") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + return d, nil +} + +func (dr DashboardRepository) GetDashboardByUserId(ctx context.Context, organizationId string, userId string, dashboardKey string) (d *model.Dashboard, err error) { + res := dr.db.WithContext(ctx).Limit(1). + Where("organization_id = ? and user_id = ? and key = ?", organizationId, userId, dashboardKey). + First(&d) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find dashboard") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + return d, nil +} + +func (dr DashboardRepository) UpdateDashboard(ctx context.Context, d *model.Dashboard) error { + res := dr.db.WithContext(ctx).Model(&d). + Updates(model.Dashboard{Content: d.Content}) + if res.Error != nil { + log.Error(ctx, res.Error) + return res.Error + } + return nil +} diff --git a/internal/repository/endpoint.go b/internal/repository/endpoint.go new file mode 100644 index 00000000..f51bd2bd --- /dev/null +++ b/internal/repository/endpoint.go @@ -0,0 +1,66 @@ +package repository + +import ( + "context" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "gorm.io/gorm" +) + +type IEndpointRepository interface { + Create(ctx context.Context, endpoint *model.Endpoint) error + List(ctx context.Context, pg *pagination.Pagination) ([]*model.Endpoint, error) + Get(ctx context.Context, id uint) (*model.Endpoint, error) +} + +type EndpointRepository struct { + db *gorm.DB +} + +func NewEndpointRepository(db *gorm.DB) *EndpointRepository { + return &EndpointRepository{ + db: db, + } +} + +func (e *EndpointRepository) Create(ctx context.Context, endpoint *model.Endpoint) error { + obj := &model.Endpoint{ + Name: endpoint.Name, + Group: endpoint.Group, + } + + if err := e.db.WithContext(ctx).Create(obj).Error; err != nil { + return err + } + + return nil +} + +func (e *EndpointRepository) List(ctx context.Context, pg *pagination.Pagination) ([]*model.Endpoint, error) { + var endpoints []*model.Endpoint + + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(e.db.WithContext(ctx).Model(&model.Endpoint{}), &endpoints) + if res.Error != nil { + return nil, res.Error + } + + return endpoints, nil +} + +func (e *EndpointRepository) Get(ctx context.Context, id uint) (*model.Endpoint, error) { + var obj model.Endpoint + + if err := e.db.WithContext(ctx).Preload("Permission").First(&obj, "id = ?", id).Error; err != nil { + return nil, err + } + + return &model.Endpoint{ + Name: obj.Name, + Group: obj.Group, + }, nil +} diff --git a/internal/repository/mapper.go b/internal/repository/mapper.go index c7f7583c..4436142b 100644 --- a/internal/repository/mapper.go +++ b/internal/repository/mapper.go @@ -6,7 +6,6 @@ import ( "github.com/google/uuid" "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/log" ) type ConverterMap map[compositeKey]func(interface{}) (interface{}, error) @@ -47,7 +46,7 @@ func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) e dstField.Set(reflect.ValueOf(converted)) } } else { - log.Errorf("no converter found for %s -> %s", srcField.Type(), dstField.Type()) + //log.Errorf("no converter found for %s -> %s", srcField.Type(), dstField.Type()) continue } } diff --git a/internal/repository/mapper_test.go b/internal/repository/mapper_test.go deleted file mode 100644 index bf040134..00000000 --- a/internal/repository/mapper_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package repository - -import ( - "fmt" - "github.com/google/uuid" - "github.com/openinfradev/tks-api/pkg/domain" - "testing" - "time" -) - -// test case -func TestConvert(t *testing.T) { - genUuid, _ := uuid.NewRandom() - uuidStr := genUuid.String() - - type args struct { - src interface{} - dst interface{} - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "test case 1", - args: args{ - src: User{ - ID: genUuid, - AccountId: "testAccount", - Name: "testName", - Password: "testPassword", - RoleId: uuid.UUID{}, - Role: Role{}, - OrganizationId: "testOrganizationId", - Organization: Organization{}, - Creator: uuid.UUID{}, - Email: "testEmail", - Department: "testDepartment", - Description: "testDescription", - }, - dst: &domain.User{}, - }, - wantErr: false, - }, - { - name: "test case 2", - args: args{ - src: domain.User{ - ID: uuidStr, - AccountId: "testAccount", - Password: "testPassword", - Name: "testName", - Token: "testToken", - Role: domain.Role{}, - Organization: domain.Organization{}, - Creator: "testCreator", - CreatedAt: time.Time{}, - UpdatedAt: time.Time{}, - Email: "test email", - Department: "testDepartment", - Description: "testDescription", - }, - dst: &User{}, - }, - wantErr: false, - }, - } - _ = tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := Map(tt.args.src, tt.args.dst); (err != nil) != tt.wantErr { - t.Errorf("Map() error = %v, wantErr %v", err, tt.wantErr) - } else { - fmt.Println(tt.args.dst) - } - }) - } -} diff --git a/internal/repository/organization.go b/internal/repository/organization.go index 75cc2e7a..0b53d9bf 100644 --- a/internal/repository/organization.go +++ b/internal/repository/organization.go @@ -1,26 +1,34 @@ package repository import ( - "fmt" - "math" + "context" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/log" "gorm.io/gorm" + "gorm.io/gorm/clause" ) // Interfaces type IOrganizationRepository interface { - Create(organizationId string, name string, creator uuid.UUID, phone string, description string) (domain.Organization, error) - Fetch(pg *pagination.Pagination) (res *[]domain.Organization, err error) - Get(organizationId string) (res domain.Organization, err error) - Update(organizationId string, in domain.UpdateOrganizationRequest) (domain.Organization, error) - UpdatePrimaryClusterId(organizationId string, primaryClusterId string) error - Delete(organizationId string) (err error) - InitWorkflow(organizationId string, workflowId string, status domain.OrganizationStatus) error + Create(ctx context.Context, dto *model.Organization) (model.Organization, error) + Fetch(ctx context.Context, pg *pagination.Pagination) (res *[]model.Organization, err error) + Get(ctx context.Context, organizationId string) (res model.Organization, err error) + Update(ctx context.Context, organizationId string, in model.Organization) (model.Organization, error) + UpdatePrimaryClusterId(ctx context.Context, organizationId string, primaryClusterId string) error + UpdateAdminId(ctx context.Context, organizationId string, adminId uuid.UUID) error + AddStackTemplates(ctx context.Context, organizationId string, stackTemplates []model.StackTemplate) (err error) + RemoveStackTemplates(ctx context.Context, organizationId string, stackTemplates []model.StackTemplate) (err error) + AddSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplates []model.SystemNotificationTemplate) (err error) + RemoveSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplates []model.SystemNotificationTemplate) (err error) + Delete(ctx context.Context, organizationId string) (err error) + InitWorkflow(ctx context.Context, organizationId string, workflowId string, status domain.OrganizationStatus) error + AddPermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplates []model.PolicyTemplate) (err error) + UpdatePermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplates []model.PolicyTemplate) (err error) + DeletePermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplateids []uuid.UUID) (err error) } type OrganizationRepository struct { @@ -33,146 +41,230 @@ func NewOrganizationRepository(db *gorm.DB) IOrganizationRepository { } } -// Models -type Organization struct { - gorm.Model - - ID string `gorm:"primarykey;type:varchar(36);not null"` - Name string - Description string - Phone string - WorkflowId string - Status domain.OrganizationStatus - StatusDesc string - Creator uuid.UUID - PrimaryClusterId string // allow null -} +// +//// Models +//type Organization struct { +// gorm.Model +// +// ID string `gorm:"primarykey;type:varchar(36);not null"` +// Name string +// Description string +// Phone string +// WorkflowId string +// Status model.OrganizationStatus +// StatusDesc string +// Creator uuid.UUID +// PrimaryClusterId string // allow null +//} //func (c *Organization) BeforeCreate(tx *gorm.DB) (err error) { // c.ID = helper.GenerateOrganizationId() // return nil //} -func (r *OrganizationRepository) Create(organizationId string, name string, creator uuid.UUID, phone string, - description string) (domain.Organization, error) { - organization := Organization{ - ID: organizationId, - Name: name, - Creator: creator, - Phone: phone, - Description: description, +func (r *OrganizationRepository) Create(ctx context.Context, dto *model.Organization) (model.Organization, error) { + organization := model.Organization{ + ID: dto.ID, + Name: dto.Name, + CreatorId: dto.CreatorId, + Description: dto.Description, Status: domain.OrganizationStatus_PENDING, + Phone: dto.Phone, } - res := r.db.Create(&organization) + res := r.db.WithContext(ctx).Create(&organization) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.Organization{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.Organization{}, res.Error } - return r.reflect(organization), nil + return organization, nil } -func (r *OrganizationRepository) Fetch(pg *pagination.Pagination) (*[]domain.Organization, error) { - var organizations []Organization - var out []domain.Organization +func (r *OrganizationRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out *[]model.Organization, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("organizations", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&Organization{})) - db.Count(&pg.TotalRows) + db := r.db.WithContext(ctx).Preload(clause.Associations).Model(&model.Organization{}) + + // [TODO] more pretty! + for _, filter := range pg.Filters { + if filter.Relation == "Admin" { + db = db.Joins("join users on users.id::text = organizations.admin_id::text"). + Where("users.name ilike ?", "%"+filter.Values[0]+"%") + break + } + } - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&organizations) + _, res := pg.Fetch(db, &out) if res.Error != nil { return nil, res.Error } - for _, organization := range organizations { - outOrganization := r.reflect(organization) - out = append(out, outOrganization) - } - return &out, nil + return } -func (r *OrganizationRepository) Get(id string) (domain.Organization, error) { - var organization Organization - res := r.db.First(&organization, "id = ?", id) +func (r *OrganizationRepository) Get(ctx context.Context, id string) (out model.Organization, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations). + First(&out, "id = ?", id) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.Organization{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.Organization{}, res.Error } - - return r.reflect(organization), nil + return } -func (r *OrganizationRepository) Update(organizationId string, in domain.UpdateOrganizationRequest) (domain.Organization, error) { - var organization Organization - res := r.db.Model(&Organization{}). +func (r *OrganizationRepository) Update(ctx context.Context, organizationId string, in model.Organization) (out model.Organization, err error) { + res := r.db.WithContext(ctx).Model(&model.Organization{}). Where("id = ?", organizationId). Updates(map[string]interface{}{ "name": in.Name, "description": in.Description, - "phone": in.Phone, }) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.Organization{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.Organization{}, res.Error } - res = r.db.Model(&Organization{}).Where("id = ?", organizationId).Find(&organization) + res = r.db.Model(&model.Organization{}).Where("id = ?", organizationId).Find(&out) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.Organization{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.Organization{}, res.Error } - - return r.reflect(organization), nil + return } -func (r *OrganizationRepository) UpdatePrimaryClusterId(organizationId string, primaryClusterId string) error { - res := r.db.Model(&Organization{}). +func (r *OrganizationRepository) UpdatePrimaryClusterId(ctx context.Context, organizationId string, primaryClusterId string) error { + res := r.db.WithContext(ctx).Model(&model.Organization{}). Where("id = ?", organizationId). Updates(map[string]interface{}{ "primary_cluster_id": primaryClusterId, }) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return res.Error + } + return nil +} + +func (r *OrganizationRepository) UpdateAdminId(ctx context.Context, organizationId string, adminId uuid.UUID) (err error) { + res := r.db.WithContext(ctx).Model(&model.Organization{}). + Where("id = ?", organizationId). + Updates(map[string]interface{}{ + "admin_id": adminId, + }) + + if res.Error != nil { + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } return nil } -func (r *OrganizationRepository) Delete(organizationId string) error { - res := r.db.Delete(&Organization{}, "id = ?", organizationId) +func (r *OrganizationRepository) Delete(ctx context.Context, organizationId string) error { + res := r.db.WithContext(ctx).Delete(&model.Organization{}, "id = ?", organizationId) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } return nil } -func (r *OrganizationRepository) InitWorkflow(organizationId string, workflowId string, status domain.OrganizationStatus) error { - res := r.db.Model(&Organization{}). +func (r *OrganizationRepository) InitWorkflow(ctx context.Context, organizationId string, workflowId string, status domain.OrganizationStatus) error { + res := r.db.WithContext(ctx).Model(&model.Organization{}). Where("ID = ?", organizationId). Updates(map[string]interface{}{"Status": status, "WorkflowId": workflowId}) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } return nil } -func (r *OrganizationRepository) reflect(organization Organization) (out domain.Organization) { - if err := serializer.Map(organization.Model, &out); err != nil { - log.Error(err) +func (r *OrganizationRepository) AddStackTemplates(ctx context.Context, organizationId string, stackTemplates []model.StackTemplate) (err error) { + var organization = model.Organization{} + res := r.db.WithContext(ctx).Preload("StackTemplates").First(&organization, "id = ?", organizationId) + if res.Error != nil { + return res.Error + } + + err = r.db.WithContext(ctx).Model(&organization).Association("StackTemplates").Append(stackTemplates) + if err != nil { + return err } - if err := serializer.Map(organization, &out); err != nil { - log.Error(err) + + return nil +} + +func (r *OrganizationRepository) RemoveStackTemplates(ctx context.Context, organizationId string, stackTemplates []model.StackTemplate) (err error) { + var organization = model.Organization{} + res := r.db.WithContext(ctx).Preload("StackTemplates").First(&organization, "id = ?", organizationId) + if res.Error != nil { + return res.Error } - out.Creator = organization.Creator.String() - return + err = r.db.WithContext(ctx).Model(&organization).Association("StackTemplates").Delete(stackTemplates) + if err != nil { + return err + } + + return nil +} + +func (r *OrganizationRepository) AddSystemNotificationTemplates(ctx context.Context, organizationId string, templates []model.SystemNotificationTemplate) (err error) { + var organization = model.Organization{} + res := r.db.WithContext(ctx).Preload("SystemNotificationTemplates").First(&organization, "id = ?", organizationId) + if res.Error != nil { + return res.Error + } + + err = r.db.WithContext(ctx).Model(&organization).Association("SystemNotificationTemplates").Append(templates) + if err != nil { + return err + } + + return nil +} + +func (r *OrganizationRepository) RemoveSystemNotificationTemplates(ctx context.Context, organizationId string, templates []model.SystemNotificationTemplate) (err error) { + var organization = model.Organization{} + res := r.db.WithContext(ctx).Preload("SystemNotificationTemplates").First(&organization, "id = ?", organizationId) + if res.Error != nil { + return res.Error + } + + err = r.db.WithContext(ctx).Model(&organization).Association("SystemNotificationTemplates").Delete(templates) + if err != nil { + return err + } + + return nil +} + +func (r *OrganizationRepository) AddPermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplates []model.PolicyTemplate) (err error) { + var organization model.Organization + organization.ID = organizationId + + err = r.db.WithContext(ctx).Model(&organization). + Association("PolicyTemplates").Append(policyTemplates) + + return err +} + +func (r *OrganizationRepository) UpdatePermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplates []model.PolicyTemplate) (err error) { + var organization model.Organization + organization.ID = organizationId + + err = r.db.WithContext(ctx).Model(&organization). + Association("PolicyTemplates").Replace(policyTemplates) + + return err +} + +func (r *OrganizationRepository) DeletePermittedPolicyTemplatesByID(ctx context.Context, organizationId string, policyTemplateids []uuid.UUID) (err error) { + return r.db.WithContext(ctx). + Where("organization_id = ?", organizationId). + Where("policy_template_id in ?", policyTemplateids). + Delete(&model.PolicyTemplatePermittedOrganization{}).Error } diff --git a/internal/repository/permission.go b/internal/repository/permission.go new file mode 100644 index 00000000..22a7d6bb --- /dev/null +++ b/internal/repository/permission.go @@ -0,0 +1,84 @@ +package repository + +import ( + "context" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "gorm.io/gorm" +) + +type IPermissionRepository interface { + Create(ctx context.Context, permission *model.Permission) error + List(ctx context.Context, roleId string) ([]*model.Permission, error) + Get(ctx context.Context, id uuid.UUID) (*model.Permission, error) + Delete(ctx context.Context, id uuid.UUID) error + Update(ctx context.Context, permission *model.Permission) error +} + +type PermissionRepository struct { + db *gorm.DB +} + +func NewPermissionRepository(db *gorm.DB) *PermissionRepository { + return &PermissionRepository{ + db: db, + } +} + +func (r PermissionRepository) Create(ctx context.Context, p *model.Permission) error { + //var parent *Permission + //var children []*Permission + // + //if p.Parent != nil { + // parent = &Permission{} + // result := r.db.First(&parent, "id = ?", p.Parent.ID) + // if result.Error != nil { + // return result.Error + // } + //} + //if p.Children != nil { + // for _, child := range p.Children { + // newChild := &Permission{} + // result := r.db.First(&newChild, "id = ?", child.ID) + // if result.Error != nil { + // return result.Error + // } + // children = append(children, newChild) + // } + //} + + p.ID = uuid.New() + return r.db.WithContext(ctx).Create(p).Error +} + +func (r PermissionRepository) List(ctx context.Context, roleId string) ([]*model.Permission, error) { + var permissions []*model.Permission + + err := r.db.WithContext(ctx).Preload("Children.Children.Children.Children"). + Where("parent_id IS NULL AND role_id = ?", roleId).Find(&permissions).Error + if err != nil { + return nil, err + } + + return permissions, nil +} + +func (r PermissionRepository) Get(ctx context.Context, id uuid.UUID) (*model.Permission, error) { + permission := &model.Permission{} + result := r.db.WithContext(ctx).Preload("Children.Children.Children").Preload("Parent").First(&permission, "id = ?", id) + if result.Error != nil { + return nil, result.Error + } + + return permission, nil +} + +func (r PermissionRepository) Delete(ctx context.Context, id uuid.UUID) error { + return r.db.WithContext(ctx).Delete(&model.Permission{}, "id = ?", id).Error +} + +func (r PermissionRepository) Update(ctx context.Context, p *model.Permission) error { + // update on is_allowed + return r.db.WithContext(ctx).Model(&model.Permission{}).Where("id = ?", p.ID).Updates(map[string]interface{}{"is_allowed": p.IsAllowed}).Error +} diff --git a/internal/repository/policy-template.go b/internal/repository/policy-template.go new file mode 100644 index 00000000..24dd7df0 --- /dev/null +++ b/internal/repository/policy-template.go @@ -0,0 +1,539 @@ +package repository + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type IPolicyTemplateRepository interface { + Create(ctx context.Context, policyTemplate model.PolicyTemplate) (policyTemplateId uuid.UUID, err error) + Update(ctx context.Context, policyTemplateId uuid.UUID, updateMap map[string]interface{}, permittedOrganizations *[]model.Organization) (err error) + Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.PolicyTemplate, err error) + FetchForOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.PolicyTemplate, err error) + GetByName(ctx context.Context, policyTemplateName string) (out *model.PolicyTemplate, err error) + GetByKind(ctx context.Context, policyTemplateKind string) (out *model.PolicyTemplate, err error) + GetByID(ctx context.Context, policyTemplateId uuid.UUID) (out *model.PolicyTemplate, err error) + Delete(ctx context.Context, policyTemplateId uuid.UUID) (err error) + ExistByName(ctx context.Context, policyTemplateName string) (exist bool, err error) + ExistByKind(ctx context.Context, policyTemplateKind string) (exist bool, err error) + ExistByNameInOrganization(ctx context.Context, organizationId string, policyTemplateName string) (exist bool, err error) + ExistByKindInOrganization(ctx context.Context, organizationId string, policyTemplateKind string) (exist bool, err error) + ExistByID(ctx context.Context, policyTemplateId uuid.UUID) (exist bool, err error) + ListPolicyTemplateVersions(ctx context.Context, policyTemplateId uuid.UUID) (policyTemplateVersionsReponse *domain.ListPolicyTemplateVersionsResponse, err error) + GetPolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, version string) (policyTemplateVersionsReponse *model.PolicyTemplate, err error) + DeletePolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, version string) (err error) + CreatePolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, newVersion string, + schema []*domain.ParameterDef, rego string, libs []string, syncKinds *[]string, syncJson *string) (version string, err error) + GetLatestTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID) (version string, err error) + CountTksTemplateByOrganization(ctx context.Context, organizationId string) (count int64, err error) + CountOrganizationTemplate(ctx context.Context, organizationId string) (count int64, err error) + CountPolicyFromOrganizationTemplate(ctx context.Context, organizationId string) (count int64, err error) + GetPolicyTemplateByOrganizationIdOrTKS(ctx context.Context, organizationId string) ([]model.PolicyTemplate, error) +} + +type PolicyTemplateRepository struct { + db *gorm.DB +} + +func NewPolicyTemplateRepository(db *gorm.DB) IPolicyTemplateRepository { + return &PolicyTemplateRepository{ + db: db, + } +} + +func (r *PolicyTemplateRepository) Create(ctx context.Context, dto model.PolicyTemplate) (policyTemplateId uuid.UUID, err error) { + err = r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // 이미 org가 존재하므로 many2many 레코드를 추가하지 않고 관계만 업데이트하도록 보장 + if err := tx.Omit("PermittedOrganizations").Create(&dto).Error; err != nil { + return err + } + + if err := tx.Model(&dto).Association("PermittedOrganizations"). + Append(dto.PermittedOrganizations); err != nil { + return err + } + + return nil + }) + + if err != nil { + return uuid.Nil, err + } + + return dto.ID, nil +} + +func (r *PolicyTemplateRepository) Update(ctx context.Context, policyTemplateId uuid.UUID, + updateMap map[string]interface{}, permittedOrganizations *[]model.Organization) (err error) { + + var policyTemplate model.PolicyTemplate + policyTemplate.ID = policyTemplateId + + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if permittedOrganizations != nil { + err = tx.WithContext(ctx).Model(&policyTemplate).Limit(1). + Association("PermittedOrganizations").Replace(permittedOrganizations) + + if err != nil { + return err + } + } + + if len(updateMap) > 0 { + err = tx.WithContext(ctx).Omit("PermittedOrganizations").Model(&policyTemplate).Limit(1). + Where("id = ?", policyTemplateId).Where("type = ?", "tks"). + Updates(updateMap).Error + + if err != nil { + return err + } + } + + // return nil will commit the whole transaction + return nil + }) +} + +func (r *PolicyTemplateRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.PolicyTemplate, err error) { + var policyTemplates []model.PolicyTemplate + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx). + Preload("SupportedVersions", func(db *gorm.DB) *gorm.DB { + // 최신 버전만 + return db.Order("policy_template_supported_versions.version DESC") + }). + Preload("PermittedOrganizations").Preload("Creator").Preload("Updator"). + Model(&model.PolicyTemplate{}). + Where("type = 'tks'"), &policyTemplates) + if res.Error != nil { + return nil, res.Error + } + + return policyTemplates, nil +} + +func (r *PolicyTemplateRepository) FetchForOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.PolicyTemplate, err error) { + var policyTemplates []model.PolicyTemplate + if pg == nil { + pg = pagination.NewPagination(nil) + } + + // 다음과 같은 쿼리를 생성해서 tks 템플릿에 대해선 PermittedOrganizations가 비거나, PermittedOrganizations에 해당 organizations이 속하는 템플릿을 찾음 + // organization 템플릿은 organizationId가 매칭되는 것을 찾음, 이를 통해 해당 사용자가 사용할 수 있는 모든 템플릿을 fetch + // select id from policy_templates where + // ( + // type = 'tks' + // and ( + // id not in (select policy_template_id from policy_template_permitted_organizations) -- PermitedOrganizations이 빈 경우, 모두에게 허용 + // or id in (select policy_template_id from policy_template_permitted_organizations organization where organization_id = 'orgid') -- PermitedOrganizations 허용된 경우 + // ) + // ) + // or (type = 'organization' and organization_id='orgid') + subQueryAloowedAll := r.db.Table("policy_template_permitted_organizations").Select("policy_template_id") + subQueryMatchId := r.db.Table("policy_template_permitted_organizations").Select("policy_template_id"). + Where("organization_id = ?", organizationId) + + _, res := pg.Fetch(r.db.WithContext(ctx). + Preload("SupportedVersions", func(db *gorm.DB) *gorm.DB { + // 최신 버전만 + return db.Order("policy_template_supported_versions.version DESC") + }). + Preload("Creator").Preload("Updator"). // organization을 기준으로 조회할 때에는 PermittedOrganizations는 로딩하지 않아도 됨 + Model(&model.PolicyTemplate{}). + Where( + // tks 템플릿인 경우 + r.db.Where("type = ?", "tks"). + Where( + // permitted_organizations이 비어있거나 + r.db.Where("id not in (?)", subQueryAloowedAll). + Or("id in (?)", subQueryMatchId), + // permitted_organization에 매칭되는 템플릿 아이디가 있거나 + ), + ). + Or( + // organization 타입 템플릿이면서 organization_id가 매칭 + r.db.Where("type = ?", "organization"). + Where("organization_id = ?", organizationId), + ), + &policyTemplates) + + if res.Error != nil { + return nil, res.Error + } + + return policyTemplates, nil +} + +func (r *PolicyTemplateRepository) CountTksTemplateByOrganization(ctx context.Context, organizationId string) (count int64, err error) { + subQueryAloowedAll := r.db.Table("policy_template_permitted_organizations").Select("policy_template_id") + subQueryMatchId := r.db.Table("policy_template_permitted_organizations").Select("policy_template_id"). + Where("organization_id = ?", organizationId) + + err = r.db.WithContext(ctx). + Model(&model.PolicyTemplate{}). + Where( + // tks 템플릿인 경우 + r.db.Where("type = ?", "tks"). + Where( + // permitted_organizations이 비어있거나 + r.db.Where("id not in (?)", subQueryAloowedAll). + Or("id in (?)", subQueryMatchId), + // permitted_organization에 매칭되는 템플릿 아이디가 있거나 + ), + ).Count(&count).Error + + return +} + +func (r *PolicyTemplateRepository) CountOrganizationTemplate(ctx context.Context, organizationId string) (count int64, err error) { + err = r.db.WithContext(ctx). + Model(&model.PolicyTemplate{}). + Where("type = ?", "organization"). + Where("organization_id = ?", organizationId). + Count(&count).Error + + return +} + +func (r *PolicyTemplateRepository) CountPolicyFromOrganizationTemplate(ctx context.Context, organizationId string) (count int64, err error) { + subQuery := r.db.Table("policy_templates").Select("id"). + Where("type = ?", "organization"). + Where("organization_id = ?", organizationId) + + err = r.db.WithContext(ctx). + Model(&model.Policy{}). + Where( + r.db.Where("template_id in (?)", subQuery), + ). + Count(&count).Error + + return +} + +func (r *PolicyTemplateRepository) ExistsBy(ctx context.Context, key string, value interface{}) (exists bool, err error) { + query := fmt.Sprintf("%s = ?", key) + + var policyTemplate model.PolicyTemplate + res := r.db.WithContext(ctx).Where(query, value). + First(&policyTemplate) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policyTemplate %s='%v'", key, value) + return false, nil + } else { + log.Error(ctx, res.Error) + return false, res.Error + } + } + + return true, nil +} + +func (r *PolicyTemplateRepository) ExistByName(ctx context.Context, policyTemplateName string) (exist bool, err error) { + return r.ExistsBy(ctx, "template_name", policyTemplateName) +} + +func (r *PolicyTemplateRepository) ExistByKind(ctx context.Context, policyTemplateKind string) (exist bool, err error) { + return r.ExistsBy(ctx, "kind", policyTemplateKind) +} + +func (r *PolicyTemplateRepository) ExistsByInOrganization(ctx context.Context, organizationId string, key string, value interface{}) (exists bool, err error) { + + var policyTemplate model.PolicyTemplate + // query := fmt.Sprintf("%s = ? and (type = 'tks' or organization_id = ?)", key) + // res := r.db.WithContext(ctx).Where(query, value, organizationId). + // First(&policyTemplate) + + res := r.db.WithContext(ctx).Where(fmt.Sprintf("%s = ?", key), value). + Where( + r.db.Where("type = ?", "tks").Or("organization_id = ?", organizationId), + ).First(&policyTemplate) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policyTemplate %s='%v'", key, value) + return false, nil + } else { + log.Error(ctx, res.Error) + return false, res.Error + } + } + + return true, nil +} + +func (r *PolicyTemplateRepository) ExistByNameInOrganization(ctx context.Context, organizationId string, policyTemplateName string) (exist bool, err error) { + return r.ExistsByInOrganization(ctx, organizationId, "template_name", policyTemplateName) +} + +func (r *PolicyTemplateRepository) ExistByKindInOrganization(ctx context.Context, organizationId string, policyTemplateKind string) (exist bool, err error) { + return r.ExistsByInOrganization(ctx, organizationId, "kind", policyTemplateKind) +} + +func (r *PolicyTemplateRepository) ExistByID(ctx context.Context, policyTemplateId uuid.UUID) (exist bool, err error) { + return r.ExistsBy(ctx, "id", policyTemplateId) +} + +func (r *PolicyTemplateRepository) GetBy(ctx context.Context, key string, value interface{}) (out *model.PolicyTemplate, err error) { + query := fmt.Sprintf("%s = ?", key) + + var policyTemplate model.PolicyTemplate + // res := r.db.WithContext(ctx).Preload(clause.Associations).Where(query, value). + // First(&policyTemplate) + res := r.db.WithContext(ctx). + Preload("SupportedVersions", func(db *gorm.DB) *gorm.DB { + // 최신 버전만 + return db.Order("policy_template_supported_versions.version DESC").Limit(1) + }). + Preload("PermittedOrganizations").Preload("Creator").Preload("Updator"). + Where(query, value). + First(&policyTemplate) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found policyTemplate id") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + if len(policyTemplate.SupportedVersions) == 0 { + log.Info(ctx, "Not found policyTemplate version") + return nil, nil + } + + return &policyTemplate, nil +} + +func (r *PolicyTemplateRepository) GetByID(ctx context.Context, policyTemplateId uuid.UUID) (out *model.PolicyTemplate, err error) { + return r.GetBy(ctx, "id", policyTemplateId) +} + +func (r *PolicyTemplateRepository) GetByName(ctx context.Context, policyTemplateName string) (out *model.PolicyTemplate, err error) { + return r.GetBy(ctx, "name", policyTemplateName) +} + +func (r *PolicyTemplateRepository) GetByKind(ctx context.Context, policyTemplateKind string) (out *model.PolicyTemplate, err error) { + return r.GetBy(ctx, "kind", policyTemplateKind) +} + +func (r *PolicyTemplateRepository) Delete(ctx context.Context, policyTemplateId uuid.UUID) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Where("policy_template_id = ?", policyTemplateId).Delete(&model.PolicyTemplateSupportedVersion{}).Error; err != nil { + return err + } + + if err := tx.Model(&model.PolicyTemplate{ID: policyTemplateId}).Association("PermittedOrganizations").Clear(); err != nil { + return err + } + + if err := tx.Where("id = ?", policyTemplateId).Delete(&model.PolicyTemplate{}).Error; err != nil { + return err + } + + return nil + }) +} + +func (r *PolicyTemplateRepository) ListPolicyTemplateVersions(ctx context.Context, policyTemplateId uuid.UUID) (policyTemplateVersionsReponse *domain.ListPolicyTemplateVersionsResponse, err error) { + var supportedVersions []model.PolicyTemplateSupportedVersion + res := r.db.WithContext(ctx).Where("policy_template_id = ?", policyTemplateId).Find(&supportedVersions) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found policyTemplate Id") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + versions := make([]string, len(supportedVersions)) + + for i, supportedVersion := range supportedVersions { + versions[i] = supportedVersion.Version + } + + result := &domain.ListPolicyTemplateVersionsResponse{ + Versions: versions, + } + + return result, nil +} + +func (r *PolicyTemplateRepository) GetPolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, version string) (policyTemplateVersionsReponse *model.PolicyTemplate, err error) { + var policyTemplate model.PolicyTemplate + + res := r.db.WithContext(ctx).Preload("SupportedVersions", "version=?", version). + Preload("PermittedOrganizations").Preload("Creator").Preload("Updator"). + Where("id = ?", policyTemplateId). + First(&policyTemplate) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found policyTemplate id") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + if len(policyTemplate.SupportedVersions) == 0 { + log.Info(ctx, "Not found policyTemplate version") + return nil, nil + } + + return &policyTemplate, nil +} + +func (r *PolicyTemplateRepository) DeletePolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, version string) (err error) { + // TODO: Operator에 현재 버전 사용중인 정책이 있는지 체크 필요 + + var policyTemplateVersion model.PolicyTemplateSupportedVersion + + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var count int64 + res := r.db.WithContext(ctx).Model(&policyTemplateVersion).Where("policy_template_id = ?", policyTemplateId).Count(&count) + if res.Error != nil { + return res.Error + } + + // 마지막으로 존재하는 버전은 삭제할 수 없으므로 해당 id의 버전 카운트가 2 이상이어야 함 + if count < 2 { + return errors.New("Unable to delete last single version") + } + + // relaton을 unscoped로 삭제하지 않으면 동일한 키로 다시 생성할 때 키가 같은 레코드가 deleted 상태로 존재하므로 unscoped delete + res = r.db.WithContext(ctx).Unscoped().Where("policy_template_id = ?", policyTemplateId).Where("version = ?", version). + Delete(&policyTemplateVersion) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found policyTemplate version") + return nil + } else { + log.Error(ctx, res.Error) + return res.Error + } + } + + return nil + }) +} + +func (r *PolicyTemplateRepository) CreatePolicyTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID, newVersion string, + schema []*domain.ParameterDef, rego string, libs []string, syncKinds *[]string, syncJson *string) (version string, err error) { + var policyTemplateVersion model.PolicyTemplateSupportedVersion + res := r.db.WithContext(ctx).Limit(1). + Where("policy_template_id = ?", policyTemplateId).Where("version = ?", version). + First(&policyTemplateVersion) + + if res.Error == nil { + err = errors.Errorf("Version %s already exists for the policyTemplate", newVersion) + + log.Error(ctx, res.Error) + + return "", err + } + + if !errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Error(ctx, res.Error) + return "", res.Error + } + + libsString := "" + if len(libs) > 0 { + libsString = strings.Join(libs, model.FILE_DELIMETER) + } + + jsonBytes, err := json.Marshal(schema) + + if err != nil { + parseErr := errors.Errorf("Unable to parse parameter schema: %v", err) + + log.Error(ctx, parseErr) + + return "", parseErr + } + + var syncKindsString *string = nil + + if syncKinds != nil { + syncJsonBytes, err := json.Marshal(syncKinds) + + if err != nil { + parseErr := errors.Errorf("Unable to parse parameter schema: %v", err) + + log.Error(ctx, parseErr) + + return "", parseErr + } + + syncStr := string(syncJsonBytes) + + syncKindsString = &syncStr + } + + newPolicyTemplateVersion := &model.PolicyTemplateSupportedVersion{ + PolicyTemplateId: policyTemplateId, + Version: newVersion, + Rego: rego, + Libs: libsString, + ParameterSchema: string(jsonBytes), + SyncJson: syncJson, + SyncKinds: syncKindsString, + } + + if err := r.db.WithContext(ctx).Create(newPolicyTemplateVersion).Error; err != nil { + return "", err + } + + return newVersion, nil +} + +func (r *PolicyTemplateRepository) GetLatestTemplateVersion(ctx context.Context, policyTemplateId uuid.UUID) (version string, err error) { + var policyTemplateVersion model.PolicyTemplateSupportedVersion + + err = r.db.WithContext(ctx). + Where("policy_template_id = ?", policyTemplateId). + Order("created_at desc").First(&policyTemplateVersion).Error + + if err != nil { + return + } + + return policyTemplateVersion.Version, nil +} + +func (r *PolicyTemplateRepository) GetPolicyTemplateByOrganizationIdOrTKS(ctx context.Context, organizationId string) (out []model.PolicyTemplate, err error) { + res := r.db.WithContext(ctx). + Select("id", "type", "template_name"). + Where("organization_id = ? or type = ?", organizationId, "tks"). + Find(&out) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find policytemplate") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + return out, nil +} diff --git a/internal/repository/policy.go b/internal/repository/policy.go new file mode 100644 index 00000000..ff995800 --- /dev/null +++ b/internal/repository/policy.go @@ -0,0 +1,363 @@ +package repository + +import ( + "context" + "fmt" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type IPolicyRepository interface { + Create(ctx context.Context, dto model.Policy) (policyId uuid.UUID, err error) + Update(ctx context.Context, organizationId string, policyId uuid.UUID, + updateMap map[string]interface{}, TargetClusters *[]model.Cluster) (err error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out *[]model.Policy, err error) + FetchByClusterId(ctx context.Context, clusterId string, pg *pagination.Pagination) (out *[]model.Policy, err error) + FetchByClusterIdAndTemplaeId(ctx context.Context, clusterId string, templateId uuid.UUID) (out *[]model.Policy, err error) + ExistByName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) + ExistByResourceName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) + ExistByID(ctx context.Context, organizationId string, policyId uuid.UUID) (exist bool, err error) + GetByName(ctx context.Context, organizationId string, policyName string) (out *model.Policy, err error) + GetByID(ctx context.Context, organizationId string, policyId uuid.UUID) (out *model.Policy, err error) + Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) + UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusters []model.Cluster) (err error) + SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) + GetUsageCountByTemplateId(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (usageCounts []model.UsageCount, err error) + CountPolicyByEnforcementAction(ctx context.Context, organizationId string) (policyCount []model.PolicyCount, err error) + AddPoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policies []model.Policy) (err error) + UpdatePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policies []model.Policy) (err error) + DeletePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) + GetPolicyIDsByClusterID(ctx context.Context, clusterId domain.ClusterId) (out *[]uuid.UUID, err error) +} + +type PolicyRepository struct { + db *gorm.DB +} + +func NewPolicyRepository(db *gorm.DB) IPolicyRepository { + return &PolicyRepository{ + db: db, + } +} + +func (r *PolicyRepository) Create(ctx context.Context, dto model.Policy) (policyId uuid.UUID, err error) { + err = r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + // 이미 org가 존재하므로 many2many 레코드를 추가하지 않고 관계만 업데이트하도록 보장 + if err := tx.Omit("TargetClusters").Create(&dto).Error; err != nil { + return err + } + + if err := tx.Model(&dto).Association("TargetClusters"). + Append(dto.TargetClusters); err != nil { + return err + } + + return nil + }) + + if err != nil { + return uuid.Nil, err + } + + return dto.ID, nil +} + +func (r *PolicyRepository) Update(ctx context.Context, organizationId string, policyId uuid.UUID, + updateMap map[string]interface{}, targetClusters *[]model.Cluster) (err error) { + + var policy model.Policy + policy.ID = policyId + + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if targetClusters != nil { + err = tx.WithContext(ctx).Model(&policy).Limit(1). + Association("TargetClusters").Replace(targetClusters) + + if err != nil { + return err + } + } + + if len(updateMap) > 0 { + err = tx.WithContext(ctx).Omit("TargetClusters").Model(&policy).Limit(1). + Where("id = ?", policyId). + Updates(updateMap).Error + + if err != nil { + return err + } + } + + // return nil will commit the whole transaction + return nil + }) +} + +func (r *PolicyRepository) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out *[]model.Policy, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations). + Where("policies.organization_id = ?", organizationId), &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *PolicyRepository) FetchByClusterId(ctx context.Context, clusterId string, pg *pagination.Pagination) (out *[]model.Policy, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + subQueryClusterId := r.db.Table("policy_target_clusters").Select("policy_id"). + Where("cluster_id = ?", clusterId) + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations). + Where("id in (?)", subQueryClusterId), &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *PolicyRepository) FetchByClusterIdAndTemplaeId(ctx context.Context, clusterId string, templateId uuid.UUID) (out *[]model.Policy, err error) { + subQueryClusterId := r.db.Table("policy_target_clusters").Select("policy_id"). + Where("cluster_id = ?", clusterId) + + res := r.db.WithContext(ctx).Preload(clause.Associations). + Where("template_id = ?", templateId).Where("id in (?)", subQueryClusterId).Find(&out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *PolicyRepository) ExistBy(ctx context.Context, organizationId string, key string, value interface{}) (exists bool, err error) { + query := fmt.Sprintf("organization_id = ? and %s = ?", key) + + var policy model.Policy + res := r.db.WithContext(ctx).Where(query, organizationId, value). + First(&policy) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policy %s='%v'", key, value) + return false, nil + } else { + log.Error(ctx, res.Error) + return false, res.Error + } + } + + return true, nil +} + +func (r *PolicyRepository) ExistByName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) { + return r.ExistBy(ctx, organizationId, "policy_name", policyName) +} + +func (r *PolicyRepository) ExistByResourceName(ctx context.Context, organizationId string, policyName string) (exist bool, err error) { + return r.ExistBy(ctx, organizationId, "policy_resource_name", policyName) +} + +func (r *PolicyRepository) ExistByID(ctx context.Context, organizationId string, policyId uuid.UUID) (exist bool, err error) { + return r.ExistBy(ctx, organizationId, "id", policyId) +} + +func (r *PolicyRepository) GetBy(ctx context.Context, organizationId string, key string, value interface{}) (out *model.Policy, err error) { + query := fmt.Sprintf("organization_id = ? and %s = ?", key) + + var policy model.Policy + res := r.db.WithContext(ctx).Preload(clause.Associations). + Where(query, organizationId, value).First(&policy) + if res.Error != nil { + log.Error(ctx, res.Error) + return nil, res.Error + } + + return &policy, nil +} + +func (r *PolicyRepository) GetByName(ctx context.Context, organizationId string, policyName string) (out *model.Policy, err error) { + return r.GetBy(ctx, organizationId, "policy_name", policyName) +} + +func (r *PolicyRepository) GetByID(ctx context.Context, organizationId string, policyId uuid.UUID) (out *model.Policy, err error) { + return r.GetBy(ctx, organizationId, "id", policyId) +} + +func (r *PolicyRepository) Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if err := tx.Model(&model.Policy{ID: policyId}).Association("TargetClusters").Clear(); err != nil { + return err + } + + if err := tx.Where("organization_id = ? and id = ?", organizationId, policyId).Delete(&model.Policy{}).Error; err != nil { + return err + } + + return nil + }) +} + +func (r *PolicyRepository) UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusters []model.Cluster) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var policy model.Policy + res := tx.Preload("TargetClusters").Where("organization_id = ? and id = ?", organizationId, policyId).First(&policy) + + if res.Error != nil { + return res.Error + } + + if len(policy.TargetClusterIds) == 0 && len(currentClusterIds) != 0 { + return errors.New("concurrent modification of target clusters") + } + + actualCurrentClusterIdSet := mapset.NewSet(policy.TargetClusterIds...) + knownCurrentClusterIdSet := mapset.NewSet(currentClusterIds...) + + if !actualCurrentClusterIdSet.Equal(knownCurrentClusterIdSet) { + return errors.New("concurrent modification of target clusters") + } + + err = tx.Model(&policy).Limit(1). + Association("TargetClusters").Replace(targetClusters) + + if err != nil { + return err + } + + // return nil will commit the whole transaction + return nil + }) +} + +func (r *PolicyRepository) SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) { + return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + if len(mandatoryPolicyIds) > 0 { + if err = tx.Model(&model.Policy{}). + Where("organization_id = ?", organizationId). + Where("id in ?", mandatoryPolicyIds). + Update("mandatory", true).Error; err != nil { + return err + } + } + + if len(nonMandatoryPolicyIds) > 0 { + if err = tx.Model(&model.Policy{}). + Where("organization_id = ?", organizationId). + Where("id in ?", nonMandatoryPolicyIds). + Update("mandatory", false).Error; err != nil { + return err + } + } + + return nil + }) +} + +func (r *PolicyRepository) GetUsageCountByTemplateId(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (usageCounts []model.UsageCount, err error) { + // 다음과 같은 쿼리, organization_id 가 nil인 경우는 and organization_id = '...'를 조합하지 않음 + // select organizations.id, organizations.name, count(organizations.id) from policies join organizations + // on policies.organization_id = organizations.id + // where policies.template_id='7f8a9f78-1771-43d4-aa4a-c395b43ebdd6' + // and organization_id = 'ozvnzr3oz' + // group by organizations.id + + query := r.db.WithContext(ctx).Model(&model.Policy{}). + Select("organizations.id as organization_id", "organizations.name as organization_name", "count(organizations.id) as usage_count"). + Joins("join organizations on policies.organization_id = organizations.id"). + Where("template_id = ?", policyTemplateId) + + if organizationId != nil { + query = query.Where("organization_id = ?", organizationId) + } + + err = query.Group("organizations.id").Scan(&usageCounts).Error + + if err != nil { + return nil, err + } + + return +} + +func (r *PolicyRepository) CountPolicyByEnforcementAction(ctx context.Context, organizationId string) (policyCount []model.PolicyCount, err error) { + + err = r.db.WithContext(ctx).Model(&model.Policy{}). + Select("enforcement_action", "count(enforcement_action) as count"). + Where("organization_id = ?", organizationId). + Group("enforcement_action").Scan(&policyCount).Error + + if err != nil { + return nil, err + } + + return +} + +func (r *PolicyRepository) AddPoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policies []model.Policy) (err error) { + var cluster model.Cluster + cluster.ID = clusterId + + err = r.db.WithContext(ctx).Model(&cluster). + Association("Policies").Append(policies) + + return err +} + +func (r *PolicyRepository) UpdatePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policies []model.Policy) (err error) { + var cluster model.Cluster + cluster.ID = clusterId + + err = r.db.WithContext(ctx).Model(&cluster). + Association("Policies").Replace(policies) + + return err +} + +func (r *PolicyRepository) DeletePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) { + return r.db.WithContext(ctx). + Where("cluster_id = ?", clusterId). + Where("policy_id in ?", policyIds). + Delete(&model.PolicyTargetCluster{}).Error +} + +func (r *PolicyRepository) GetPolicyIDsByClusterID(ctx context.Context, clusterId domain.ClusterId) (*[]uuid.UUID, error) { + var policyTargetClusters []model.PolicyTargetCluster + + err := r.db.WithContext(ctx). + Where("cluster_id = ?", clusterId). + Find(&policyTargetClusters).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policices for clusterId '%v'", clusterId) + return nil, nil + } else { + log.Error(ctx, err) + return nil, err + } + } + + result := make([]uuid.UUID, len(policyTargetClusters)) + + for i, policyTargetCluster := range policyTargetClusters { + result[i] = policyTargetCluster.PolicyId + } + + return &result, nil +} diff --git a/internal/repository/project.go b/internal/repository/project.go new file mode 100644 index 00000000..d1649eb7 --- /dev/null +++ b/internal/repository/project.go @@ -0,0 +1,661 @@ +package repository + +import ( + "context" + "database/sql" + + "github.com/google/uuid" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type IProjectRepository interface { + CreateProject(ctx context.Context, p *model.Project) (string, error) + GetProjects(ctx context.Context, organizationId string, userId uuid.UUID, projectName string, pg *pagination.Pagination) ([]domain.ProjectResponse, error) + GetProjectsByUserId(ctx context.Context, organizationId string, userId uuid.UUID, projectName string, pg *pagination.Pagination) ([]domain.ProjectResponse, error) + GetAllProjects(ctx context.Context, organizationId string, projectName string, pg *pagination.Pagination) (pr []domain.ProjectResponse, err error) + GetProjectById(ctx context.Context, organizationId string, projectId string) (*model.Project, error) + GetProjectByIdAndLeader(ctx context.Context, organizationId string, projectId string) (*model.Project, error) + GetProjectByName(ctx context.Context, organizationId string, projectName string) (*model.Project, error) + UpdateProject(ctx context.Context, p *model.Project) error + GetAllProjectRoles(ctx context.Context) ([]model.ProjectRole, error) + GetProjectRoleByName(ctx context.Context, name string) (*model.ProjectRole, error) + GetProjectRoleById(ctx context.Context, id string) (*model.ProjectRole, error) + AddProjectMember(context.Context, *model.ProjectMember) (string, error) + GetProjectMembersByProjectId(ctx context.Context, projectId string, pg *pagination.Pagination) ([]model.ProjectMember, error) + GetProjectMembersByProjectIdAndRoleName(ctx context.Context, projectId string, memberRole string, pg *pagination.Pagination) ([]model.ProjectMember, error) + GetProjectMemberCountByProjectId(ctx context.Context, projectId string) (*domain.GetProjectMemberCountResponse, error) + GetProjectMemberById(ctx context.Context, projectMemberId string) (*model.ProjectMember, error) + GetProjectMemberByUserId(ctx context.Context, projectId string, projectUserId string) (pm *model.ProjectMember, err error) + RemoveProjectMember(ctx context.Context, projectMemberId string) error + UpdateProjectMemberRole(ctx context.Context, pm *model.ProjectMember) error + CreateProjectNamespace(ctx context.Context, organizationId string, pn *model.ProjectNamespace) error + GetProjectNamespaceByName(ctx context.Context, organizationId string, projectId string, stackId string, projectNamespace string) (*model.ProjectNamespace, error) + GetProjectNamespaces(ctx context.Context, organizationId string, projectId string, pg *pagination.Pagination) ([]model.ProjectNamespace, error) + GetProjectNamespaceByPrimaryKey(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) (*model.ProjectNamespace, error) + UpdateProjectNamespace(ctx context.Context, pn *model.ProjectNamespace) error + DeleteProjectNamespace(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) error + GetAppCountByProjectId(ctx context.Context, organizationId string, projectId string) (int, error) + GetAppCountByNamespace(ctx context.Context, organizationId string, projectId string, namespace string) (int, error) +} + +type ProjectRepository struct { + db *gorm.DB +} + +func NewProjectRepository(db *gorm.DB) IProjectRepository { + return &ProjectRepository{ + db: db, + } +} + +func (r *ProjectRepository) CreateProject(ctx context.Context, p *model.Project) (string, error) { + p.ID = uuid.New().String() + res := r.db.WithContext(ctx).Create(&p) + if res.Error != nil { + return "", res.Error + } + + return p.ID, nil +} + +func (r *ProjectRepository) GetProjects(ctx context.Context, organizationId string, userId uuid.UUID, projectName string, pg *pagination.Pagination) (pr []domain.ProjectResponse, err error) { + res := r.db.WithContext(ctx).Raw(""+ + "select id, organization_id, name, description, created_at, is_my_project, project_role_id, project_role_name, namespace_count, app_count, member_count, "+ + " project_leader_id, project_leader_name"+ + " from ( "+ + "select distinct p.id as id, p.organization_id as organization_id, p.name as name, p.description as description, p.created_at as created_at, "+ + " true as is_my_project, pm.project_role_id as project_role_id, pm.pr_name as project_role_name, "+ + " pn.count as namespace_count, asa.count as app_count, pm_count.count as member_count, "+ + " pm_leader.project_leader_id as project_leader_id, pm_leader.project_leader_name as project_leader_name "+ + " from projects as p "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_user_id, pm.project_role_id as project_role_id, "+ + " pm.created_at as created_at, pm.is_project_leader as is_project_leader, "+ + " pr.name as pr_name "+ + " from project_members as pm "+ + " left join project_roles as pr on pr.id = pm.project_role_id "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.project_user_id = @userId) as pm on p.id = pm.project_id "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_leader_id, users.name as project_leader_name "+ + " from project_members as pm "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.is_project_leader = true) as pm_leader on p.id = pm_leader.project_id "+ + " left join "+ + " (select p.id as project_id, count(pn.stack_id || pn.project_id) as count "+ + " from project_namespaces as pn "+ + " left join projects as p on pn.project_id = p.id "+ + " left join project_members as pm on pn.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pn on p.id = pn.project_id "+ + " left join "+ + " (select p.id as project_id, count(asa.id) as count "+ + " from app_serve_apps as asa "+ + " left join projects as p on asa.project_id = p.id "+ + " left join project_members as pm on asa.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as asa on p.id = asa.project_id "+ + " left join "+ + " (select p.id as project_id, count(pm.id) as count "+ + " from project_members as pm "+ + " left join projects as p on pm.project_id = p.id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pm_count on p.id = pm_count.project_id "+ + " where p.id = pm.project_id "+ + " and p.id = pm_leader.project_id "+ + " and p.organization_id = @organizationId "+ + " and p.name like '%"+projectName+"%' "+ + "union "+ + "select distinct p.id as id, p.organization_id as organization_id, p.name as name, p.description as description, p.created_at as created_at, "+ + " false as is_my_project, '' as project_role_id, '' as project_role_name, "+ + " pn.count as namespace_count, asa.count as app_count, pm_count.count as member_count, "+ + " pm_leader.project_leader_id as project_leader_id, pm_leader.project_leader_name as project_leader_name "+ + " from projects as p "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_user_id, pm.project_role_id as project_role_id, "+ + " pm.created_at as created_at, pm.is_project_leader as is_project_leader, "+ + " pr.name as pr_name "+ + " from project_members as pm "+ + " left join project_roles as pr on pr.id = pm.project_role_id "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.project_user_id <> @userId) as pm on p.id = pm.project_id "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_leader_id, users.name as project_leader_name "+ + " from project_members as pm "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.is_project_leader = true) as pm_leader on p.id = pm_leader.project_id "+ + " left join "+ + " (select p.id as project_id, count(pn.stack_id || pn.project_id) as count "+ + " from project_namespaces as pn "+ + " left join projects as p on pn.project_id = p.id "+ + " left join project_members as pm on pn.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pn on p.id = pn.project_id "+ + " left join "+ + " (select p.id as project_id, count(asa.id) as count "+ + " from app_serve_apps as asa "+ + " left join projects as p on asa.project_id = p.id "+ + " left join project_members as pm on asa.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as asa on p.id = asa.project_id "+ + " left join "+ + " (select p.id as project_id, count(pm.id) as count "+ + " from project_members as pm "+ + " left join projects as p on pm.project_id = p.id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pm_count on p.id = pm_count.project_id"+ + " where p.id = pm.project_id "+ + " and p.id = pm_leader.project_id "+ + " and p.organization_id = @organizationId "+ + " and p.name like '%"+projectName+"%' "+ + " and p.id not in (select projects.id "+ + " from projects "+ + " left join project_members on project_members.project_id = projects.id "+ + " where project_members.project_user_id = @userId) "+ + ") as union_project "+ + "order by is_my_project desc ", + sql.Named("organizationId", organizationId), sql.Named("userId", userId)). + Scan(&pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project") + } + } + return pr, nil +} + +func (r *ProjectRepository) GetProjectsByUserId(ctx context.Context, organizationId string, userId uuid.UUID, projectName string, pg *pagination.Pagination) (pr []domain.ProjectResponse, err error) { + res := r.db.WithContext(ctx).Raw(""+ + "select distinct p.id as id, p.organization_id as organization_id, p.name as name, p.description as description, p.created_at as created_at, "+ + " true as is_my_project, pm.project_role_id as project_role_id, pm.pr_name as project_role_name, "+ + " pn.count as namespace_count, asa.count as app_count, pm_count.count as member_count, "+ + " pm_leader.project_leader_id as project_leader_id, pm_leader.project_leader_name as project_leader_name "+ + " from projects as p "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_user_id, pm.project_role_id as project_role_id, "+ + " pm.created_at as created_at, pm.is_project_leader as is_project_leader, "+ + " pr.name as pr_name "+ + " from project_members as pm "+ + " left join project_roles as pr on pr.id = pm.project_role_id "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.project_user_id = @userId) as pm on p.id = pm.project_id "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_leader_id, users.name as project_leader_name "+ + " from project_members as pm "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.is_project_leader = true) as pm_leader on p.id = pm_leader.project_id "+ + " left join "+ + " (select p.id as project_id, count(pn.stack_id || pn.project_id) as count "+ + " from project_namespaces as pn "+ + " left join projects as p on pn.project_id = p.id "+ + " left join project_members as pm on pn.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pn on p.id = pn.project_id "+ + " left join "+ + " (select p.id as project_id, count(asa.id) as count "+ + " from app_serve_apps as asa "+ + " left join projects as p on asa.project_id = p.id "+ + " left join project_members as pm on asa.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as asa on p.id = asa.project_id "+ + " left join "+ + " (select p.id as project_id, count(pm.id) as count "+ + " from project_members as pm "+ + " left join projects as p on pm.project_id = p.id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pm_count on p.id = pm_count.project_id "+ + " where p.id = pm.project_id "+ + " and p.id = pm_leader.project_id "+ + " and p.organization_id = @organizationId "+ + " and p.name like '%"+projectName+"%' ", + sql.Named("organizationId", organizationId), sql.Named("userId", userId)). + Scan(&pr) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pr, nil +} + +func (r *ProjectRepository) GetAllProjects(ctx context.Context, organizationId string, projectName string, pg *pagination.Pagination) (pr []domain.ProjectResponse, err error) { + res := r.db.WithContext(ctx).Raw(""+ + "select distinct p.id as id, p.organization_id as organization_id, p.name as name, p.description as description, p.created_at as created_at, "+ + " false as is_my_project, pm.project_role_id as project_role_id, pm.pr_name as project_role_name, "+ + " pn.count as namespace_count, asa.count as app_count, pm_count.count as member_count, "+ + " pm_leader.project_leader_id as project_leader_id, pm_leader.project_leader_name as project_leader_name "+ + " from projects as p "+ + " left join "+ + " (select distinct pm.project_id as project_id, '' as project_user_id, '' as project_role_id, "+ + " pm.created_at as created_at, pm.is_project_leader as is_project_leader, "+ + " '' as pr_name "+ + " from project_members as pm "+ + " left join project_roles as pr on pr.id = pm.project_role_id "+ + " left join users on users.id = pm.project_user_id) as pm on p.id = pm.project_id "+ + " left join "+ + " (select pm.project_id as project_id, pm.project_user_id as project_leader_id, users.name as project_leader_name "+ + " from project_members as pm "+ + " left join users on users.id = pm.project_user_id "+ + " where pm.is_project_leader = true) as pm_leader on p.id = pm_leader.project_id "+ + " left join "+ + " (select p.id as project_id, count(pn.stack_id || pn.project_id) as count "+ + " from project_namespaces as pn "+ + " left join projects as p on pn.project_id = p.id "+ + " left join project_members as pm on pn.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pn on p.id = pn.project_id "+ + " left join "+ + " (select p.id as project_id, count(asa.id) as count "+ + " from app_serve_apps as asa "+ + " left join projects as p on asa.project_id = p.id "+ + " left join project_members as pm on asa.project_id = pm.project_id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as asa on p.id = asa.project_id "+ + " left join "+ + " (select p.id as project_id, count(pm.id) as count "+ + " from project_members as pm "+ + " left join projects as p on pm.project_id = p.id "+ + " where p.organization_id = @organizationId "+ + " group by p.id) as pm_count on p.id = pm_count.project_id "+ + " where p.id = pm.project_id "+ + " and p.id = pm_leader.project_id "+ + " and p.organization_id = @organizationId "+ + " and p.name like '%"+projectName+"%' ", + sql.Named("organizationId", organizationId)). + Scan(&pr) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pr, nil +} + +func (r *ProjectRepository) GetProjectById(ctx context.Context, organizationId string, projectId string) (p *model.Project, err error) { + res := r.db.WithContext(ctx).Limit(1).Where("organization_id = ? and id = ?", organizationId, projectId).First(&p) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return p, nil +} + +func (r *ProjectRepository) GetProjectByIdAndLeader(ctx context.Context, organizationId string, projectId string) (p *model.Project, err error) { + res := r.db.WithContext(ctx).Limit(1). + Preload("ProjectMembers", "is_project_leader = ?", true). + Preload("ProjectMembers.ProjectRole"). + Preload("ProjectMembers.ProjectUser"). + First(&p, "organization_id = ? and id = ?", organizationId, projectId) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return p, nil +} + +func (r *ProjectRepository) GetProjectByName(ctx context.Context, organizationId string, projectName string) (p *model.Project, err error) { + res := r.db.WithContext(ctx).Limit(1). + Where("organization_id = ? and name = ?", organizationId, projectName). + First(&p) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found project name") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return p, nil +} + +func (r *ProjectRepository) UpdateProject(ctx context.Context, p *model.Project) error { + res := r.db.WithContext(ctx).Model(&p).Updates(model.Project{Name: p.Name, Description: p.Description, UpdatedAt: p.UpdatedAt}) + if res.Error != nil { + return res.Error + } + + return nil +} + +func (r *ProjectRepository) GetProjectRoleById(ctx context.Context, id string) (*model.ProjectRole, error) { + var pr = &model.ProjectRole{ID: id} + res := r.db.WithContext(ctx).First(pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project role") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pr, nil +} + +func (r *ProjectRepository) GetAllProjectRoles(ctx context.Context) (prs []model.ProjectRole, err error) { + res := r.db.WithContext(ctx).Find(&prs) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project roles") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return prs, nil +} + +func (r *ProjectRepository) GetProjectRoleByName(ctx context.Context, name string) (pr *model.ProjectRole, err error) { + res := r.db.WithContext(ctx).Where("name = ?", name).First(&pr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project roles") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pr, nil +} + +func (r *ProjectRepository) AddProjectMember(ctx context.Context, pm *model.ProjectMember) (string, error) { + pm.ID = uuid.New().String() + res := r.db.WithContext(ctx).Create(&pm) + if res.Error != nil { + return "", res.Error + } + + return pm.ID, nil +} + +func (r *ProjectRepository) GetProjectMembersByProjectId(ctx context.Context, projectId string, pg *pagination.Pagination) (pms []model.ProjectMember, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + _, res := pg.Fetch(r.db.WithContext(ctx).Joins("ProjectUser"). + Joins("ProjectRole"). + Where("project_members.project_id = ?", projectId). + Order("project_members.created_at ASC"), &pms) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project member") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pms, nil +} + +func (r *ProjectRepository) GetProjectMembersByProjectIdAndRoleName(ctx context.Context, projectId string, memberRole string, pg *pagination.Pagination) (pms []model.ProjectMember, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + _, res := pg.Fetch(r.db.WithContext(ctx).Joins("ProjectUser"). + InnerJoins("ProjectRole", r.db.Where(&model.ProjectRole{Name: memberRole})). + Order("project_members.created_at ASC"). + Where("project_members.project_id = ?", projectId), &pms) + + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project member") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pms, nil +} + +func (r *ProjectRepository) GetProjectMemberCountByProjectId(ctx context.Context, projectId string) (pmcr *domain.GetProjectMemberCountResponse, err error) { + res := r.db.WithContext(ctx).Raw(""+ + "select (plc.count + pmc.count + pvc.count) as project_member_all_count,"+ + " plc.count as project_leader_count,"+ + " pmc.count as project_member_count,"+ + " pvc.count as project_viewer_count"+ + " from (select count(project_members.id) as count"+ + " from project_members"+ + " left join project_roles on project_roles.id = project_members.project_role_id"+ + " where project_members.project_id = @projectId"+ + " and project_roles.name = 'project-leader') as plc,"+ + " (select count(project_members.id) as count"+ + " from project_members"+ + " left join project_roles on project_roles.id = project_members.project_role_id"+ + " where project_members.project_id = @projectId"+ + " and project_roles.name = 'project-member') as pmc,"+ + " (select count(project_members.id) as count"+ + " from project_members"+ + " left join project_roles on project_roles.id = project_members.project_role_id"+ + " where project_members.project_id = @projectId"+ + " and project_roles.name = 'project-viewer') as pvc", sql.Named("projectId", projectId)). + Scan(&pmcr) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project member count") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pmcr, nil +} + +func (r *ProjectRepository) GetProjectMemberById(ctx context.Context, projectMemberId string) (pm *model.ProjectMember, err error) { + res := r.db.WithContext(ctx).Preload("ProjectUser"). + Joins("ProjectRole").Where("project_members.id = ?", projectMemberId).First(&pm) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project member") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pm, nil +} + +func (r *ProjectRepository) GetProjectMemberByUserId(ctx context.Context, projectId string, projectUserId string) (pm *model.ProjectMember, err error) { + res := r.db.WithContext(ctx).Preload("ProjectUser"). + Joins("ProjectRole").Where("project_id = ? and project_user_id = ?", projectId, projectUserId).First(&pm) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Cannot find project member") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pm, nil +} + +func (r *ProjectRepository) RemoveProjectMember(ctx context.Context, projectMemberId string) error { + res := r.db.WithContext(ctx).Delete(&model.ProjectMember{ID: projectMemberId}) + if res.Error != nil { + return res.Error + } + + return nil +} + +//func (r *ProjectRepository) UpdateProjectMemberRole(projectMemberId string, projectRoleId string) error { +// res := r.db.Model(&model.ProjectMember{ID: projectMemberId}).Update("project_role_id", projectRoleId) +// if res.Error != nil { +// return res.Error +// } +// +// return nil +//} + +func (r *ProjectRepository) UpdateProjectMemberRole(ctx context.Context, pm *model.ProjectMember) error { + res := r.db.WithContext(ctx).Model(&pm).Updates( + model.ProjectMember{ + ProjectRoleId: pm.ProjectRoleId, + IsProjectLeader: pm.IsProjectLeader, + UpdatedAt: pm.UpdatedAt, + }) + if res.Error != nil { + return res.Error + } + + return nil +} + +func (r *ProjectRepository) CreateProjectNamespace(ctx context.Context, organizationId string, pn *model.ProjectNamespace) error { + res := r.db.WithContext(ctx).Create(&pn) + if res.Error != nil { + return res.Error + } + + //return pn.ID, nil + return nil +} + +func (r *ProjectRepository) GetProjectNamespaceByName(ctx context.Context, organizationId string, projectId string, stackId string, + projectNamespace string) (pn *model.ProjectNamespace, err error) { + res := r.db.WithContext(ctx).Limit(1). + Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + First(&pn) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found project namespace") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pn, nil +} + +func (r *ProjectRepository) GetProjectNamespaces(ctx context.Context, organizationId string, projectId string, pg *pagination.Pagination) (pns []model.ProjectNamespace, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + _, res := pg.Fetch(r.db.WithContext(ctx).Where("project_id = ?", projectId). + Preload("Stack"), &pns) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found project namespaces") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pns, nil +} + +func (r *ProjectRepository) GetProjectNamespaceByPrimaryKey(ctx context.Context, organizationId string, projectId string, + projectNamespace string, stackId string) (pn *model.ProjectNamespace, err error) { + res := r.db.WithContext(ctx).Limit(1). + Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + Preload("Stack"). + First(&pn) + if res.Error != nil { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Info(ctx, "Not found project namespace") + return nil, nil + } else { + log.Error(ctx, res.Error) + return nil, res.Error + } + } + + return pn, nil +} + +func (r *ProjectRepository) UpdateProjectNamespace(ctx context.Context, pn *model.ProjectNamespace) error { + res := r.db.WithContext(ctx).Model(&pn).Select("Description").Updates(model.ProjectNamespace{Description: pn.Description, UpdatedAt: pn.UpdatedAt}) + if res.Error != nil { + return res.Error + } + + return nil +} + +func (r *ProjectRepository) DeleteProjectNamespace(ctx context.Context, organizationId string, projectId string, projectNamespace string, + stackId string) error { + res := r.db.WithContext(ctx).Where("stack_id = ? and namespace = ? and project_id = ?", stackId, projectNamespace, projectId). + Delete(&model.ProjectNamespace{StackId: stackId, Namespace: projectNamespace}) + if res.Error != nil { + return res.Error + } + + return nil +} + +func (r *ProjectRepository) GetAppCountByProjectId(ctx context.Context, organizationId string, projectId string) (appCount int, err error) { + res := r.db.WithContext(ctx).Select("count(*) as app_count"). + Table("app_serve_apps"). + Where("organization_id = ? and project_Id = ?", organizationId, projectId). + Find(&appCount) + if res.Error != nil { + log.Error(ctx, res.Error) + return 0, res.Error + } + + return appCount, nil +} + +func (r *ProjectRepository) GetAppCountByNamespace(ctx context.Context, organizationId string, projectId string, namespace string) (appCount int, err error) { + res := r.db.WithContext(ctx).Select("count(*) as app_count"). + Table("app_serve_apps"). + Where("organization_id = ? and project_Id = ? and namespace = ?", organizationId, projectId, namespace). + Find(&appCount) + if res.Error != nil { + log.Error(ctx, res.Error) + return 0, res.Error + } + + return appCount, nil +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 0503a53d..bee291a5 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -1,67 +1,29 @@ package repository import ( - "fmt" - "strings" - "gorm.io/gorm" - - "github.com/openinfradev/tks-api/internal/pagination" ) type FilterFunc func(user *gorm.DB) *gorm.DB type Repository struct { - Auth IAuthRepository - User IUserRepository - Cluster IClusterRepository - Organization IOrganizationRepository - AppGroup IAppGroupRepository - AppServeApp IAppServeAppRepository - CloudAccount ICloudAccountRepository - StackTemplate IStackTemplateRepository - Alert IAlertRepository -} - -func CombinedGormFilter(table string, filters []pagination.Filter, combinedFilter pagination.CombinedFilter) FilterFunc { - return func(db *gorm.DB) *gorm.DB { - // and query - for _, filter := range filters { - if len(filter.Values) > 1 { - inQuery := fmt.Sprintf("LOWER(%s.%s::text) in (", table, filter.Column) - for _, val := range filter.Values { - inQuery = inQuery + fmt.Sprintf("LOWER($$%s$$),", val) - } - inQuery = inQuery[:len(inQuery)-1] + ")" - db = db.Where(inQuery) - } else { - if len(filter.Values[0]) > 0 { - if strings.Contains(filter.Values[0], "%") { - filterVal := strings.Replace(filter.Values[0], "%", "`%", -1) - db = db.Where(fmt.Sprintf("LOWER(%s.%s::text) like LOWER($$%%%s%%$$) escape '`'", table, filter.Column, filterVal)) - } else { - db = db.Where(fmt.Sprintf("LOWER(%s.%s::text) like LOWER($$%%%s%%$$)", table, filter.Column, filter.Values[0])) - } - } - } - } - - // or query - // id = '123' or description = '345' - if len(combinedFilter.Columns) > 0 { - orQuery := "" - for _, column := range combinedFilter.Columns { - if strings.Contains(combinedFilter.Value, "%") { - filterVal := strings.Replace(combinedFilter.Value, "%", "`%", -1) - orQuery = orQuery + fmt.Sprintf("LOWER(%s.%s::text) like LOWER($$%%%s%%$$) escape '`' OR ", table, column, filterVal) - } else { - orQuery = orQuery + fmt.Sprintf("LOWER(%s.%s::text) like LOWER($$%%%s%%$$) OR ", table, column, combinedFilter.Value) - } - } - orQuery = orQuery[:len(orQuery)-3] - db = db.Where(orQuery) - } - - return db - } + Auth IAuthRepository + User IUserRepository + Cluster IClusterRepository + Organization IOrganizationRepository + AppGroup IAppGroupRepository + AppServeApp IAppServeAppRepository + CloudAccount ICloudAccountRepository + StackTemplate IStackTemplateRepository + Role IRoleRepository + Permission IPermissionRepository + Endpoint IEndpointRepository + Project IProjectRepository + Audit IAuditRepository + PolicyTemplate IPolicyTemplateRepository + Policy IPolicyRepository + SystemNotification ISystemNotificationRepository + SystemNotificationTemplate ISystemNotificationTemplateRepository + SystemNotificationRule ISystemNotificationRuleRepository + Dashboard IDashboardRepository } diff --git a/internal/repository/role.go b/internal/repository/role.go new file mode 100644 index 00000000..a6617686 --- /dev/null +++ b/internal/repository/role.go @@ -0,0 +1,106 @@ +package repository + +import ( + "context" + "fmt" + "github.com/pkg/errors" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +type IRoleRepository interface { + Create(ctx context.Context, roleObj *model.Role) (string, error) + ListTksRoles(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]*model.Role, error) + GetTksRole(ctx context.Context, organizationId string, id string) (*model.Role, error) + GetTksRoleByRoleName(ctx context.Context, organizationId string, roleName string) (*model.Role, error) + Delete(ctx context.Context, id string) error + Update(ctx context.Context, roleObj *model.Role) error +} + +type RoleRepository struct { + db *gorm.DB +} + +func (r RoleRepository) GetTksRoleByRoleName(ctx context.Context, oragnizationId string, roleName string) (*model.Role, error) { + var role model.Role + if err := r.db.WithContext(ctx).First(&role, "organization_id = ? AND name = ?", oragnizationId, roleName).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return &role, nil +} + +func (r RoleRepository) Create(ctx context.Context, roleObj *model.Role) (string, error) { + if roleObj == nil { + return "", fmt.Errorf("roleObj is nil") + } + if roleObj.ID == "" { + roleObj.ID = uuid.New().String() + } + if err := r.db.WithContext(ctx).Create(roleObj).Error; err != nil { + return "", err + } + + return roleObj.ID, nil +} + +func (r RoleRepository) ListTksRoles(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]*model.Role, error) { + var roles []*model.Role + + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations).Model(&model.Role{}).Where("organization_id = ?", organizationId), &roles) + if res.Error != nil { + return nil, res.Error + } + return roles, nil +} + +func (r RoleRepository) GetTksRole(ctx context.Context, organizationId string, id string) (*model.Role, error) { + var role model.Role + if err := r.db.WithContext(ctx).First(&role, "id = ?", id).Error; err != nil { + return nil, err + } + + return &role, nil +} + +func (r RoleRepository) Update(ctx context.Context, roleObj *model.Role) error { + if roleObj == nil { + return fmt.Errorf("roleObj is nil") + } + + err := r.db.WithContext(ctx).Model(&model.Role{}).Where("id = ?", roleObj.ID).Updates(model.Role{ + Name: roleObj.Name, + Description: roleObj.Description, + }).Error + + if err != nil { + return err + } + + return nil +} + +func (r RoleRepository) Delete(ctx context.Context, id string) error { + if err := r.db.WithContext(ctx).Delete(&model.Role{}, "id = ?", id).Error; err != nil { + return err + } + + return nil +} + +func NewRoleRepository(db *gorm.DB) IRoleRepository { + return &RoleRepository{ + db: db, + } +} diff --git a/internal/repository/stack-template.go b/internal/repository/stack-template.go index 7f8da72c..143b345e 100644 --- a/internal/repository/stack-template.go +++ b/internal/repository/stack-template.go @@ -1,27 +1,26 @@ package repository import ( - "fmt" - "math" + "context" "github.com/google/uuid" - "gorm.io/datatypes" "gorm.io/gorm" "gorm.io/gorm/clause" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/serializer" - "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/log" ) // Interfaces type IStackTemplateRepository interface { - Get(stackTemplateId uuid.UUID) (domain.StackTemplate, error) - Fetch(pg *pagination.Pagination) ([]domain.StackTemplate, error) - Create(dto domain.StackTemplate) (stackTemplateId uuid.UUID, err error) - Update(dto domain.StackTemplate) (err error) - Delete(dto domain.StackTemplate) (err error) + Get(ctx context.Context, stackTemplateId uuid.UUID) (model.StackTemplate, error) + GetByName(ctx context.Context, name string) (model.StackTemplate, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.StackTemplate, error) + FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.StackTemplate, err error) + Create(ctx context.Context, dto model.StackTemplate) (stackTemplateId uuid.UUID, err error) + Update(ctx context.Context, dto model.StackTemplate) (err error) + Delete(ctx context.Context, dto model.StackTemplate) (err error) + UpdateOrganizations(ctx context.Context, stackTemplateId uuid.UUID, organizationIds []model.Organization) (err error) } type StackTemplateRepository struct { @@ -34,114 +33,98 @@ func NewStackTemplateRepository(db *gorm.DB) IStackTemplateRepository { } } -// Models -type StackTemplate struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId"` - Name string `gorm:"index"` - Description string `gorm:"index"` - Template string - TemplateType string - Version string - CloudService string - Platform string - KubeVersion string - KubeType string - Services datatypes.JSON - CreatorId *uuid.UUID `gorm:"type:uuid"` - Creator User `gorm:"foreignKey:CreatorId"` - UpdatorId *uuid.UUID `gorm:"type:uuid"` - Updator User `gorm:"foreignKey:UpdatorId"` -} - -func (c *StackTemplate) BeforeCreate(tx *gorm.DB) (err error) { - c.ID = uuid.New() - return nil +// Logics +func (r *StackTemplateRepository) Get(ctx context.Context, stackTemplateId uuid.UUID) (out model.StackTemplate, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "id = ?", stackTemplateId) + if res.Error != nil { + return model.StackTemplate{}, res.Error + } + return } -// Logics -func (r *StackTemplateRepository) Get(stackTemplateId uuid.UUID) (out domain.StackTemplate, err error) { - var stackTemplate StackTemplate - res := r.db.Preload(clause.Associations).First(&stackTemplate, "id = ?", stackTemplateId) +func (r *StackTemplateRepository) GetByName(ctx context.Context, name string) (out model.StackTemplate, err error) { + res := r.db.WithContext(ctx).First(&out, "name = ?", name) if res.Error != nil { - return domain.StackTemplate{}, res.Error + return out, res.Error } - out = reflectStackTemplate(stackTemplate) return } -// [TODO] organizationId 별로 생성하지 않고, 하나의 stackTemplate 을 모든 organization 에서 재사용한다. ( 5월 한정, 추후 rearchitecture 필요) -func (r *StackTemplateRepository) Fetch(pg *pagination.Pagination) (out []domain.StackTemplate, err error) { - var stackTemplates []StackTemplate +func (r *StackTemplateRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.StackTemplate, err error) { if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("stack_templates", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&StackTemplate{})) - db.Count(&pg.TotalRows) - - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order("kube_type DESC,template_type ASC").Order(orderQuery).Find(&stackTemplates) + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations), &out) if res.Error != nil { return nil, res.Error } + return +} - for _, stackTemplate := range stackTemplates { - out = append(out, reflectStackTemplate(stackTemplate)) +func (r *StackTemplateRepository) FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.StackTemplate, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch( + r.db.WithContext(ctx).Preload(clause.Associations). + Joins("JOIN stack_template_organizations ON stack_template_organizations.stack_template_id = stack_templates.id AND stack_template_organizations.organization_id = ?", organizationId), + &out) + if res.Error != nil { + return nil, res.Error } return } -func (r *StackTemplateRepository) Create(dto domain.StackTemplate) (stackTemplateId uuid.UUID, err error) { - stackTemplate := StackTemplate{ - OrganizationId: dto.OrganizationId, - Name: dto.Name, - Description: dto.Description, - CloudService: dto.CloudService, - Platform: dto.Platform, - Template: dto.Template, - TemplateType: dto.TemplateType, - CreatorId: &dto.CreatorId, - UpdatorId: nil} - res := r.db.Create(&stackTemplate) +func (r *StackTemplateRepository) Create(ctx context.Context, dto model.StackTemplate) (stackTemplateId uuid.UUID, err error) { + dto.ID = uuid.New() + res := r.db.WithContext(ctx).Create(&dto) if res.Error != nil { return uuid.Nil, res.Error } - return stackTemplate.ID, nil + return dto.ID, nil } -func (r *StackTemplateRepository) Update(dto domain.StackTemplate) (err error) { - res := r.db.Model(&StackTemplate{}). +func (r *StackTemplateRepository) Update(ctx context.Context, dto model.StackTemplate) (err error) { + res := r.db.WithContext(ctx).Model(&model.StackTemplate{}). Where("id = ?", dto.ID). Updates(map[string]interface{}{ - "Description": dto.Description, - "UpdatorId": dto.UpdatorId}) + "Template": dto.Template, + "TemplateType": dto.TemplateType, + "Version": dto.Version, + "CloudService": dto.CloudService, + "Platform": dto.Platform, + "KubeVersion": dto.KubeVersion, + "KubeType": dto.KubeType, + "Services": dto.Services, + "Description": dto.Description, + "UpdatorId": dto.UpdatorId, + "Name": dto.Name}) if res.Error != nil { return res.Error } return nil } -func (r *StackTemplateRepository) Delete(dto domain.StackTemplate) (err error) { - res := r.db.Delete(&StackTemplate{}, "id = ?", dto.ID) +func (r *StackTemplateRepository) Delete(ctx context.Context, dto model.StackTemplate) (err error) { + res := r.db.WithContext(ctx).Delete(&model.StackTemplate{}, "id = ?", dto.ID) if res.Error != nil { return res.Error } return nil } -func reflectStackTemplate(stackTemplate StackTemplate) (out domain.StackTemplate) { - if err := serializer.Map(stackTemplate.Model, &out); err != nil { - log.Error(err) +func (r *StackTemplateRepository) UpdateOrganizations(ctx context.Context, stackTemplateId uuid.UUID, organizations []model.Organization) (err error) { + var stackTemplate = model.StackTemplate{} + res := r.db.WithContext(ctx).Preload("Organizations").First(&stackTemplate, "id = ?", stackTemplateId) + if res.Error != nil { + return res.Error } - if err := serializer.Map(stackTemplate, &out); err != nil { - log.Error(err) + err = r.db.WithContext(ctx).Model(&stackTemplate).Association("Organizations").Replace(organizations) + if err != nil { + return err } - out.Services = stackTemplate.Services - return + + return nil } diff --git a/internal/repository/system-notification-rule.go b/internal/repository/system-notification-rule.go new file mode 100644 index 00000000..b298fc30 --- /dev/null +++ b/internal/repository/system-notification-rule.go @@ -0,0 +1,159 @@ +package repository + +import ( + "context" + + "github.com/google/uuid" + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" +) + +// Interfaces +type ISystemNotificationRuleRepository interface { + Get(ctx context.Context, systemNotificationRuleId uuid.UUID) (model.SystemNotificationRule, error) + GetByName(ctx context.Context, name string) (model.SystemNotificationRule, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.SystemNotificationRule, error) + FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotificationRule, err error) + Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRuleId uuid.UUID, err error) + Creates(ctx context.Context, dto []model.SystemNotificationRule) (err error) + Update(ctx context.Context, dto model.SystemNotificationRule) (err error) + UpdateStatus(ctx context.Context, systemNotificationRuleId uuid.UUID, status domain.SystemNotificationRuleStatus) (err error) + Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) +} + +type SystemNotificationRuleRepository struct { + db *gorm.DB +} + +func NewSystemNotificationRuleRepository(db *gorm.DB) ISystemNotificationRuleRepository { + return &SystemNotificationRuleRepository{ + db: db, + } +} + +// Logics +func (r *SystemNotificationRuleRepository) Get(ctx context.Context, systemNotificationRuleId uuid.UUID) (out model.SystemNotificationRule, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "id = ?", systemNotificationRuleId) + if res.Error != nil { + return model.SystemNotificationRule{}, res.Error + } + return +} + +func (r *SystemNotificationRuleRepository) GetByName(ctx context.Context, name string) (out model.SystemNotificationRule, err error) { + res := r.db.WithContext(ctx).First(&out, "name = ?", name) + if res.Error != nil { + return out, res.Error + } + return +} + +func (r *SystemNotificationRuleRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.SystemNotificationRule, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations), &out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRuleRepository) FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotificationRule, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + db := r.db.WithContext(ctx).Preload(clause.Associations).Model(&model.SystemNotificationRule{}). + Where("system_notification_rules.organization_id = ?", organizationId) + + // [TODO] more pretty! + for _, filter := range pg.Filters { + if filter.Relation == "TargetUsers" { + db = db.Joins("join system_notification_rule_users on system_notification_rules.id = system_notification_rule_users.system_notification_rule_id"). + Joins("join users on system_notification_rule_users.user_id = users.id"). + Where("users.name ilike ?", "%"+filter.Values[0]+"%") + break + } + } + + _, res := pg.Fetch(db, &out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRuleRepository) Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRuleId uuid.UUID, err error) { + dto.ID = uuid.New() + res := r.db.WithContext(ctx).Create(&dto) + if res.Error != nil { + return uuid.Nil, res.Error + } + + return dto.ID, nil +} + +func (r *SystemNotificationRuleRepository) Creates(ctx context.Context, rules []model.SystemNotificationRule) (err error) { + res := r.db.WithContext(ctx).Create(&rules) + if res.Error != nil { + return res.Error + } + + return nil +} + +func (r *SystemNotificationRuleRepository) Update(ctx context.Context, dto model.SystemNotificationRule) (err error) { + var m model.SystemNotificationRule + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&m, "id = ?", dto.ID) + if res.Error != nil { + return res.Error + } + + m.Name = dto.Name + m.Description = dto.Description + m.SystemNotificationTemplateId = dto.SystemNotificationTemplateId + m.SystemNotificationCondition = dto.SystemNotificationCondition + m.MessageTitle = dto.MessageTitle + m.MessageContent = dto.MessageContent + m.MessageActionProposal = dto.MessageActionProposal + m.UpdatorId = dto.UpdatorId + + res = r.db.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations: true}).Save(&m) + if res.Error != nil { + return res.Error + } + + err = r.db.WithContext(ctx).Model(&m).Association("TargetUsers").Replace(dto.TargetUsers) + if err != nil { + return err + } + + return nil +} + +func (r *SystemNotificationRuleRepository) Delete(ctx context.Context, dto model.SystemNotificationRule) (err error) { + res := r.db.WithContext(ctx).Delete(&model.SystemNotificationRule{}, "id = ?", dto.ID) + if res.Error != nil { + return res.Error + } + return nil +} + +func (r *SystemNotificationRuleRepository) UpdateStatus(ctx context.Context, systemNotificationRuleId uuid.UUID, status domain.SystemNotificationRuleStatus) error { + res := r.db.WithContext(ctx).Model(&model.SystemNotificationRule{}). + Where("id = ?", systemNotificationRuleId). + Updates(map[string]interface{}{ + "Status": status, + }) + if res.Error != nil { + return res.Error + } + + return nil +} diff --git a/internal/repository/system-notification-template.go b/internal/repository/system-notification-template.go new file mode 100644 index 00000000..4c70884f --- /dev/null +++ b/internal/repository/system-notification-template.go @@ -0,0 +1,134 @@ +package repository + +import ( + "context" + + "github.com/google/uuid" + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/log" +) + +// Interfaces +type ISystemNotificationTemplateRepository interface { + Get(ctx context.Context, systemNotificationTemplateId uuid.UUID) (model.SystemNotificationTemplate, error) + GetByName(ctx context.Context, name string) (model.SystemNotificationTemplate, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.SystemNotificationTemplate, error) + FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotificationTemplate, err error) + Create(ctx context.Context, dto model.SystemNotificationTemplate) (systemNotificationTemplateId uuid.UUID, err error) + Update(ctx context.Context, dto model.SystemNotificationTemplate) (err error) + Delete(ctx context.Context, dto model.SystemNotificationTemplate) (err error) + UpdateOrganizations(ctx context.Context, systemNotificationTemplateId uuid.UUID, organizations []model.Organization) (err error) +} + +type SystemNotificationTemplateRepository struct { + db *gorm.DB +} + +func NewSystemNotificationTemplateRepository(db *gorm.DB) ISystemNotificationTemplateRepository { + return &SystemNotificationTemplateRepository{ + db: db, + } +} + +// Logics +func (r *SystemNotificationTemplateRepository) Get(ctx context.Context, systemNotificationTemplateId uuid.UUID) (out model.SystemNotificationTemplate, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "id = ?", systemNotificationTemplateId) + if res.Error != nil { + return out, res.Error + } + return +} + +func (r *SystemNotificationTemplateRepository) GetByName(ctx context.Context, name string) (out model.SystemNotificationTemplate, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).First(&out, "name = ?", name) + if res.Error != nil { + return out, res.Error + } + return +} + +func (r *SystemNotificationTemplateRepository) Fetch(ctx context.Context, pg *pagination.Pagination) (out []model.SystemNotificationTemplate, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch(r.db.WithContext(ctx).Preload(clause.Associations), &out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationTemplateRepository) FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotificationTemplate, err error) { + if pg == nil { + pg = pagination.NewPagination(nil) + } + + _, res := pg.Fetch( + r.db.WithContext(ctx).Preload(clause.Associations). + Joins("JOIN system_notification_template_organizations ON system_notification_template_organizations.system_notification_template_id = system_notification_templates.id AND system_notification_template_organizations.organization_id = ?", organizationId), + &out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationTemplateRepository) Create(ctx context.Context, dto model.SystemNotificationTemplate) (systemNotificationTemplateId uuid.UUID, err error) { + dto.ID = uuid.New() + res := r.db.WithContext(ctx).Create(&dto) + if res.Error != nil { + return uuid.Nil, res.Error + } + + err = r.db.WithContext(ctx).Model(&dto).Association("MetricParameters").Replace(dto.MetricParameters) + if err != nil { + log.Error(ctx, err) + } + + return dto.ID, nil +} + +func (r *SystemNotificationTemplateRepository) Update(ctx context.Context, dto model.SystemNotificationTemplate) (err error) { + res := r.db.WithContext(ctx).Model(&model.SystemNotificationTemplate{}). + Where("id = ?", dto.ID). + Updates(map[string]interface{}{ + "Name": dto.Name, + "Description": dto.Description, + "MetricQuery": dto.MetricQuery, + }) + if res.Error != nil { + return res.Error + } + + if err = r.db.WithContext(ctx).Model(&dto).Association("MetricParameters").Replace(dto.MetricParameters); err != nil { + return err + } + return nil +} + +func (r *SystemNotificationTemplateRepository) Delete(ctx context.Context, dto model.SystemNotificationTemplate) (err error) { + res := r.db.WithContext(ctx).Delete(&model.SystemNotificationTemplate{}, "id = ?", dto.ID) + if res.Error != nil { + return res.Error + } + return nil +} + +func (r *SystemNotificationTemplateRepository) UpdateOrganizations(ctx context.Context, systemNotificationTemplateId uuid.UUID, organizations []model.Organization) (err error) { + var systemNotificationTemplate = model.SystemNotificationTemplate{} + res := r.db.WithContext(ctx).Preload("Organizations").First(&systemNotificationTemplate, "id = ?", systemNotificationTemplateId) + if res.Error != nil { + return res.Error + } + err = r.db.WithContext(ctx).Model(&systemNotificationTemplate).Association("Organizations").Replace(organizations) + if err != nil { + return err + } + + return nil +} diff --git a/internal/repository/system-notification.go b/internal/repository/system-notification.go new file mode 100644 index 00000000..1f43a560 --- /dev/null +++ b/internal/repository/system-notification.go @@ -0,0 +1,211 @@ +package repository + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" +) + +// Interfaces +type ISystemNotificationRepository interface { + Get(ctx context.Context, systemNotificationId uuid.UUID) (model.SystemNotification, error) + GetByName(ctx context.Context, organizationId string, name string) (model.SystemNotification, error) + FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchPodRestart(ctx context.Context, organizationId string, start time.Time, end time.Time) ([]model.SystemNotification, error) + Create(ctx context.Context, dto model.SystemNotification) (systemNotificationId uuid.UUID, err error) + Update(ctx context.Context, dto model.SystemNotification) (err error) + Delete(ctx context.Context, dto model.SystemNotification) (err error) + CreateSystemNotificationAction(ctx context.Context, dto model.SystemNotificationAction) (systemNotificationActionId uuid.UUID, err error) + UpdateRead(ctx context.Context, systemNotificationId uuid.UUID, user model.User) (err error) +} + +type SystemNotificationRepository struct { + db *gorm.DB +} + +func NewSystemNotificationRepository(db *gorm.DB) ISystemNotificationRepository { + return &SystemNotificationRepository{ + db: db, + } +} + +// Logics +func (r *SystemNotificationRepository) Get(ctx context.Context, systemNotificationId uuid.UUID) (out model.SystemNotification, err error) { + res := r.db.WithContext(ctx).Preload("SystemNotificationActions.Taker").Preload(clause.Associations).First(&out, "id = ?", systemNotificationId) + if res.Error != nil { + return model.SystemNotification{}, res.Error + } + + return +} + +func (r *SystemNotificationRepository) GetByName(ctx context.Context, organizationId string, name string) (out model.SystemNotification, err error) { + res := r.db.WithContext(ctx).Preload("SystemNotificationActions.Taker").Preload(clause.Associations).First(&out, "organization_id = ? AND name = ?", organizationId, name) + if res.Error != nil { + return model.SystemNotification{}, res.Error + } + return +} + +func (r *SystemNotificationRepository) FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return out, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + if pg == nil { + pg = pagination.NewPagination(nil) + } + + db := r.db.WithContext(ctx).Model(&model.SystemNotification{}). + Preload("SystemNotificationActions", func(db *gorm.DB) *gorm.DB { + return db.Order("created_at ASC") + }).Preload("SystemNotificationActions.Taker"). + Preload("Cluster", "status = 2"). + Preload("Organization"). + Joins("join clusters on clusters.id = system_notifications.cluster_id AND clusters.status = 2"). + Joins("left outer join system_notification_rules ON system_notification_rules.id = system_notifications.system_notification_rule_id"). + Joins("left outer join system_notification_rule_users ON system_notification_rule_users.system_notification_rule_id = system_notifications.system_notification_rule_id"). + Where("system_notification_rule_users.user_id is null OR system_notification_rule_users.user_id = ?", userInfo.GetUserId()). + Where("system_notifications.organization_id = ? AND system_notifications.notification_type = 'SYSTEM_NOTIFICATION'", organizationId) + + readFilter := pg.GetFilter("read") + if readFilter != nil { + if readFilter.Values[0] == "true" { + db.Joins("join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()) + } else { + db.Joins("left outer join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()). + Where("system_notification_users.user_id is null") + } + } + + _, res := pg.Fetch(db, &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRepository) FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return out, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + if pg == nil { + pg = pagination.NewPagination(nil) + } + + db := r.db.WithContext(ctx).Model(&model.SystemNotification{}). + Preload("Cluster", "status = 2"). + Preload("Organization"). + Joins("join clusters on clusters.id = system_notifications.cluster_id AND clusters.status = 2"). + Where("system_notifications.organization_id = ? AND system_notifications.notification_type = 'POLICY_NOTIFICATION'", organizationId) + + readFilter := pg.GetFilter("read") + if readFilter != nil { + if readFilter.Values[0] == "true" { + db.Joins("join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()) + } else { + db.Joins("left outer join system_notification_users on system_notification_users.system_notification_id = system_notifications.id AND system_notification_users.user_id = ?", userInfo.GetUserId()). + Where("system_notification_users.user_id is null") + } + } + + _, res := pg.Fetch(db, &out) + + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRepository) FetchPodRestart(ctx context.Context, organizationId string, start time.Time, end time.Time) (out []model.SystemNotification, err error) { + res := r.db.WithContext(ctx).Preload(clause.Associations).Order("created_at DESC"). + Where("organization_id = ? AND name = 'pod-restart-frequently' AND created_at BETWEEN ? AND ?", organizationId, start, end). + Find(&out) + if res.Error != nil { + return nil, res.Error + } + return +} + +func (r *SystemNotificationRepository) Create(ctx context.Context, dto model.SystemNotification) (systemNotificationId uuid.UUID, err error) { + + dto.ID = uuid.New() + dto.Status = domain.SystemNotificationActionStatus_CREATED + res := r.db.WithContext(ctx).Create(&dto) + if res.Error != nil { + return uuid.Nil, res.Error + } + return dto.ID, nil +} + +func (r *SystemNotificationRepository) Update(ctx context.Context, dto model.SystemNotification) (err error) { + res := r.db.WithContext(ctx).Model(&model.SystemNotification{}). + Where("id = ?", dto.ID). + Updates(map[string]interface{}{ + "MessageTitle": dto.MessageTitle, + }) + if res.Error != nil { + return res.Error + } + return nil +} + +func (r *SystemNotificationRepository) Delete(ctx context.Context, dto model.SystemNotification) (err error) { + res := r.db.WithContext(ctx).Delete(&model.SystemNotification{}, "id = ?", dto.ID) + if res.Error != nil { + return res.Error + } + return nil +} + +func (r *SystemNotificationRepository) CreateSystemNotificationAction(ctx context.Context, dto model.SystemNotificationAction) (systemNotificationActionId uuid.UUID, err error) { + systemNotification := model.SystemNotificationAction{ + ID: uuid.New(), + SystemNotificationId: dto.SystemNotificationId, + Content: dto.Content, + Status: dto.Status, + TakerId: dto.TakerId, + } + res := r.db.WithContext(ctx).Create(&systemNotification) + if res.Error != nil { + return uuid.Nil, res.Error + } + res = r.db.WithContext(ctx).Model(&model.SystemNotification{}). + Where("id = ?", dto.SystemNotificationId). + Update("status", dto.Status) + if res.Error != nil { + return uuid.Nil, res.Error + } + + return systemNotification.ID, nil +} + +func (r *SystemNotificationRepository) UpdateRead(ctx context.Context, systemNotificationId uuid.UUID, user model.User) (err error) { + var systemNotification = model.SystemNotification{} + res := r.db.WithContext(ctx).First(&systemNotification, "id = ?", systemNotificationId) + if res.Error != nil { + return res.Error + } + + users := []model.User{user} + err = r.db.WithContext(ctx).Model(&systemNotification).Association("Readers").Append(users) + if err != nil { + return err + } + return nil +} diff --git a/internal/repository/user.go b/internal/repository/user.go index c4c254c3..08d66169 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -1,50 +1,46 @@ package repository import ( - "fmt" - "math" + "context" "time" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" "gorm.io/gorm" + "gorm.io/gorm/clause" ) // Interface type IUserRepository interface { - Create(accountId string, organizationId string, password string, name string) (domain.User, error) - CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, - department string, description string, organizationId string, roleId uuid.UUID) (domain.User, error) - List(filters ...FilterFunc) (out *[]domain.User, err error) - ListWithPagination(pg *pagination.Pagination, organizationId string) (out *[]domain.User, err error) - Get(accountId string, organizationId string) (domain.User, error) - GetByUuid(userId uuid.UUID) (domain.User, error) - UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId uuid.UUID, email string, - department string, description string) (domain.User, error) - UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error - DeleteWithUuid(uuid uuid.UUID) error - Flush(organizationId string) error - - FetchRoles() (out *[]domain.Role, err error) - AssignRole(accountId string, organizationId string, roleName string) error + Create(ctx context.Context, user *model.User) (*model.User, error) + List(ctx context.Context, filters ...FilterFunc) (out *[]model.User, err error) + ListWithPagination(ctx context.Context, pg *pagination.Pagination, organizationId string) (out *[]model.User, err error) + Get(ctx context.Context, accountId string, organizationId string) (model.User, error) + GetByUuid(ctx context.Context, userId uuid.UUID) (model.User, error) + Update(ctx context.Context, user *model.User) (*model.User, error) + UpdatePasswordAt(ctx context.Context, userId uuid.UUID, organizationId string, isTemporary bool) error + DeleteWithUuid(ctx context.Context, uuid uuid.UUID) error + Flush(ctx context.Context, organizationId string) error + + ListUsersByRole(ctx context.Context, organizationId string, roleId string, pg *pagination.Pagination) (*[]model.User, error) + AccountIdFilter(accountId string) FilterFunc OrganizationFilter(organization string) FilterFunc EmailFilter(email string) FilterFunc NameFilter(name string) FilterFunc - AssignRoleWithUuid(uuid uuid.UUID, roleName string) error } type UserRepository struct { db *gorm.DB } -func (r *UserRepository) Flush(organizationId string) error { - res := r.db.Where("organization_id = ?", organizationId).Delete(&User{}) +func (r *UserRepository) Flush(ctx context.Context, organizationId string) error { + res := r.db.WithContext(ctx).Where("organization_id = ?", organizationId).Delete(&model.User{}) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } return nil @@ -56,99 +52,41 @@ func NewUserRepository(db *gorm.DB) IUserRepository { } } -// Models -type User struct { - gorm.Model - - ID uuid.UUID `gorm:"primarykey;type:uuid"` - AccountId string - Name string - Password string - AuthType string `gorm:"authType"` - RoleId uuid.UUID - Role Role `gorm:"foreignKey:RoleId;references:ID"` - OrganizationId string - Organization Organization `gorm:"foreignKey:OrganizationId;references:ID"` - Creator uuid.UUID - Email string - Department string - Description string - - PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` -} - -func (g *User) BeforeCreate(tx *gorm.DB) (err error) { - g.PasswordUpdatedAt = time.Now() - return nil -} - -func (r *UserRepository) Create(accountId string, organizationId string, password string, name string) (domain.User, error) { - newUser := User{ - AccountId: accountId, - Password: password, - OrganizationId: organizationId, - Name: name, - } - res := r.db.Create(&newUser) - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.User{}, res.Error - } - - return r.reflect(newUser), nil -} -func (r *UserRepository) CreateWithUuid(uuid uuid.UUID, accountId string, name string, password string, email string, - department string, description string, organizationId string, roleId uuid.UUID) (domain.User, error) { - - newUser := User{ - ID: uuid, - AccountId: accountId, - Password: password, - Name: name, - Email: email, - Department: department, - Description: description, - OrganizationId: organizationId, - RoleId: roleId, - } - res := r.db.Create(&newUser) +// func (r *UserRepository) CreateWithUuid(ctx context.Context, uuid uuid.UUID, accountId string, name string, email string, +// +// department string, description string, organizationId string, roleId string) (model.User, error) { +func (r *UserRepository) Create(ctx context.Context, user *model.User) (*model.User, error) { + user.PasswordUpdatedAt = time.Now() + //newUser := model.User{ + // ID: uuid, + // AccountId: accountId, + // Name: name, + // Email: email, + // Department: department, + // Description: description, + // OrganizationId: organizationId, + // RoleId: roleId, + // PasswordUpdatedAt: time.Now(), + //} + res := r.db.WithContext(ctx).Create(user) if res.Error != nil { - log.Error(res.Error.Error()) - return domain.User{}, res.Error + log.Error(ctx, res.Error.Error()) + return nil, res.Error } - user, err := r.getUserByAccountId(accountId, organizationId) + resp, err := r.getUserByAccountId(ctx, user.AccountId, user.Organization.ID) if err != nil { - return domain.User{}, err + return nil, err } - return r.reflect(user), nil -} -func (r *UserRepository) AccountIdFilter(accountId string) FilterFunc { - return func(user *gorm.DB) *gorm.DB { - return user.Where("account_id = ?", accountId) - } -} -func (r *UserRepository) OrganizationFilter(organization string) FilterFunc { - return func(user *gorm.DB) *gorm.DB { - return user.Where("organization_id = ?", organization) - } -} -func (r *UserRepository) EmailFilter(email string) FilterFunc { - return func(user *gorm.DB) *gorm.DB { - return user.Where("email = ?", email) - } -} -func (r *UserRepository) NameFilter(name string) FilterFunc { - return func(user *gorm.DB) *gorm.DB { - return user.Where("name = ?", name) - } + return &resp, nil } -func (r *UserRepository) List(filters ...FilterFunc) (*[]domain.User, error) { - var users []User +func (r *UserRepository) List(ctx context.Context, filters ...FilterFunc) (*[]model.User, error) { + var users []model.User var res *gorm.DB + if filters == nil { - res = r.db.Model(&User{}).Preload("Organization").Preload("Role").Find(&users) + res = r.db.WithContext(ctx).Model(&model.User{}).Preload("Organization").Preload("Roles").Find(&users) } else { combinedFilter := func(filters ...FilterFunc) FilterFunc { return func(user *gorm.DB) *gorm.DB { @@ -159,350 +97,224 @@ func (r *UserRepository) List(filters ...FilterFunc) (*[]domain.User, error) { } } cFunc := combinedFilter(filters...) - res = cFunc(r.db.Model(&User{}).Preload("Organization").Preload("Role")).Find(&users) + res = cFunc(r.db.Model(&model.User{}).Preload("Organization").Preload("Roles")).Find(&users) } + if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return nil, res.Error } if res.RowsAffected == 0 { return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } - var out []domain.User - for _, user := range users { - out = append(out, r.reflect(user)) - } + var out []model.User + out = append(out, users...) return &out, nil } -func (r *UserRepository) ListWithPagination(pg *pagination.Pagination, organizationId string) (*[]domain.User, error) { - var users []User +func (r *UserRepository) ListWithPagination(ctx context.Context, pg *pagination.Pagination, organizationId string) (*[]model.User, error) { + var users []model.User if pg == nil { - pg = pagination.NewDefaultPagination() + pg = pagination.NewPagination(nil) } - filterFunc := CombinedGormFilter("users", pg.GetFilters(), pg.CombinedFilter) - db := filterFunc(r.db.Model(&User{}).Where("organization_id = ?", organizationId)) - db.Count(&pg.TotalRows) + db := r.db.WithContext(ctx).Preload(clause.Associations).Model(&model.User{}). + Where("users.organization_id = ?", organizationId) + + // [TODO] more pretty! + for _, filter := range pg.Filters { + if filter.Relation == "Roles" { + db = db.Joins("join user_roles on user_roles.user_id = users.id"). + Joins("join roles on roles.id = user_roles.role_id"). + Where("roles.name ilike ?", "%"+filter.Values[0]+"%") + break + } + } - pg.TotalPages = int(math.Ceil(float64(pg.TotalRows) / float64(pg.Limit))) - orderQuery := fmt.Sprintf("%s %s", pg.SortColumn, pg.SortOrder) - res := db.Preload("Organization").Preload("Role").Offset(pg.GetOffset()).Limit(pg.GetLimit()).Order(orderQuery).Find(&users) + _, res := pg.Fetch(db, &users) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return nil, res.Error } - var out []domain.User - for _, user := range users { - out = append(out, r.reflect(user)) - } + var out []model.User + out = append(out, users...) return &out, nil } -func (r *UserRepository) Get(accountId string, organizationId string) (domain.User, error) { - user, err := r.getUserByAccountId(accountId, organizationId) +func (r *UserRepository) Get(ctx context.Context, accountId string, organizationId string) (model.User, error) { + user, err := r.getUserByAccountId(ctx, accountId, organizationId) if err != nil { - return domain.User{}, err + return model.User{}, err } - return r.reflect(user), nil + return user, nil } -func (r *UserRepository) GetByUuid(userId uuid.UUID) (respUser domain.User, err error) { - user := User{} - res := r.db.Model(&User{}).Preload("Organization").Preload("Role").Find(&user, "id = ?", userId) + +func (r *UserRepository) GetByUuid(ctx context.Context, userId uuid.UUID) (respUser model.User, err error) { + user := model.User{} + res := r.db.WithContext(ctx).Model(&model.User{}).Preload("Organization").Preload("Roles").Find(&user, "id = ?", userId) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.User{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.User{}, res.Error } if res.RowsAffected == 0 { - return domain.User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + return model.User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } - return r.reflect(user), nil -} -func (r *UserRepository) UpdateWithUuid(uuid uuid.UUID, accountId string, name string, password string, roleId uuid.UUID, - email string, department string, description string) (domain.User, error) { - var user User - res := r.db.Model(&User{}).Where("id = ?", uuid).Updates(User{ - AccountId: accountId, - Name: name, - Password: password, - Email: email, - Department: department, - Description: description, - RoleId: roleId, - }) - if res.RowsAffected == 0 || res.Error != nil { - return domain.User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") - } - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return domain.User{}, res.Error - } - res = r.db.Model(&User{}).Preload("Organization").Preload("Role").Where("id = ?", uuid).Find(&user) - if res.Error != nil { - return domain.User{}, res.Error - } - return r.reflect(user), nil + return user, nil } -func (r *UserRepository) UpdatePassword(userId uuid.UUID, organizationId string, password string, isTemporary bool) error { - var updateUser = User{ - Password: password, - } - if isTemporary { - updateUser.PasswordUpdatedAt = time.Time{} - } else { - updateUser.PasswordUpdatedAt = time.Now() - } - res := r.db.Model(&User{}).Where("id = ? AND organization_id = ?", userId, organizationId). - Select("password", "password_updated_at").Updates(updateUser) - if res.RowsAffected == 0 || res.Error != nil { - return httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") - } - if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return res.Error +func (r *UserRepository) ListUsersByRole(ctx context.Context, organizationId string, roleId string, pg *pagination.Pagination) (*[]model.User, error) { + var users []model.User + + if pg == nil { + pg = pagination.NewPagination(nil) } - return nil -} -func (r *UserRepository) DeleteWithUuid(uuid uuid.UUID) error { - res := r.db.Unscoped().Delete(&User{}, "id = ?", uuid) + _, res := pg.Fetch(r.db.WithContext(ctx).Preload("Organization").Preload("Roles").Model(&model.User{}). + Where("users.organization_id = ? AND roles.id = ?", organizationId, roleId). + Joins("JOIN user_roles ON users.id = user_roles.user_id"). + Joins("JOIN roles ON user_roles.role_id = roles.id"), &users) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return nil, res.Error } - return nil -} -type Role struct { - gorm.Model + var out []model.User + out = append(out, users...) - ID uuid.UUID `gorm:"primarykey;type:uuid;"` - Name string - Description string - Creator uuid.UUID -} - -func (r *Role) BeforeCreate(tx *gorm.DB) (err error) { - r.ID = uuid.New() - return nil -} - -type Policy struct { - gorm.Model - - //ID uuid.UUID `gorm:"primarykey;type:uuid;"` - RoleId uuid.UUID - Role Role `gorm:"references:ID"` - Name string - Description string - Create bool `gorm:"column:c"` - CreatePriviledge string - Update bool `gorm:"column:u"` - UpdatePriviledge string - Read bool `gorm:"column:r"` - ReadPriviledge string - Delete bool `gorm:"column:d"` - DeletePriviledge string - Creator uuid.UUID + return &out, nil } -type UserRole struct { - UserId uuid.UUID - User User - RoleId uuid.UUID - Role Role -} +func (r *UserRepository) Update(ctx context.Context, user *model.User) (*model.User, error) { + res := r.db.WithContext(ctx).Model(&model.User{}).Where("id = ?", user.ID). + Select("Name", "Email", "Department", "Description").Updates(model.User{ + Name: user.Name, + Email: user.Email, + Department: user.Department, + Description: user.Description, + }) -func (r *UserRepository) AssignRoleWithUuid(uuid uuid.UUID, roleName string) error { - _, err := r.GetByUuid(uuid) - if err != nil { - return err + if res.Error != nil { + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return nil, res.Error } - role, err := r.getRoleByName(roleName) + err := r.db.WithContext(ctx).Model(&user).Association("Roles").Replace(user.Roles) if err != nil { - return err + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + return nil, err } - newRole := UserRole{ - UserId: uuid, - RoleId: role.ID, - } - res := r.db.Create(&newRole) + outUser := model.User{} + res = r.db.WithContext(ctx).Preload("Organization").Preload("Roles").Model(&model.User{}).Where("users.id = ?", user.ID).Find(&outUser) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return res.Error + return nil, res.Error } - - return nil + return &outUser, nil } -func (r *UserRepository) AssignRole(accountId string, organizationId string, roleName string) error { - user, err := r.getUserByAccountId(accountId, organizationId) - if err != nil { - return err - } - - role, err := r.getRoleByName(roleName) - if err != nil { - return err +func (r *UserRepository) UpdatePasswordAt(ctx context.Context, userId uuid.UUID, organizationId string, isTemporary bool) error { + var updateUser = model.User{} + if isTemporary { + updateUser.PasswordUpdatedAt = time.Time{} + } else { + updateUser.PasswordUpdatedAt = time.Now() } + res := r.db.WithContext(ctx).Model(&model.User{}).Where("id = ? AND organization_id = ?", userId, organizationId). + Select("password_updated_at").Updates(updateUser) - newRole := UserRole{ - UserId: user.ID, - RoleId: role.ID, + if res.RowsAffected == 0 || res.Error != nil { + return httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } - res := r.db.Create(&newRole) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) return res.Error } return nil } -func (r *UserRepository) GetRoleByName(roleName string) (domain.Role, error) { - role, err := r.getRoleByName(roleName) - if err != nil { - return domain.Role{}, err +func (r *UserRepository) DeleteWithUuid(ctx context.Context, uuid uuid.UUID) error { + var user model.User + if err := r.db.WithContext(ctx).Model(&model.User{}).Preload("Organization").Preload("Roles").Find(&user, "id = ?", uuid).Error; err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + return err } - return r.reflectRole(role), nil -} - -func (r *UserRepository) FetchRoles() (*[]domain.Role, error) { - var roles []Role - res := r.db.Find(&roles) - + res := r.db.WithContext(ctx).Delete(&user) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return nil, res.Error - } - - if res.RowsAffected == 0 { - return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return res.Error } + return nil +} - var out []domain.Role - for _, role := range roles { - outRole := r.reflectRole(role) - out = append(out, outRole) +func (r *UserRepository) GetRoleByName(ctx context.Context, roleName string) (model.Role, error) { + role, err := r.getRoleByName(ctx, roleName) + if err != nil { + return model.Role{}, err } - return &out, nil + return role, nil } // private members -func (r *UserRepository) getUserByAccountId(accountId string, organizationId string) (User, error) { - user := User{} - res := r.db.Model(&User{}).Preload("Organization").Preload("Role"). +func (r *UserRepository) getUserByAccountId(ctx context.Context, accountId string, organizationId string) (model.User, error) { + user := model.User{} + res := r.db.WithContext(ctx).Model(&model.User{}).Preload("Organization").Preload("Roles"). Find(&user, "account_id = ? AND organization_id = ?", accountId, organizationId) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return User{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.User{}, res.Error } if res.RowsAffected == 0 { - return User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + return model.User{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } return user, nil } -func (r *UserRepository) getRoleByName(roleName string) (Role, error) { - role := Role{} - res := r.db.First(&role, "name = ?", roleName) +func (r *UserRepository) getRoleByName(ctx context.Context, roleName string) (model.Role, error) { + role := model.Role{} + res := r.db.WithContext(ctx).First(&role, "name = ?", roleName) if res.Error != nil { - log.Errorf("error is :%s(%T)", res.Error.Error(), res.Error) - return Role{}, res.Error + log.Errorf(ctx, "error is :%s(%T)", res.Error.Error(), res.Error) + return model.Role{}, res.Error } if res.RowsAffected == 0 { - return Role{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") + return model.Role{}, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") } - //if res.RowsAffected == 0 { - // return Role{}, nil - //} - return role, nil } -func (r *UserRepository) reflect(user User) domain.User { - role := domain.Role{ - ID: user.Role.ID.String(), - Name: user.Role.Name, - Description: user.Role.Description, - Creator: user.Role.Creator.String(), - CreatedAt: user.Role.CreatedAt, - UpdatedAt: user.Role.UpdatedAt, - } - //for _, role := range user.Roles { - // outRole := domain.Role{ - // ID: role.ID.String(), - // Name: role.Name, - // Description: role.Description, - // Creator: role.Creator.String(), - // CreatedAt: role.CreatedAt, - // UpdatedAt: role.UpdatedAt, - // } - // resRoles = append(resRoles, outRole) - //} +func (r *UserRepository) AccountIdFilter(accountId string) FilterFunc { + return func(user *gorm.DB) *gorm.DB { + return user.Where("account_id = ?", accountId) + } +} - organization := domain.Organization{ - ID: user.Organization.ID, - Name: user.Organization.Name, - Description: user.Organization.Description, - Phone: user.Organization.Phone, - Status: user.Organization.Status, - StatusDesc: user.Organization.StatusDesc, - Creator: user.Organization.Creator.String(), - CreatedAt: user.Organization.CreatedAt, - UpdatedAt: user.Organization.UpdatedAt, - } - //for _, organization := range user.Organizations { - // outOrganization := domain.Organization{ - // ID: organization.ID, - // Name: organization.Name, - // Description: organization.Description, - // Creator: organization.Creator.String(), - // CreatedAt: organization.CreatedAt, - // UpdatedAt: organization.UpdatedAt, - // } - // resOrganizations = append(resOrganizations, outOrganization) - //} +func (r *UserRepository) OrganizationFilter(organization string) FilterFunc { + return func(user *gorm.DB) *gorm.DB { + return user.Where("organization_id = ?", organization) + } +} - return domain.User{ - ID: user.ID.String(), - AccountId: user.AccountId, - Password: user.Password, - Name: user.Name, - Role: role, - Organization: organization, - Creator: user.Creator.String(), - CreatedAt: user.CreatedAt, - UpdatedAt: user.UpdatedAt, - Email: user.Email, - Department: user.Department, - Description: user.Description, - PasswordUpdatedAt: user.PasswordUpdatedAt, +func (r *UserRepository) EmailFilter(email string) FilterFunc { + return func(user *gorm.DB) *gorm.DB { + return user.Where("email = ?", email) } } -func (r *UserRepository) reflectRole(role Role) domain.Role { - return domain.Role{ - ID: role.ID.String(), - Name: role.Name, - Description: role.Description, - Creator: role.Creator.String(), - CreatedAt: role.CreatedAt, - UpdatedAt: role.UpdatedAt, +func (r *UserRepository) NameFilter(name string) FilterFunc { + return func(user *gorm.DB) *gorm.DB { + return user.Where("name = ?", name) } } diff --git a/internal/route/route.go b/internal/route/route.go index e82d6214..e9edbf12 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -1,29 +1,29 @@ package route import ( - "bytes" - "context" - "fmt" - "io" "net/http" "time" - "github.com/google/uuid" + internalApi "github.com/openinfradev/tks-api/internal/delivery/api" + "github.com/openinfradev/tks-api/internal/middleware/audit" + "github.com/openinfradev/tks-api/internal/middleware/auth/requestRecoder" + "github.com/openinfradev/tks-api/internal/middleware/logging" + "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/openinfradev/tks-api/internal" delivery "github.com/openinfradev/tks-api/internal/delivery/http" "github.com/openinfradev/tks-api/internal/keycloak" - "github.com/openinfradev/tks-api/internal/middleware/auth" + internalMiddleware "github.com/openinfradev/tks-api/internal/middleware" "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator" + authCustom "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/custom" authKeycloak "github.com/openinfradev/tks-api/internal/middleware/auth/authenticator/keycloak" "github.com/openinfradev/tks-api/internal/middleware/auth/authorizer" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/usecase" argowf "github.com/openinfradev/tks-api/pkg/argo-client" - "github.com/openinfradev/tks-api/pkg/log" gcache "github.com/patrickmn/go-cache" - "github.com/swaggo/http-swagger" + httpSwagger "github.com/swaggo/http-swagger" "gorm.io/gorm" ) @@ -36,161 +36,343 @@ var ( SYSTEM_API_PREFIX = internal.SYSTEM_API_PREFIX ) -type StatusRecorder struct { - http.ResponseWriter - Status int -} - -func (r *StatusRecorder) WriteHeader(status int) { - r.Status = status - r.ResponseWriter.WriteHeader(status) -} - func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloak, asset http.Handler) http.Handler { r := mux.NewRouter() + cache := gcache.New(5*time.Minute, 10*time.Minute) + repoFactory := repository.Repository{ - Auth: repository.NewAuthRepository(db), - User: repository.NewUserRepository(db), - Cluster: repository.NewClusterRepository(db), - Organization: repository.NewOrganizationRepository(db), - AppGroup: repository.NewAppGroupRepository(db), - AppServeApp: repository.NewAppServeAppRepository(db), - CloudAccount: repository.NewCloudAccountRepository(db), - StackTemplate: repository.NewStackTemplateRepository(db), - Alert: repository.NewAlertRepository(db), + Auth: repository.NewAuthRepository(db), + User: repository.NewUserRepository(db), + Cluster: repository.NewClusterRepository(db), + Organization: repository.NewOrganizationRepository(db), + AppGroup: repository.NewAppGroupRepository(db), + AppServeApp: repository.NewAppServeAppRepository(db), + CloudAccount: repository.NewCloudAccountRepository(db), + StackTemplate: repository.NewStackTemplateRepository(db), + SystemNotification: repository.NewSystemNotificationRepository(db), + SystemNotificationTemplate: repository.NewSystemNotificationTemplateRepository(db), + SystemNotificationRule: repository.NewSystemNotificationRuleRepository(db), + Role: repository.NewRoleRepository(db), + Project: repository.NewProjectRepository(db), + Permission: repository.NewPermissionRepository(db), + Endpoint: repository.NewEndpointRepository(db), + Audit: repository.NewAuditRepository(db), + PolicyTemplate: repository.NewPolicyTemplateRepository(db), + Policy: repository.NewPolicyRepository(db), + Dashboard: repository.NewDashboardRepository(db), } - authMiddleware := auth.NewAuthMiddleware( - authenticator.NewAuthenticator(authKeycloak.NewKeycloakAuthenticator(kc)), - authorizer.NewDefaultAuthorization(repoFactory)) - cache := gcache.New(5*time.Minute, 10*time.Minute) + usecaseFactory := usecase.Usecase{ + Auth: usecase.NewAuthUsecase(repoFactory, kc), + User: usecase.NewUserUsecase(repoFactory, kc), + Cluster: usecase.NewClusterUsecase(repoFactory, argoClient, cache), + Organization: usecase.NewOrganizationUsecase(repoFactory, argoClient, kc), + AppGroup: usecase.NewAppGroupUsecase(repoFactory, argoClient), + AppServeApp: usecase.NewAppServeAppUsecase(repoFactory, argoClient), + CloudAccount: usecase.NewCloudAccountUsecase(repoFactory, argoClient), + StackTemplate: usecase.NewStackTemplateUsecase(repoFactory), + Dashboard: usecase.NewDashboardUsecase(repoFactory, cache), + SystemNotification: usecase.NewSystemNotificationUsecase(repoFactory), + SystemNotificationTemplate: usecase.NewSystemNotificationTemplateUsecase(repoFactory), + SystemNotificationRule: usecase.NewSystemNotificationRuleUsecase(repoFactory), + Stack: usecase.NewStackUsecase(repoFactory, argoClient, usecase.NewDashboardUsecase(repoFactory, cache)), + Project: usecase.NewProjectUsecase(repoFactory, kc, argoClient), + Audit: usecase.NewAuditUsecase(repoFactory), + Role: usecase.NewRoleUsecase(repoFactory, kc), + Permission: usecase.NewPermissionUsecase(repoFactory), + PolicyTemplate: usecase.NewPolicyTemplateUsecase(repoFactory), + Policy: usecase.NewPolicyUsecase(repoFactory), + } - r.Use(loggingMiddleware) + customMiddleware := internalMiddleware.NewMiddleware( + authenticator.NewAuthenticator(authKeycloak.NewKeycloakAuthenticator(kc), repoFactory, authCustom.NewCustomAuthenticator(repoFactory)), + authorizer.NewDefaultAuthorization(repoFactory), + requestRecoder.NewDefaultRequestRecoder(), + audit.NewDefaultAudit(repoFactory)) + + r.Use(logging.LoggingMiddleware) // [TODO] Transaction //r.Use(transactionMiddleware(db)) - authHandler := delivery.NewAuthHandler(usecase.NewAuthUsecase(repoFactory, kc)) + authHandler := delivery.NewAuthHandler(usecaseFactory) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/login", authHandler.Login).Methods(http.MethodPost) - r.HandleFunc(API_PREFIX+API_VERSION+"/auth/ping", authHandler.PingToken).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/auth/logout", authMiddleware.Handle(http.HandlerFunc(authHandler.Logout))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/auth/refresh", authMiddleware.Handle(http.HandlerFunc(authHandler.RefreshToken))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/auth/logout", customMiddleware.Handle(internalApi.Logout, http.HandlerFunc(authHandler.Logout))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/auth/refresh", customMiddleware.Handle(internalApi.RefreshToken, http.HandlerFunc(authHandler.RefreshToken))).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-id/verification", authHandler.FindId).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-password/verification", authHandler.FindPassword).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-id/code", authHandler.VerifyIdentityForLostId).Methods(http.MethodPost) r.HandleFunc(API_PREFIX+API_VERSION+"/auth/find-password/code", authHandler.VerifyIdentityForLostPassword).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/auth/verify-token", customMiddleware.Handle(internalApi.VerifyToken, http.HandlerFunc(authHandler.VerifyToken))).Methods(http.MethodGet) //r.HandleFunc(API_PREFIX+API_VERSION+"/cookie-test", authHandler.CookieTest).Methods(http.MethodPost) //r.HandleFunc(API_PREFIX+API_VERSION+"/auth/callback", authHandler.CookieTestCallback).Methods(http.MethodGet) - userHandler := delivery.NewUserHandler(usecase.NewUserUsecase(repoFactory, kc)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware.Handle(http.HandlerFunc(userHandler.Create))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", authMiddleware.Handle(http.HandlerFunc(userHandler.List))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Get))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Update))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/reset-password", authMiddleware.Handle(http.HandlerFunc(userHandler.ResetPassword))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", authMiddleware.Handle(http.HandlerFunc(userHandler.Delete))).Methods(http.MethodDelete) - - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", authMiddleware.Handle(http.HandlerFunc(userHandler.GetMyProfile))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", authMiddleware.Handle(http.HandlerFunc(userHandler.UpdateMyProfile))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/password", authMiddleware.Handle(http.HandlerFunc(userHandler.UpdateMyPassword))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/next-password-change", authMiddleware.Handle(http.HandlerFunc(userHandler.RenewPasswordExpiredDate))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", authMiddleware.Handle(http.HandlerFunc(userHandler.DeleteMyProfile))).Methods(http.MethodDelete) - - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/account-id/{accountId}/existence", authMiddleware.Handle(http.HandlerFunc(userHandler.CheckId))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/email/{email}/existence", authMiddleware.Handle(http.HandlerFunc(userHandler.CheckEmail))).Methods(http.MethodGet) - - organizationHandler := delivery.NewOrganizationHandler(usecase.NewOrganizationUsecase(repoFactory, argoClient, kc), usecase.NewUserUsecase(repoFactory, kc)) - r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware.Handle(http.HandlerFunc(organizationHandler.CreateOrganization))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations", authMiddleware.Handle(http.HandlerFunc(organizationHandler.GetOrganizations))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.GetOrganization))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.DeleteOrganization))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", authMiddleware.Handle(http.HandlerFunc(organizationHandler.UpdateOrganization))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/primary-cluster", authMiddleware.Handle(http.HandlerFunc(organizationHandler.UpdatePrimaryCluster))).Methods(http.MethodPatch) - - clusterHandler := delivery.NewClusterHandler(usecase.NewClusterUsecase(repoFactory, argoClient, cache)) - r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.CreateCluster))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/clusters", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusters))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/import", authMiddleware.Handle(http.HandlerFunc(clusterHandler.ImportCluster))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetCluster))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", authMiddleware.Handle(http.HandlerFunc(clusterHandler.DeleteCluster))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/site-values", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetClusterSiteValues))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/install", authMiddleware.Handle(http.HandlerFunc(clusterHandler.InstallCluster))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/bootstrap-kubeconfig", authMiddleware.Handle(http.HandlerFunc(clusterHandler.CreateBootstrapKubeconfig))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/bootstrap-kubeconfig", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetBootstrapKubeconfig))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/nodes", authMiddleware.Handle(http.HandlerFunc(clusterHandler.GetNodes))).Methods(http.MethodGet) - - appGroupHandler := delivery.NewAppGroupHandler(usecase.NewAppGroupUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.CreateAppGroup))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/app-groups", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetAppGroups))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetAppGroup))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.DeleteAppGroup))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.GetApplications))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", authMiddleware.Handle(http.HandlerFunc(appGroupHandler.CreateApplication))).Methods(http.MethodPost) - - appServeAppHandler := delivery.NewAppServeAppHandler(usecase.NewAppServeAppUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.CreateAppServeApp))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeApps))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/count", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetNumOfAppsOnStack))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeApp))).Methods(http.MethodGet) - // TODO: To be implemented - // r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/tasks/{taskId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeAppTask))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/latest-task", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.GetAppServeAppLatestTask))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/exist", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.IsAppServeAppExist))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/name/{name}/existence", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.IsAppServeAppNameExist))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.DeleteAppServeApp))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeApp))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/status", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppStatus))).Methods(http.MethodPatch) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/endpoint", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.UpdateAppServeAppEndpoint))).Methods(http.MethodPatch) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/app-serve-apps/{appId}/rollback", authMiddleware.Handle(http.HandlerFunc(appServeAppHandler.RollbackAppServeApp))).Methods(http.MethodPost) - - cloudAccountHandler := delivery.NewCloudAccountHandler(usecase.NewCloudAccountUsecase(repoFactory, argoClient)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetCloudAccounts))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.CreateCloudAccount))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/name/{name}/existence", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.CheckCloudAccountName))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.CheckAwsAccountId))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetCloudAccount))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.UpdateCloudAccount))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.DeleteCloudAccount))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.DeleteForceCloudAccount))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quotas", authMiddleware.Handle(http.HandlerFunc(cloudAccountHandler.GetResourceQuota))).Methods(http.MethodGet) - - stackTemplateHandler := delivery.NewStackTemplateHandler(usecase.NewStackTemplateUsecase(repoFactory)) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.GetStackTemplates))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.CreateStackTemplate))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.GetStackTemplate))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.UpdateStackTemplate))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/stack-templates/{stackTemplateId}", authMiddleware.Handle(http.HandlerFunc(stackTemplateHandler.DeleteStackTemplate))).Methods(http.MethodDelete) - - dashboardHandler := delivery.NewDashboardHandler(usecase.NewDashboardUsecase(repoFactory, cache)) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/charts", authMiddleware.Handle(http.HandlerFunc(dashboardHandler.GetCharts))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/charts/{chartType}", authMiddleware.Handle(http.HandlerFunc(dashboardHandler.GetChart))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/stacks", authMiddleware.Handle(http.HandlerFunc(dashboardHandler.GetStacks))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboard/resources", authMiddleware.Handle(http.HandlerFunc(dashboardHandler.GetResources))).Methods(http.MethodGet) - - alertHandler := delivery.NewAlertHandler(usecase.NewAlertUsecase(repoFactory)) - r.HandleFunc(SYSTEM_API_PREFIX+SYSTEM_API_VERSION+"/alerts", alertHandler.CreateAlert).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts", authMiddleware.Handle(http.HandlerFunc(alertHandler.GetAlerts))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts/{alertId}", authMiddleware.Handle(http.HandlerFunc(alertHandler.GetAlert))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts/{alertId}", authMiddleware.Handle(http.HandlerFunc(alertHandler.DeleteAlert))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts/{alertId}", authMiddleware.Handle(http.HandlerFunc(alertHandler.UpdateAlert))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts/{alertId}/actions", authMiddleware.Handle(http.HandlerFunc(alertHandler.CreateAlertAction))).Methods(http.MethodPost) - //r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/alerts/{alertId}/actions/status", authMiddleware.Handle(http.HandlerFunc(alertHandler.UpdateAlertActionStatus))).Methods(http.MethodPatch) - - stackHandler := delivery.NewStackHandler(usecase.NewStackUsecase(repoFactory, argoClient, usecase.NewDashboardUsecase(repoFactory, cache))) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStacks))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", authMiddleware.Handle(http.HandlerFunc(stackHandler.CreateStack))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/name/{name}/existence", authMiddleware.Handle(http.HandlerFunc(stackHandler.CheckStackName))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStack))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.UpdateStack))).Methods(http.MethodPut) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", authMiddleware.Handle(http.HandlerFunc(stackHandler.DeleteStack))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/kube-config", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStackKubeConfig))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/status", authMiddleware.Handle(http.HandlerFunc(stackHandler.GetStackStatus))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", authMiddleware.Handle(http.HandlerFunc(stackHandler.SetFavorite))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", authMiddleware.Handle(http.HandlerFunc(stackHandler.DeleteFavorite))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/install", authMiddleware.Handle(http.HandlerFunc(stackHandler.InstallStack))).Methods(http.MethodPost) - - r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", alertHandler.CreateAlert).Methods(http.MethodPost) + userHandler := delivery.NewUserHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.CreateUser, http.HandlerFunc(userHandler.Create))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.ListUser, http.HandlerFunc(userHandler.List))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", customMiddleware.Handle(internalApi.GetUser, http.HandlerFunc(userHandler.Get))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.UpdateUsers, http.HandlerFunc(userHandler.UpdateUsers))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", customMiddleware.Handle(internalApi.UpdateUser, http.HandlerFunc(userHandler.Update))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/reset-password", customMiddleware.Handle(internalApi.ResetPassword, http.HandlerFunc(userHandler.ResetPassword))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}", customMiddleware.Handle(internalApi.DeleteUser, http.HandlerFunc(userHandler.Delete))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/account-id/{accountId}/existence", customMiddleware.Handle(internalApi.CheckId, http.HandlerFunc(userHandler.CheckId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/email/{email}/existence", customMiddleware.Handle(internalApi.CheckEmail, http.HandlerFunc(userHandler.CheckEmail))).Methods(http.MethodGet) + + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", customMiddleware.Handle(internalApi.GetMyProfile, http.HandlerFunc(userHandler.GetMyProfile))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", customMiddleware.Handle(internalApi.UpdateMyProfile, http.HandlerFunc(userHandler.UpdateMyProfile))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/password", customMiddleware.Handle(internalApi.UpdateMyPassword, http.HandlerFunc(userHandler.UpdateMyPassword))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile/next-password-change", customMiddleware.Handle(internalApi.RenewPasswordExpiredDate, http.HandlerFunc(userHandler.RenewPasswordExpiredDate))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/my-profile", customMiddleware.Handle(internalApi.DeleteMyProfile, http.HandlerFunc(userHandler.DeleteMyProfile))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/users/{accountId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByAccountId, http.HandlerFunc(userHandler.GetPermissionsByAccountId))).Methods(http.MethodGet) + + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/users", customMiddleware.Handle(internalApi.Admin_CreateUser, http.HandlerFunc(userHandler.Admin_Create))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/users/{accountId}", customMiddleware.Handle(internalApi.Admin_UpdateUser, http.HandlerFunc(userHandler.Admin_Update))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/users/{accountId}", customMiddleware.Handle(internalApi.Admin_DeleteUser, http.HandlerFunc(userHandler.Admin_Delete))).Methods(http.MethodDelete) + + organizationHandler := delivery.NewOrganizationHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations", customMiddleware.Handle(internalApi.Admin_CreateOrganization, http.HandlerFunc(organizationHandler.Admin_CreateOrganization))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}", customMiddleware.Handle(internalApi.Admin_DeleteOrganization, http.HandlerFunc(organizationHandler.Admin_DeleteOrganization))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations", customMiddleware.Handle(internalApi.GetOrganizations, http.HandlerFunc(organizationHandler.GetOrganizations))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", customMiddleware.Handle(internalApi.GetOrganization, http.HandlerFunc(organizationHandler.GetOrganization))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}", customMiddleware.Handle(internalApi.UpdateOrganization, http.HandlerFunc(organizationHandler.UpdateOrganization))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/primary-cluster", customMiddleware.Handle(internalApi.UpdatePrimaryCluster, http.HandlerFunc(organizationHandler.UpdatePrimaryCluster))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/name/{name}/existence", customMiddleware.Handle(internalApi.CheckOrganizationName, http.HandlerFunc(organizationHandler.CheckOrganizationName))).Methods(http.MethodGet) + + clusterHandler := delivery.NewClusterHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/clusters", customMiddleware.Handle(internalApi.CreateCluster, http.HandlerFunc(clusterHandler.CreateCluster))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/clusters", customMiddleware.Handle(internalApi.GetClusters, http.HandlerFunc(clusterHandler.GetClusters))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/import", customMiddleware.Handle(internalApi.ImportCluster, http.HandlerFunc(clusterHandler.ImportCluster))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", customMiddleware.Handle(internalApi.GetCluster, http.HandlerFunc(clusterHandler.GetCluster))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}", customMiddleware.Handle(internalApi.DeleteCluster, http.HandlerFunc(clusterHandler.DeleteCluster))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/site-values", customMiddleware.Handle(internalApi.GetClusterSiteValues, http.HandlerFunc(clusterHandler.GetClusterSiteValues))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/install", customMiddleware.Handle(internalApi.InstallCluster, http.HandlerFunc(clusterHandler.InstallCluster))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/bootstrap-kubeconfig", customMiddleware.Handle(internalApi.CreateBootstrapKubeconfig, http.HandlerFunc(clusterHandler.CreateBootstrapKubeconfig))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/bootstrap-kubeconfig", customMiddleware.Handle(internalApi.GetBootstrapKubeconfig, http.HandlerFunc(clusterHandler.GetBootstrapKubeconfig))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/clusters/{clusterId}/nodes", customMiddleware.Handle(internalApi.GetNodes, http.HandlerFunc(clusterHandler.GetNodes))).Methods(http.MethodGet) + + appGroupHandler := delivery.NewAppGroupHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/app-groups", customMiddleware.Handle(internalApi.CreateAppgroup, http.HandlerFunc(appGroupHandler.CreateAppGroup))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/app-groups", customMiddleware.Handle(internalApi.GetAppgroups, http.HandlerFunc(appGroupHandler.GetAppGroups))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", customMiddleware.Handle(internalApi.GetAppgroup, http.HandlerFunc(appGroupHandler.GetAppGroup))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}", customMiddleware.Handle(internalApi.DeleteAppgroup, http.HandlerFunc(appGroupHandler.DeleteAppGroup))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", customMiddleware.Handle(internalApi.GetApplications, http.HandlerFunc(appGroupHandler.GetApplications))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/app-groups/{appGroupId}/applications", customMiddleware.Handle(internalApi.CreateApplication, http.HandlerFunc(appGroupHandler.CreateApplication))).Methods(http.MethodPost) + + appServeAppHandler := delivery.NewAppServeAppHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps", customMiddleware.Handle(internalApi.CreateAppServeApp, http.HandlerFunc(appServeAppHandler.CreateAppServeApp))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps", customMiddleware.Handle(internalApi.GetAppServeApps, http.HandlerFunc(appServeAppHandler.GetAppServeApps))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/count", customMiddleware.Handle(internalApi.GetNumOfAppsOnStack, http.HandlerFunc(appServeAppHandler.GetNumOfAppsOnStack))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks", customMiddleware.Handle(internalApi.GetAppServeAppTasksByAppId, http.HandlerFunc(appServeAppHandler.GetAppServeAppTasksByAppId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/tasks/{taskId}", customMiddleware.Handle(internalApi.GetAppServeAppTaskDetail, http.HandlerFunc(appServeAppHandler.GetAppServeAppTaskDetail))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/latest-task", customMiddleware.Handle(internalApi.GetAppServeAppLatestTask, http.HandlerFunc(appServeAppHandler.GetAppServeAppLatestTask))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/exist", customMiddleware.Handle(internalApi.IsAppServeAppExist, http.HandlerFunc(appServeAppHandler.IsAppServeAppExist))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/name/{name}/existence", customMiddleware.Handle(internalApi.IsAppServeAppNameExist, http.HandlerFunc(appServeAppHandler.IsAppServeAppNameExist))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}", customMiddleware.Handle(internalApi.DeleteAppServeApp, http.HandlerFunc(appServeAppHandler.DeleteAppServeApp))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}", customMiddleware.Handle(internalApi.UpdateAppServeApp, http.HandlerFunc(appServeAppHandler.UpdateAppServeApp))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/status", customMiddleware.Handle(internalApi.UpdateAppServeAppStatus, http.HandlerFunc(appServeAppHandler.UpdateAppServeAppStatus))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/endpoint", customMiddleware.Handle(internalApi.UpdateAppServeAppEndpoint, http.HandlerFunc(appServeAppHandler.UpdateAppServeAppEndpoint))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/app-serve-apps/{appId}/rollback", customMiddleware.Handle(internalApi.RollbackAppServeApp, http.HandlerFunc(appServeAppHandler.RollbackAppServeApp))).Methods(http.MethodPost) + + cloudAccountHandler := delivery.NewCloudAccountHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", customMiddleware.Handle(internalApi.GetCloudAccounts, http.HandlerFunc(cloudAccountHandler.GetCloudAccounts))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts", customMiddleware.Handle(internalApi.CreateCloudAccount, http.HandlerFunc(cloudAccountHandler.CreateCloudAccount))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/name/{name}/existence", customMiddleware.Handle(internalApi.CheckCloudAccountName, http.HandlerFunc(cloudAccountHandler.CheckCloudAccountName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/aws-account-id/{awsAccountId}/existence", customMiddleware.Handle(internalApi.CheckAwsAccountId, http.HandlerFunc(cloudAccountHandler.CheckAwsAccountId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", customMiddleware.Handle(internalApi.GetCloudAccount, http.HandlerFunc(cloudAccountHandler.GetCloudAccount))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", customMiddleware.Handle(internalApi.UpdateCloudAccount, http.HandlerFunc(cloudAccountHandler.UpdateCloudAccount))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}", customMiddleware.Handle(internalApi.DeleteCloudAccount, http.HandlerFunc(cloudAccountHandler.DeleteCloudAccount))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/error", customMiddleware.Handle(internalApi.DeleteForceCloudAccount, http.HandlerFunc(cloudAccountHandler.DeleteForceCloudAccount))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/cloud-accounts/{cloudAccountId}/quotas", customMiddleware.Handle(internalApi.GetResourceQuota, http.HandlerFunc(cloudAccountHandler.GetResourceQuota))).Methods(http.MethodGet) + + stackTemplateHandler := delivery.NewStackTemplateHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates", customMiddleware.Handle(internalApi.Admin_GetStackTemplates, http.HandlerFunc(stackTemplateHandler.GetStackTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/services", customMiddleware.Handle(internalApi.Admin_GetStackTemplateServices, http.HandlerFunc(stackTemplateHandler.GetStackTemplateServices))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/template-ids", customMiddleware.Handle(internalApi.Admin_GetStackTemplateTemplateIds, http.HandlerFunc(stackTemplateHandler.GetStackTemplateTemplateIds))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/name/{name}/existence", customMiddleware.Handle(internalApi.Admin_CheckStackTemplateName, http.HandlerFunc(stackTemplateHandler.CheckStackTemplateName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/{stackTemplateId}", customMiddleware.Handle(internalApi.Admin_GetStackTemplates, http.HandlerFunc(stackTemplateHandler.GetStackTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates", customMiddleware.Handle(internalApi.Admin_CreateStackTemplate, http.HandlerFunc(stackTemplateHandler.CreateStackTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/{stackTemplateId}/organizations", customMiddleware.Handle(internalApi.Admin_UpdateStackTemplateOrganizations, http.HandlerFunc(stackTemplateHandler.UpdateStackTemplateOrganizations))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/{stackTemplateId}", customMiddleware.Handle(internalApi.Admin_UpdateStackTemplate, http.HandlerFunc(stackTemplateHandler.UpdateStackTemplate))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/stack-templates/{stackTemplateId}", customMiddleware.Handle(internalApi.Admin_DeleteStackTemplate, http.HandlerFunc(stackTemplateHandler.DeleteStackTemplate))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stack-templates", customMiddleware.Handle(internalApi.GetOrganizationStackTemplates, http.HandlerFunc(stackTemplateHandler.GetOrganizationStackTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stack-templates/{stackTemplateId}", customMiddleware.Handle(internalApi.GetOrganizationStackTemplate, http.HandlerFunc(stackTemplateHandler.GetOrganizationStackTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stack-templates", customMiddleware.Handle(internalApi.AddOrganizationStackTemplates, http.HandlerFunc(stackTemplateHandler.AddOrganizationStackTemplates))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stack-templates", customMiddleware.Handle(internalApi.RemoveOrganizationStackTemplates, http.HandlerFunc(stackTemplateHandler.RemoveOrganizationStackTemplates))).Methods(http.MethodPut) + + dashboardHandler := delivery.NewDashboardHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/charts", customMiddleware.Handle(internalApi.GetChartsDashboard, http.HandlerFunc(dashboardHandler.GetCharts))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/charts/{chartType}", customMiddleware.Handle(internalApi.GetChartDashboard, http.HandlerFunc(dashboardHandler.GetChart))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/stacks", customMiddleware.Handle(internalApi.GetStacksDashboard, http.HandlerFunc(dashboardHandler.GetStacks))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/resources", customMiddleware.Handle(internalApi.GetResourcesDashboard, http.HandlerFunc(dashboardHandler.GetResources))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-status", customMiddleware.Handle(internalApi.GetPolicyStatusDashboard, http.HandlerFunc(dashboardHandler.GetPolicyStatus))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-update", customMiddleware.Handle(internalApi.GetPolicyUpdateDashboard, http.HandlerFunc(dashboardHandler.GetPolicyUpdate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-enforcement", customMiddleware.Handle(internalApi.GetPolicyEnforcementDashboard, http.HandlerFunc(dashboardHandler.GetPolicyEnforcement))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-violation", customMiddleware.Handle(internalApi.GetPolicyViolationDashboard, http.HandlerFunc(dashboardHandler.GetPolicyViolation))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-violation-log", customMiddleware.Handle(internalApi.GetPolicyViolationLogDashboard, http.HandlerFunc(dashboardHandler.GetPolicyViolationLog))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-statistics", customMiddleware.Handle(internalApi.GetPolicyStatisticsDashboard, http.HandlerFunc(dashboardHandler.GetPolicyStatistics))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/workload", customMiddleware.Handle(internalApi.GetWorkloadDashboard, http.HandlerFunc(dashboardHandler.GetWorkload))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/widgets/policy-violation-top5", customMiddleware.Handle(internalApi.GetPolicyViolationTop5Dashboard, http.HandlerFunc(dashboardHandler.GetPolicyViolationTop5))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards", customMiddleware.Handle(internalApi.CreateDashboard, http.HandlerFunc(dashboardHandler.CreateDashboard))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/{dashboardKey}", customMiddleware.Handle(internalApi.GetDashboard, http.HandlerFunc(dashboardHandler.GetDashboard))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/dashboards/{dashboardKey}", customMiddleware.Handle(internalApi.UpdateDashboard, http.HandlerFunc(dashboardHandler.UpdateDashboard))).Methods(http.MethodPut) + + systemNotificationTemplateHandler := delivery.NewSystemNotificationTemplateHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates", customMiddleware.Handle(internalApi.Admin_CreateSystemNotificationTemplate, http.HandlerFunc(systemNotificationTemplateHandler.CreateSystemNotificationTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates", customMiddleware.Handle(internalApi.Admin_GetSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.GetSystemNotificationTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates/{systemNotificationTemplateId}", customMiddleware.Handle(internalApi.Admin_GetSystemNotificationTemplate, http.HandlerFunc(systemNotificationTemplateHandler.GetSystemNotificationTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates/{systemNotificationTemplateId}", customMiddleware.Handle(internalApi.Admin_UpdateSystemNotificationTemplate, http.HandlerFunc(systemNotificationTemplateHandler.UpdateSystemNotificationTemplate))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates/{systemNotificationTemplateId}", customMiddleware.Handle(internalApi.Admin_DeleteSystemNotificationTemplate, http.HandlerFunc(systemNotificationTemplateHandler.DeleteSystemNotificationTemplate))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/system-notification-templates/name/{name}/existence", customMiddleware.Handle(internalApi.Admin_CheckSystemNotificationTemplateName, http.HandlerFunc(systemNotificationTemplateHandler.CheckSystemNotificationTemplateName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-templates", customMiddleware.Handle(internalApi.GetOrganizationSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.GetOrganizationSystemNotificationTemplates))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-templates/{systemNotificationTemplateId}", customMiddleware.Handle(internalApi.GetOrganizationSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.GetOrganizationSystemNotificationTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-templates", customMiddleware.Handle(internalApi.AddOrganizationSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.AddOrganizationSystemNotificationTemplates))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-templates", customMiddleware.Handle(internalApi.RemoveOrganizationSystemNotificationTemplates, http.HandlerFunc(systemNotificationTemplateHandler.RemoveOrganizationSystemNotificationTemplates))).Methods(http.MethodPut) + + systemNotificationRuleHandler := delivery.NewSystemNotificationRuleHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules", customMiddleware.Handle(internalApi.CreateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.CreateSystemNotificationRule))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules", customMiddleware.Handle(internalApi.GetSystemNotificationRules, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRules))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/default-system-rules", customMiddleware.Handle(internalApi.CreateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.MakeDefaultSystemNotificationRules))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/name/{name}/existence", customMiddleware.Handle(internalApi.CheckSystemNotificationRuleName, http.HandlerFunc(systemNotificationRuleHandler.CheckSystemNotificationRuleName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.GetSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.GetSystemNotificationRule))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.UpdateSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.UpdateSystemNotificationRule))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notification-rules/{systemNotificationRuleId}", customMiddleware.Handle(internalApi.DeleteSystemNotificationRule, http.HandlerFunc(systemNotificationRuleHandler.DeleteSystemNotificationRule))).Methods(http.MethodDelete) + + systemNotificationHandler := delivery.NewSystemNotificationHandler(usecaseFactory) + r.HandleFunc(SYSTEM_API_PREFIX+SYSTEM_API_VERSION+"/system-notifications", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications", customMiddleware.Handle(internalApi.GetSystemNotifications, http.HandlerFunc(systemNotificationHandler.GetSystemNotifications))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.GetSystemNotification, http.HandlerFunc(systemNotificationHandler.GetSystemNotification))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.DeleteSystemNotification, http.HandlerFunc(systemNotificationHandler.DeleteSystemNotification))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}", customMiddleware.Handle(internalApi.UpdateSystemNotification, http.HandlerFunc(systemNotificationHandler.UpdateSystemNotification))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/system-notifications/{systemNotificationId}/actions", customMiddleware.Handle(internalApi.CreateSystemNotificationAction, http.HandlerFunc(systemNotificationHandler.CreateSystemNotificationAction))).Methods(http.MethodPost) + r.HandleFunc(API_PREFIX+API_VERSION+"/alerttest", systemNotificationHandler.CreateSystemNotification).Methods(http.MethodPost) + + policyNotificationHandler := delivery.NewPolicyNotificationHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-notifications", customMiddleware.Handle(internalApi.GetSystemNotifications, http.HandlerFunc(policyNotificationHandler.GetPolicyNotifications))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-notifications/{policyNotificationId}", customMiddleware.Handle(internalApi.GetSystemNotification, http.HandlerFunc(policyNotificationHandler.GetPolicyNotification))).Methods(http.MethodGet) + + stackHandler := delivery.NewStackHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", customMiddleware.Handle(internalApi.GetStacks, http.HandlerFunc(stackHandler.GetStacks))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks", customMiddleware.Handle(internalApi.CreateStack, http.HandlerFunc(stackHandler.CreateStack))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/name/{name}/existence", customMiddleware.Handle(internalApi.CheckStackName, http.HandlerFunc(stackHandler.CheckStackName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", customMiddleware.Handle(internalApi.GetStack, http.HandlerFunc(stackHandler.GetStack))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", customMiddleware.Handle(internalApi.UpdateStack, http.HandlerFunc(stackHandler.UpdateStack))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}", customMiddleware.Handle(internalApi.DeleteStack, http.HandlerFunc(stackHandler.DeleteStack))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/kube-config", customMiddleware.Handle(internalApi.GetStackKubeConfig, http.HandlerFunc(stackHandler.GetStackKubeConfig))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/status", customMiddleware.Handle(internalApi.GetStackStatus, http.HandlerFunc(stackHandler.GetStackStatus))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", customMiddleware.Handle(internalApi.SetFavoriteStack, http.HandlerFunc(stackHandler.SetFavorite))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/favorite", customMiddleware.Handle(internalApi.DeleteFavoriteStack, http.HandlerFunc(stackHandler.DeleteFavorite))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/install", customMiddleware.Handle(internalApi.InstallStack, http.HandlerFunc(stackHandler.InstallStack))).Methods(http.MethodPost) + + projectHandler := delivery.NewProjectHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects", customMiddleware.Handle(internalApi.CreateProject, http.HandlerFunc(projectHandler.CreateProject))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects", customMiddleware.Handle(internalApi.GetProjects, http.HandlerFunc(projectHandler.GetProjects))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/project-roles", customMiddleware.Handle(internalApi.GetProjectRoles, http.HandlerFunc(projectHandler.GetProjectRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/project-roles/{projectRoleId}", customMiddleware.Handle(internalApi.GetProjectRole, http.HandlerFunc(projectHandler.GetProjectRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/existence", customMiddleware.Handle(internalApi.GetProjectNamespace, http.HandlerFunc(projectHandler.IsProjectNameExist))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.GetProject, http.HandlerFunc(projectHandler.GetProject))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.UpdateProject, http.HandlerFunc(projectHandler.UpdateProject))).Methods(http.MethodPut) + //r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}", customMiddleware.Handle(internalApi.DeleteProject, http.HandlerFunc(projectHandler.DeleteProject))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.AddProjectMember, http.HandlerFunc(projectHandler.AddProjectMember))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/count", customMiddleware.Handle(internalApi.GetProjectMembers, http.HandlerFunc(projectHandler.GetProjectMemberCount))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}", customMiddleware.Handle(internalApi.GetProjectMember, http.HandlerFunc(projectHandler.GetProjectMember))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.GetProjectMembers, http.HandlerFunc(projectHandler.GetProjectMembers))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members/{projectMemberId}", customMiddleware.Handle(internalApi.RemoveProjectMember, http.HandlerFunc(projectHandler.RemoveProjectMember))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.RemoveProjectMember, http.HandlerFunc(projectHandler.RemoveProjectMembers))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/members", customMiddleware.Handle(internalApi.UpdateProjectMemberRole, http.HandlerFunc(projectHandler.UpdateProjectMembersRole))).Methods(http.MethodPut) + 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/{projectNamespace}/stacks/{stackId}/resources-usage", customMiddleware.Handle(internalApi.GetProjectNamespaceK8sResources, http.HandlerFunc(projectHandler.GetProjectNamespaceResourcesUsage))).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) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}", customMiddleware.Handle(internalApi.DeleteProjectNamespace, http.HandlerFunc(projectHandler.DeleteProjectNamespace))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/namespaces/{projectNamespace}/stacks/{stackId}/kube-config", customMiddleware.Handle(internalApi.GetProjectNamespaceKubeconfig, http.HandlerFunc(projectHandler.GetProjectNamespaceKubeconfig))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/projects/{projectId}/kube-config", customMiddleware.Handle(internalApi.GetProjectKubeconfig, http.HandlerFunc(projectHandler.GetProjectKubeconfig))).Methods(http.MethodGet) + + auditHandler := delivery.NewAuditHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/admin/audits", customMiddleware.Handle(internalApi.GetAudits, http.HandlerFunc(auditHandler.GetAudits))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/admin/audits/{auditId}", customMiddleware.Handle(internalApi.GetAudit, http.HandlerFunc(auditHandler.GetAudit))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/admin/audits/{auditId}", customMiddleware.Handle(internalApi.DeleteAudit, http.HandlerFunc(auditHandler.DeleteAudit))).Methods(http.MethodDelete) + + roleHandler := delivery.NewRoleHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.CreateTksRole, http.HandlerFunc(roleHandler.CreateTksRole))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.ListTksRoles, http.HandlerFunc(roleHandler.ListTksRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.GetTksRole, http.HandlerFunc(roleHandler.GetTksRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.DeleteTksRole, http.HandlerFunc(roleHandler.DeleteTksRole))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.UpdateTksRole, http.HandlerFunc(roleHandler.UpdateTksRole))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.GetPermissionsByRoleId, http.HandlerFunc(roleHandler.GetPermissionsByRoleId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/permissions", customMiddleware.Handle(internalApi.UpdatePermissionsByRoleId, http.HandlerFunc(roleHandler.UpdatePermissionsByRoleId))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleName}/existence", customMiddleware.Handle(internalApi.IsRoleNameExisted, http.HandlerFunc(roleHandler.IsRoleNameExisted))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/users", customMiddleware.Handle(internalApi.AppendUsersToRole, http.HandlerFunc(roleHandler.AppendUsersToRole))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/users", customMiddleware.Handle(internalApi.GetUsersInRoleId, http.HandlerFunc(roleHandler.GetUsersInRoleId))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/roles/{roleId}/users", customMiddleware.Handle(internalApi.RemoveUsersFromRole, http.HandlerFunc(roleHandler.RemoveUsersFromRole))).Methods(http.MethodDelete) + + // Admin + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/roles", customMiddleware.Handle(internalApi.Admin_ListTksRoles, http.HandlerFunc(roleHandler.Admin_ListTksRoles))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/roles/{roleId}", customMiddleware.Handle(internalApi.Admin_GetTksRole, http.HandlerFunc(roleHandler.Admin_GetTksRole))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/projects", customMiddleware.Handle(internalApi.Admin_GetProjects, http.HandlerFunc(projectHandler.Admin_GetProjects))).Methods(http.MethodGet) + + permissionHandler := delivery.NewPermissionHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/permissions/templates", customMiddleware.Handle(internalApi.GetPermissionTemplates, http.HandlerFunc(permissionHandler.GetPermissionTemplates))).Methods(http.MethodGet) + + policyTemplateHandler := delivery.NewPolicyTemplateHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.Admin_CreatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_CreatePolicyTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_DeletePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_DeletePolicyTemplate))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.Admin_UpdatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.Admin_UpdatePolicyTemplate))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/deploy", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplateDeploy, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplateDeploy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/statistics", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplateStatistics, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplateStatistics))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.Admin_ListPolicyTemplateVersions, http.HandlerFunc(policyTemplateHandler.Admin_ListPolicyTemplateVersions))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.Admin_CreatePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_CreatePolicyTemplateVersion))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.Admin_DeletePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_DeletePolicyTemplateVersion))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.Admin_GetPolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.Admin_GetPolicyTemplateVersion))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters", customMiddleware.Handle(internalApi.Admin_ExtractParameters, http.HandlerFunc(policyTemplateHandler.Admin_ExtractParameters))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/kind/{policyTemplateKind}/existence", customMiddleware.Handle(internalApi.Admin_ExistsPolicyTemplateKind, http.HandlerFunc(policyTemplateHandler.Admin_ExistsPolicyTemplateKind))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.Admin_ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.Admin_ExistsPolicyTemplateName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/policyTemplates", customMiddleware.Handle(internalApi.Admin_AddPermittedPolicyTemplatesForOrganization, http.HandlerFunc(policyTemplateHandler.Admin_AddPermittedPolicyTemplatesForOrganization))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/organizations/{organizationId}/policyTemplates", customMiddleware.Handle(internalApi.Admin_DeletePermittedPolicyTemplatesForOrganization, http.HandlerFunc(policyTemplateHandler.Admin_DeletePermittedPolicyTemplatesForOrganization))).Methods(http.MethodPut) + + r.Handle(API_PREFIX+API_VERSION+"/policy-templates/rego-compile", customMiddleware.Handle(internalApi.CompileRego, http.HandlerFunc(policyTemplateHandler.RegoCompile))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates", customMiddleware.Handle(internalApi.ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates", customMiddleware.Handle(internalApi.CreatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplate))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.DeletePolicyTemplate, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplate))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.GetPolicyTemplate, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplate))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.UpdatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.UpdatePolicyTemplate))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/deploy", customMiddleware.Handle(internalApi.GetPolicyTemplateDeploy, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateDeploy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/statistics", customMiddleware.Handle(internalApi.ListPolicyTemplateStatistics, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateStatistics))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.ListPolicyTemplateVersions, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateVersions))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.CreatePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplateVersion))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.DeletePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplateVersion))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.GetPolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateVersion))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/kind/{policyTemplateKind}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateKind, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateKind))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-templates/{policyTemplateId}/versions/{version}/extract-parameters", customMiddleware.Handle(internalApi.ExtractParameters, http.HandlerFunc(policyTemplateHandler.ExtractParameters))).Methods(http.MethodPost) + + policyHandler := delivery.NewPolicyHandler(usecaseFactory) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/mandatory-policies", customMiddleware.Handle(internalApi.GetMandatoryPolicies, http.HandlerFunc(policyHandler.GetMandatoryPolicies))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/mandatory-policies", customMiddleware.Handle(internalApi.SetMandatoryPolicies, http.HandlerFunc(policyHandler.SetMandatoryPolicies))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policy-statistics", customMiddleware.Handle(internalApi.GetPolicyStatistics, http.HandlerFunc(policyHandler.GetPolicyStatistics))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies", customMiddleware.Handle(internalApi.ListPolicy, http.HandlerFunc(policyHandler.ListPolicy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies", customMiddleware.Handle(internalApi.CreatePolicy, http.HandlerFunc(policyHandler.CreatePolicy))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}/clusters", customMiddleware.Handle(internalApi.UpdatePolicyTargetClusters, http.HandlerFunc(policyHandler.UpdatePolicyTargetClusters))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}/edit", customMiddleware.Handle(internalApi.GetPolicyEdit, http.HandlerFunc(policyHandler.GetPolicyEdit))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.GetPolicy, http.HandlerFunc(policyHandler.GetPolicy))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.DeletePolicy, http.HandlerFunc(policyHandler.DeletePolicy))).Methods(http.MethodDelete) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyId}", customMiddleware.Handle(internalApi.UpdatePolicy, http.HandlerFunc(policyHandler.UpdatePolicy))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/name/{policyName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyName, http.HandlerFunc(policyHandler.ExistsPolicyName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/resource-name/{policyResourceName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyResourceName, http.HandlerFunc(policyHandler.ExistsPolicyResourceName))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/policies", customMiddleware.Handle(internalApi.AddPoliciesForStack, http.HandlerFunc(policyHandler.AddPoliciesForStack))).Methods(http.MethodPost) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/policies", customMiddleware.Handle(internalApi.DeletePoliciesForStack, http.HandlerFunc(policyHandler.DeletePoliciesForStack))).Methods(http.MethodPut) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/statistics", customMiddleware.Handle(internalApi.StackPolicyStatistics, http.HandlerFunc(policyHandler.StackPolicyStatistics))).Methods(http.MethodGet) + + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/policy-status", customMiddleware.Handle(internalApi.ListStackPolicyStatus, http.HandlerFunc(policyHandler.ListStackPolicyStatus))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.GetStackPolicyTemplateStatus, http.HandlerFunc(policyHandler.GetStackPolicyTemplateStatus))).Methods(http.MethodGet) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/stacks/{stackId}/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.UpdateStackPolicyTemplateStatus, http.HandlerFunc(policyHandler.UpdateStackPolicyTemplateStatus))).Methods(http.MethodPatch) + // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) r.PathPrefix("/").Handler(httpSwagger.WrapHandler).Methods(http.MethodGet) @@ -205,32 +387,13 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa return handlers.CORS(credentials, headersOk, originsOk, methodsOk)(r) } -func loggingMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - r = r.WithContext(context.WithValue(ctx, internal.ContextKeyRequestID, uuid.New().String())) - - log.InfoWithContext(r.Context(), fmt.Sprintf("***** START [%s %s] ***** ", r.Method, r.RequestURI)) - - body, err := io.ReadAll(r.Body) - if err == nil { - log.InfoWithContext(r.Context(), fmt.Sprintf("REQUEST BODY : %s", bytes.NewBuffer(body).String())) - } - r.Body = io.NopCloser(bytes.NewBuffer(body)) - - next.ServeHTTP(w, r) - - log.InfofWithContext(r.Context(), "***** END [%s %s] *****", r.Method, r.RequestURI) - }) -} - /* func transactionMiddleware(db *gorm.DB) mux.MiddlewareFunc { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() txHandle := db.Begin() - log.DebugWithContext(r.Context(),"beginning database transaction") + log.Debug(r.Context(),"beginning database transaction") defer func() { if r := recover(); r != nil { @@ -247,12 +410,12 @@ func transactionMiddleware(db *gorm.DB) mux.MiddlewareFunc { next.ServeHTTP(recorder, r) if StatusInList(recorder.Status, []int{http.StatusOK}) { - log.DebugWithContext(r.Context(),"committing transactions") + log.Debug(r.Context(),"committing transactions") if err := txHandle.Commit().Error; err != nil { - log.DebugWithContext(r.Context(),"trx commit error: ", err) + log.Debug(r.Context(),"trx commit error: ", err) } } else { - log.DebugWithContext(r.Context(),"rolling back transaction due to status code: ", recorder.Status) + log.Debug(r.Context(),"rolling back transaction due to status code: ", recorder.Status) txHandle.Rollback() } }) diff --git a/internal/serializer/serializer.go b/internal/serializer/serializer.go index 70e96552..6003de3b 100644 --- a/internal/serializer/serializer.go +++ b/internal/serializer/serializer.go @@ -1,11 +1,12 @@ package serializer import ( + "context" "fmt" "reflect" "github.com/google/uuid" - "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/pkg/log" ) @@ -16,7 +17,7 @@ type compositeKey struct { dstType reflect.Type } -func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) error { +func recursiveMap(ctx context.Context, src interface{}, dst interface{}, converterMap ConverterMap) error { srcVal := reflect.ValueOf(src) srcType := srcVal.Type() @@ -40,8 +41,16 @@ func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) e dstField.Set(srcField) continue } else if srcField.Type().Kind() == reflect.Struct && dstField.Type().Kind() == reflect.Struct { - if err := recursiveMap(srcField.Interface(), dstField.Addr().Interface(), converterMap); err != nil { - return err + if err := recursiveMap(ctx, srcField.Interface(), dstField.Addr().Interface(), converterMap); err != nil { + log.Error(ctx, err) + continue + } + } else if srcField.Kind() == reflect.Pointer && dstField.Type().Kind() == reflect.Struct { + if !reflect.Value.IsNil(srcField) { + if err := recursiveMap(ctx, srcField.Elem().Interface(), dstField.Addr().Interface(), converterMap); err != nil { + log.Error(ctx, err) + continue + } } } else { if functionExists(srcField.Interface(), "String") && @@ -66,12 +75,13 @@ func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) e converterKey := compositeKey{srcType: srcField.Type(), dstType: dstField.Type()} if converter, ok := converterMap[converterKey]; ok { if converted, err := converter(srcField.Interface()); err != nil { - return err + log.Error(ctx, err) + continue } else { dstField.Set(reflect.ValueOf(converted)) } } else { - log.Debugf("no converter found for %s -> %s", srcField.Type(), dstField.Type()) + //log.Debugf(ctx, "no converter found for %s -> %s", srcField.Type(), dstField.Type()) continue } } @@ -85,14 +95,20 @@ func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) e } } */ - + } else { + if fieldName == "Model" { + if err := recursiveMap(ctx, srcField.Interface(), dst, converterMap); err != nil { + return err + } + } } + } return nil } -func Map(src interface{}, dst interface{}) error { - return recursiveMap(src, dst, ConverterMap{ +func Map(ctx context.Context, src interface{}, dst interface{}) error { + return recursiveMap(ctx, src, dst, ConverterMap{ {srcType: reflect.TypeOf((*uuid.UUID)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { return i.(uuid.UUID).String(), nil }, @@ -100,11 +116,11 @@ func Map(src interface{}, dst interface{}) error { val, _ := uuid.Parse(i.(string)) return val, nil }, - {srcType: reflect.TypeOf((*domain.Role)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(domain.Role).Name, nil + {srcType: reflect.TypeOf((*model.Role)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { + return i.(model.Role).Name, nil }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*domain.Role)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return domain.Role{Name: i.(string)}, nil + {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*model.Role)(nil)).Elem()}: func(i interface{}) (interface{}, error) { + return model.Role{Name: i.(string)}, nil }, }) } diff --git a/internal/usecase/alert.go b/internal/usecase/alert.go deleted file mode 100644 index 99fafa9c..00000000 --- a/internal/usecase/alert.go +++ /dev/null @@ -1,283 +0,0 @@ -package usecase - -import ( - "context" - "encoding/json" - "fmt" - "strings" - "time" - - "github.com/openinfradev/tks-api/internal/middleware/auth/request" - - "github.com/google/uuid" - "github.com/openinfradev/tks-api/internal/pagination" - "github.com/openinfradev/tks-api/internal/repository" - "github.com/openinfradev/tks-api/pkg/domain" - "github.com/openinfradev/tks-api/pkg/httpErrors" - "github.com/openinfradev/tks-api/pkg/log" - "github.com/pkg/errors" - "gorm.io/gorm" -) - -type IAlertUsecase interface { - Get(ctx context.Context, alertId uuid.UUID) (domain.Alert, error) - GetByName(ctx context.Context, organizationId string, name string) (domain.Alert, error) - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.Alert, error) - Create(ctx context.Context, dto domain.CreateAlertRequest) (err error) - Update(ctx context.Context, dto domain.Alert) error - Delete(ctx context.Context, dto domain.Alert) error - - CreateAlertAction(ctx context.Context, dto domain.AlertAction) (alertActionId uuid.UUID, err error) -} - -type AlertUsecase struct { - repo repository.IAlertRepository - clusterRepo repository.IClusterRepository - organizationRepo repository.IOrganizationRepository - appGroupRepo repository.IAppGroupRepository -} - -func NewAlertUsecase(r repository.Repository) IAlertUsecase { - return &AlertUsecase{ - repo: r.Alert, - clusterRepo: r.Cluster, - appGroupRepo: r.AppGroup, - organizationRepo: r.Organization, - } -} - -func (u *AlertUsecase) Create(ctx context.Context, input domain.CreateAlertRequest) (err error) { - if input.Alerts == nil || len(input.Alerts) == 0 { - return fmt.Errorf("No data found") - } - - allClusters, err := u.clusterRepo.Fetch(nil) - if err != nil { - return fmt.Errorf("No clusters") - } - - for _, alert := range input.Alerts { - clusterId := alert.Labels.TacoCluster - organizationId, err := u.getOrganizationFromCluster(&allClusters, clusterId) - if err != nil { - log.ErrorWithContext(ctx, err) - continue - } - - organization, err := u.organizationRepo.Get(organizationId) - if err != nil { - log.ErrorWithContext(ctx, err) - continue - } - primaryCluster, err := u.clusterRepo.Get(domain.ClusterId(organization.PrimaryClusterId)) - if err != nil { - log.ErrorWithContext(ctx, err) - continue - } - - rawData, err := json.Marshal(alert) - if err != nil { - rawData = []byte{} - } - - /* - target := "" - // discriminative 에 target 에 대한 정보가 있다. - // discriminative: $labels.taco_cluster, $labels.instance - discriminative := alert.Annotations.Discriminative - if discriminative != "" { - trimed := strings.TrimLeft(discriminative, " ") - trimed = strings.TrimLeft(trimed, "$") - arr := strings.Split(trimed, ",") - - for _, refer := range arr { - - } - } - */ - - node := "" - if strings.Contains(alert.Labels.AlertName, "node") { - node = alert.Labels.Instance - } - - dto := domain.Alert{ - OrganizationId: organizationId, - Name: alert.Labels.AlertName, - Code: alert.Labels.AlertName, - Grade: alert.Labels.Severity, - Node: node, - Message: alert.Annotations.Message, - Description: alert.Annotations.Description, - CheckPoint: alert.Annotations.Checkpoint, - Summary: alert.Annotations.Summary, - ClusterId: domain.ClusterId(clusterId), - GrafanaUrl: u.makeGrafanaUrl(ctx, primaryCluster, alert, domain.ClusterId(clusterId)), - RawData: rawData, - } - - _, err = u.repo.Create(dto) - if err != nil { - continue - } - } - - return nil -} - -func (u *AlertUsecase) Update(ctx context.Context, dto domain.Alert) error { - return nil -} - -func (u *AlertUsecase) Get(ctx context.Context, alertId uuid.UUID) (alert domain.Alert, err error) { - alert, err = u.repo.Get(alertId) - if err != nil { - return alert, err - } - u.makeAdditionalInfo(&alert) - - return -} - -func (u *AlertUsecase) GetByName(ctx context.Context, organizationId string, name string) (out domain.Alert, err error) { - out, err = u.repo.GetByName(organizationId, name) - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return out, httpErrors.NewNotFoundError(err, "", "") - } - return out, err - } - return -} - -func (u *AlertUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (alerts []domain.Alert, err error) { - alerts, err = u.repo.Fetch(organizationId, pg) - if err != nil { - return nil, err - } - - for i := range alerts { - u.makeAdditionalInfo(&alerts[i]) - } - - return alerts, nil -} - -func (u *AlertUsecase) Delete(ctx context.Context, dto domain.Alert) (err error) { - user, ok := request.UserFrom(ctx) - if !ok { - return httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") - } - - _, err = u.Get(ctx, dto.ID) - if err != nil { - return httpErrors.NewNotFoundError(err, "AL_NOT_FOUND_ALERT", "") - } - - *dto.UpdatorId = user.GetUserId() - - err = u.repo.Delete(dto) - if err != nil { - return err - } - - return nil -} - -func (u *AlertUsecase) CreateAlertAction(ctx context.Context, dto domain.AlertAction) (alertActionId uuid.UUID, err error) { - user, ok := request.UserFrom(ctx) - if !ok { - return uuid.Nil, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") - } - - _, err = u.repo.Get(dto.AlertId) - if err != nil { - return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Not found alert"), "AL_NOT_FOUND_ALERT", "") - } - - userId := user.GetUserId() - dto.TakerId = &userId - dto.CreatedAt = time.Now() - - alertActionId, err = u.repo.CreateAlertAction(dto) - if err != nil { - return uuid.Nil, err - } - log.InfoWithContext(ctx, "newly created alertActionId:", alertActionId) - - return -} - -func (u *AlertUsecase) getOrganizationFromCluster(clusters *[]domain.Cluster, strId string) (organizationId string, err error) { - clusterId := domain.ClusterId(strId) - if !clusterId.Validate() { - return "", fmt.Errorf("Invalid clusterId %s", strId) - } - - for _, cluster := range *clusters { - if cluster.ID == clusterId { - return cluster.OrganizationId, nil - } - } - - return "", fmt.Errorf("No martched organization %s", strId) -} - -func (u *AlertUsecase) makeAdditionalInfo(alert *domain.Alert) { - alert.FiredAt = &alert.CreatedAt - //alert.Status = domain.AlertActionStatus_CREATED - - if len(alert.AlertActions) > 0 { - alert.TakedAt = &alert.AlertActions[0].CreatedAt - for _, action := range alert.AlertActions { - if action.Status == domain.AlertActionStatus_CLOSED { - alert.ClosedAt = &action.CreatedAt - alert.ProcessingSec = int((action.CreatedAt).Sub(alert.CreatedAt).Seconds()) - } - } - - alert.LastTaker = alert.AlertActions[len(alert.AlertActions)-1].Taker - alert.TakedSec = int((alert.AlertActions[0].CreatedAt).Sub(alert.CreatedAt).Seconds()) - //alert.Status = alert.AlertActions[len(alert.AlertActions)-1].Status - } -} - -func (u *AlertUsecase) makeGrafanaUrl(ctx context.Context, primaryCluster domain.Cluster, alert domain.CreateAlertRequestAlert, clusterId domain.ClusterId) (url string) { - primaryGrafanaEndpoint := "" - appGroups, err := u.appGroupRepo.Fetch(primaryCluster.ID, nil) - if err == nil { - for _, appGroup := range appGroups { - if appGroup.AppGroupType == domain.AppGroupType_LMA { - applications, err := u.appGroupRepo.GetApplications(appGroup.ID, domain.ApplicationType_GRAFANA) - if err != nil { - return "" - } - if len(applications) > 0 { - primaryGrafanaEndpoint = applications[0].Endpoint - } - } - } - } - - // check type - url = primaryGrafanaEndpoint - - // tks_node_dashboard/tks-kubernetes-view-nodes?orgId=1&refresh=30s&var-datasource=default&var-taco_cluster=c19rjkn4j&var-job=prometheus-node-exporter&var-hostname=All&var-node=10.0.168.71:9100&var-device=All&var-maxmount=%2F&var-show_hostname=prometheus-node-exporter-xt4vb - - switch alert.Labels.AlertName { - case "node-memory-high-utilization": - url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" - case "node-cpu-high-load": - url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" - case "node-disk-full": - url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" - case "pod-restart-frequently": - url = primaryGrafanaEndpoint + "/d/tks_podv1_dashboard/tks-kubernetes-view-pods-v1?var-taco_cluster=" + clusterId.String() + "&kiosk" - case "pvc-full": - url = primaryGrafanaEndpoint + "/d/tks_cluster_dashboard/tks-kubernetes-view-cluster-global?var-taco_cluster=" + clusterId.String() + "&kiosk" - default: - log.ErrorfWithContext(ctx, "Invalid alert name %s", alert.Labels.AlertName) - } - - return -} diff --git a/internal/usecase/app-group.go b/internal/usecase/app-group.go index f6a2d7b4..9d9490d5 100644 --- a/internal/usecase/app-group.go +++ b/internal/usecase/app-group.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" @@ -17,12 +18,12 @@ import ( ) type IAppGroupUsecase interface { - Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) ([]domain.AppGroup, error) - Create(ctx context.Context, dto domain.AppGroup) (id domain.AppGroupId, err error) - Get(ctx context.Context, id domain.AppGroupId) (out domain.AppGroup, err error) + Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) ([]model.AppGroup, error) + Create(ctx context.Context, dto model.AppGroup) (id domain.AppGroupId, err error) + Get(ctx context.Context, id domain.AppGroupId) (out model.AppGroup, err error) Delete(ctx context.Context, id domain.AppGroupId) (err error) - GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []domain.Application, err error) - UpdateApplication(ctx context.Context, dto domain.Application) (err error) + GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []model.Application, err error) + UpdateApplication(ctx context.Context, dto model.Application) (err error) } type AppGroupUsecase struct { @@ -41,15 +42,15 @@ func NewAppGroupUsecase(r repository.Repository, argoClient argowf.ArgoClient) I } } -func (u *AppGroupUsecase) Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) (out []domain.AppGroup, err error) { - out, err = u.repo.Fetch(clusterId, pg) +func (u *AppGroupUsecase) Fetch(ctx context.Context, clusterId domain.ClusterId, pg *pagination.Pagination) (out []model.AppGroup, err error) { + out, err = u.repo.Fetch(ctx, clusterId, pg) if err != nil { return nil, err } return } -func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id domain.AppGroupId, err error) { +func (u *AppGroupUsecase) Create(ctx context.Context, dto model.AppGroup) (id domain.AppGroupId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") @@ -57,12 +58,12 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d userId := user.GetUserId() dto.CreatorId = &userId - cluster, err := u.clusterRepo.Get(dto.ClusterId) + cluster, err := u.clusterRepo.Get(ctx, dto.ClusterId) if err != nil { return "", httpErrors.NewBadRequestError(err, "AG_NOT_FOUND_CLUSTER", "") } - resAppGroups, err := u.repo.Fetch(dto.ClusterId, nil) + resAppGroups, err := u.repo.Fetch(ctx, dto.ClusterId, nil) if err != nil { return "", httpErrors.NewBadRequestError(err, "AG_NOT_FOUND_APPGROUP", "") } @@ -79,10 +80,11 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d // check cloudAccount tksCloudAccountId := "" - tksObjectStore := "minio" + //tksObjectStore := "minio" // FOR TEST + tksObjectStore := "s3" if cluster.CloudService != domain.CloudService_BYOH { tksObjectStore = "s3" - cloudAccounts, err := u.cloudAccountRepo.Fetch(cluster.OrganizationId, nil) + cloudAccounts, err := u.cloudAccountRepo.Fetch(ctx, cluster.OrganizationId, nil) if err != nil { return "", httpErrors.NewBadRequestError(fmt.Errorf("Failed to get cloudAccounts"), "", "") } @@ -106,9 +108,9 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d } if dto.ID == "" { - dto.ID, err = u.repo.Create(dto) + dto.ID, err = u.repo.Create(ctx, dto) } else { - err = u.repo.Update(dto) + err = u.repo.Update(ctx, dto) } if err != nil { return "", httpErrors.NewInternalServerError(err, "AG_FAILED_TO_CREATE_APPGROUP", "") @@ -126,7 +128,7 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d "app_group_id=" + dto.ID.String(), "keycloak_url=" + strings.TrimSuffix(viper.GetString("keycloak-address"), "/auth"), "console_url=" + viper.GetString("console-address"), - "alert_tks=" + viper.GetString("external-address") + "/system-api/1.0/alerts", + "alert_tks=" + viper.GetString("external-address") + "/system-api/1.0/system-notifications", "alert_slack=" + viper.GetString("alert-slack"), "cloud_account_id=" + tksCloudAccountId, "object_store=" + tksObjectStore, @@ -141,37 +143,37 @@ func (u *AppGroupUsecase) Create(ctx context.Context, dto domain.AppGroup) (id d workflowTemplate = "tks-service-mesh" default: - log.ErrorWithContext(ctx, "invalid appGroup type ", dto.AppGroupType.String()) + log.Error(ctx, "invalid appGroup type ", dto.AppGroupType.String()) return "", errors.Wrap(err, fmt.Sprintf("Invalid appGroup type. %s", dto.AppGroupType.String())) } - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflowTemplate, opts) + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflowTemplate, opts) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return "", httpErrors.NewInternalServerError(err, "AG_FAILED_TO_CALL_WORKFLOW", "") } - if err := u.repo.InitWorkflow(dto.ID, workflowId, domain.AppGroupStatus_INSTALLING); err != nil { + if err := u.repo.InitWorkflow(ctx, dto.ID, workflowId, domain.AppGroupStatus_INSTALLING); err != nil { return "", errors.Wrap(err, "Failed to initialize appGroup status") } return dto.ID, nil } -func (u *AppGroupUsecase) Get(ctx context.Context, id domain.AppGroupId) (out domain.AppGroup, err error) { - appGroup, err := u.repo.Get(id) +func (u *AppGroupUsecase) Get(ctx context.Context, id domain.AppGroupId) (out model.AppGroup, err error) { + appGroup, err := u.repo.Get(ctx, id) if err != nil { - return domain.AppGroup{}, err + return model.AppGroup{}, err } return appGroup, nil } func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err error) { - appGroup, err := u.repo.Get(id) + appGroup, err := u.repo.Get(ctx, id) if err != nil { return fmt.Errorf("No appGroup for deletiing : %s", id) } - cluster, err := u.clusterRepo.Get(appGroup.ClusterId) + cluster, err := u.clusterRepo.Get(ctx, appGroup.ClusterId) if err != nil { return httpErrors.NewBadRequestError(err, "AG_NOT_FOUND_CLUSTER", "") } @@ -179,10 +181,11 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err // check cloudAccount tksCloudAccountId := "" - tksObjectStore := "minio" + //tksObjectStore := "minio" // FOR TEST + tksObjectStore := "s3" if cluster.CloudService != domain.CloudService_BYOH { tksObjectStore = "s3" - cloudAccounts, err := u.cloudAccountRepo.Fetch(cluster.OrganizationId, nil) + cloudAccounts, err := u.cloudAccountRepo.Fetch(ctx, cluster.OrganizationId, nil) if err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("Failed to get cloudAccounts"), "", "") } @@ -234,14 +237,14 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err "object_store=" + tksObjectStore, } - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflowTemplate, opts) + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflowTemplate, opts) if err != nil { return fmt.Errorf("Failed to call argo workflow : %s", err) } - log.DebugWithContext(ctx, "submited workflow name : ", workflowId) + log.Debug(ctx, "submited workflow name : ", workflowId) - if err := u.repo.InitWorkflow(id, workflowId, domain.AppGroupStatus_DELETING); err != nil { + if err := u.repo.InitWorkflow(ctx, id, workflowId, domain.AppGroupStatus_DELETING); err != nil { return fmt.Errorf("Failed to initialize appGroup status. err : %s", err) } @@ -255,16 +258,16 @@ func (u *AppGroupUsecase) Delete(ctx context.Context, id domain.AppGroupId) (err return nil } -func (u *AppGroupUsecase) GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []domain.Application, err error) { - out, err = u.repo.GetApplications(id, applicationType) +func (u *AppGroupUsecase) GetApplications(ctx context.Context, id domain.AppGroupId, applicationType domain.ApplicationType) (out []model.Application, err error) { + out, err = u.repo.GetApplications(ctx, id, applicationType) if err != nil { return nil, err } return } -func (u *AppGroupUsecase) UpdateApplication(ctx context.Context, dto domain.Application) (err error) { - err = u.repo.UpsertApplication(dto) +func (u *AppGroupUsecase) UpdateApplication(ctx context.Context, dto model.Application) (err error) { + err = u.repo.UpsertApplication(ctx, dto) if err != nil { return err } diff --git a/internal/usecase/app-serve-app.go b/internal/usecase/app-serve-app.go index 38e0b702..033f3aae 100644 --- a/internal/usecase/app-serve-app.go +++ b/internal/usecase/app-serve-app.go @@ -13,31 +13,34 @@ import ( "github.com/spf13/viper" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/openinfradev/tks-api/internal/kubernetes" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" "github.com/openinfradev/tks-api/pkg/log" ) type IAppServeAppUsecase interface { - CreateAppServeApp(app *domain.AppServeApp) (appId string, taskId string, err error) - GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) ([]domain.AppServeApp, error) - GetAppServeAppById(appId string) (*domain.AppServeApp, error) - GetAppServeAppLatestTask(appId string) (*domain.AppServeAppTask, error) - GetNumOfAppsOnStack(organizationId string, clusterId string) (int64, error) - IsAppServeAppExist(appId string) (bool, error) - IsAppServeAppNameExist(orgId string, appName string) (bool, error) - IsAppServeAppNamespaceExist(clusterId string, namespace string) (bool, error) - UpdateAppServeAppStatus(appId string, taskId string, status string, output string) (ret string, err error) - DeleteAppServeApp(appId string) (res string, err error) - UpdateAppServeApp(app *domain.AppServeApp, appTask *domain.AppServeAppTask) (ret string, err error) - UpdateAppServeAppEndpoint(appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) (string, error) - PromoteAppServeApp(appId string) (ret string, err error) - AbortAppServeApp(appId string) (ret string, err error) - RollbackAppServeApp(appId string, taskId string) (ret string, err error) + CreateAppServeApp(ctx context.Context, app *model.AppServeApp, task *model.AppServeAppTask) (appId string, taskId string, err error) + GetAppServeApps(ctx context.Context, organizationId string, projectId string, showAll bool, pg *pagination.Pagination) ([]model.AppServeApp, error) + GetAppServeAppById(ctx context.Context, appId string) (*model.AppServeApp, error) + GetAppServeAppTasks(ctx context.Context, appId string, pg *pagination.Pagination) ([]model.AppServeAppTask, error) + GetAppServeAppTaskById(ctx context.Context, taskId string) (*model.AppServeAppTask, error) + GetAppServeAppLatestTask(ctx context.Context, appId string) (*model.AppServeAppTask, error) + GetNumOfAppsOnStack(ctx context.Context, organizationId string, clusterId string) (int64, error) + IsAppServeAppExist(ctx context.Context, appId string) (bool, error) + IsAppServeAppNameExist(ctx context.Context, orgId string, appName string) (bool, error) + IsAppServeAppNamespaceExist(ctx context.Context, clusterId string, namespace string) (bool, error) + UpdateAppServeAppStatus(ctx context.Context, appId string, taskId string, status string, output string) (ret string, err error) + DeleteAppServeApp(ctx context.Context, appId string) (res string, err error) + UpdateAppServeApp(ctx context.Context, appId string, appTask *model.AppServeAppTask) (ret string, err error) + UpdateAppServeAppEndpoint(ctx context.Context, appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) (string, error) + PromoteAppServeApp(ctx context.Context, appId string) (ret string, err error) + AbortAppServeApp(ctx context.Context, appId string) (ret string, err error) + RollbackAppServeApp(ctx context.Context, appId string, taskId string) (ret string, err error) } type AppServeAppUsecase struct { @@ -56,7 +59,7 @@ func NewAppServeAppUsecase(r repository.Repository, argoClient argowf.ArgoClient } } -func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, string, error) { +func (u *AppServeAppUsecase) CreateAppServeApp(ctx context.Context, app *model.AppServeApp, task *model.AppServeAppTask) (string, string, error) { if app == nil { return "", "", fmt.Errorf("invalid app obj") } @@ -66,29 +69,29 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, // (Refer to 'tks-appserve-template') if app.Type != "deploy" { // Validate param - if app.AppServeAppTasks[0].ArtifactUrl == "" { + if task.ArtifactUrl == "" { return "", "", fmt.Errorf("error: For 'build'/'all' type task, 'artifact_url' is mandatory param") } // Construct imageUrl - imageUrl := viper.GetString("image-registry-url") + "/" + app.Name + "-" + app.TargetClusterId + ":" + app.AppServeAppTasks[0].Version - app.AppServeAppTasks[0].ImageUrl = imageUrl + imageUrl := viper.GetString("image-registry-url") + "/" + app.Name + "-" + app.TargetClusterId + ":" + task.Version + task.ImageUrl = imageUrl if app.AppType == "springboot" { // Construct executable_path - artiUrl := app.AppServeAppTasks[0].ArtifactUrl + artiUrl := task.ArtifactUrl tempArr := strings.Split(artiUrl, "/") exeFilename := tempArr[len(tempArr)-1] executablePath := "/usr/src/myapp/" + exeFilename - app.AppServeAppTasks[0].ExecutablePath = executablePath + task.ExecutablePath = executablePath } } else { // Validate param for 'deploy' type. // TODO: check params for legacy spring app case if app.AppType == "springboot" { - if app.AppServeAppTasks[0].ImageUrl == "" || app.AppServeAppTasks[0].ExecutablePath == "" || - app.AppServeAppTasks[0].Profile == "" || app.AppServeAppTasks[0].ResourceSpec == "" { + if task.ImageUrl == "" || task.ExecutablePath == "" || + task.Profile == "" || task.ResourceSpec == "" { return "", "", fmt.Errorf("Error: For 'deploy' type task, the following params must be provided." + @@ -97,18 +100,18 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, } } - extEnv := app.AppServeAppTasks[0].ExtraEnv + extEnv := task.ExtraEnv if extEnv != "" { /* Preprocess extraEnv param */ - log.Debug("extraEnv received: ", extEnv) + log.Debug(ctx, "extraEnv received: ", extEnv) tempMap := map[string]string{} err := json.Unmarshal([]byte(extEnv), &tempMap) if err != nil { - log.Error(err) + log.Error(ctx, err) return "", "", errors.Wrap(err, "Failed to process extraEnv param.") } - log.Debugf("extraEnv marshalled: %v", tempMap) + log.Debugf(ctx, "extraEnv marshalled: %v", tempMap) newExtEnv := map[string]string{} for key, val := range tempMap { @@ -119,15 +122,21 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, mJson, _ := json.Marshal(newExtEnv) extEnv = string(mJson) - log.Debug("After transform, extraEnv: ", extEnv) + log.Debug(ctx, "After transform, extraEnv: ", extEnv) } - appId, taskId, err := u.repo.CreateAppServeApp(app) + appId, err := u.repo.CreateAppServeApp(ctx, app) if err != nil { - log.Error(err) + log.Error(ctx, err) return "", "", errors.Wrap(err, "Failed to create app.") } + taskId, err := u.repo.CreateTask(ctx, task, appId) + if err != nil { + log.Error(ctx, err) + return "", "", errors.Wrap(err, "Failed to create task.") + } + fmt.Printf("appId = %s, taskId = %s", appId, taskId) // TODO: Validate PV params @@ -138,56 +147,57 @@ func (u *AppServeAppUsecase) CreateAppServeApp(app *domain.AppServeApp) (string, opts := argowf.SubmitOptions{} opts.Parameters = []string{ "type=" + app.Type, - "strategy=" + app.AppServeAppTasks[0].Strategy, + "strategy=" + task.Strategy, "app_type=" + app.AppType, "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, "namespace=" + app.Namespace, "asa_id=" + appId, "asa_task_id=" + taskId, - "artifact_url=" + app.AppServeAppTasks[0].ArtifactUrl, - "image_url=" + app.AppServeAppTasks[0].ImageUrl, - "port=" + app.AppServeAppTasks[0].Port, - "profile=" + app.AppServeAppTasks[0].Profile, + "artifact_url=" + task.ArtifactUrl, + "image_url=" + task.ImageUrl, + "port=" + task.Port, + "profile=" + task.Profile, "extra_env=" + extEnv, - "app_config=" + app.AppServeAppTasks[0].AppConfig, - "app_secret=" + app.AppServeAppTasks[0].AppSecret, - "resource_spec=" + app.AppServeAppTasks[0].ResourceSpec, - "executable_path=" + app.AppServeAppTasks[0].ExecutablePath, + "app_config=" + task.AppConfig, + "app_secret=" + task.AppSecret, + "resource_spec=" + task.ResourceSpec, + "executable_path=" + task.ExecutablePath, "git_repo_url=" + viper.GetString("git-repository-url"), "harbor_pw_secret=" + viper.GetString("harbor-pw-secret"), - "pv_enabled=" + strconv.FormatBool(app.AppServeAppTasks[0].PvEnabled), - "pv_storage_class=" + app.AppServeAppTasks[0].PvStorageClass, - "pv_access_mode=" + app.AppServeAppTasks[0].PvAccessMode, - "pv_size=" + app.AppServeAppTasks[0].PvSize, - "pv_mount_path=" + app.AppServeAppTasks[0].PvMountPath, + "pv_enabled=" + strconv.FormatBool(task.PvEnabled), + "pv_storage_class=" + task.PvStorageClass, + "pv_access_mode=" + task.PvAccessMode, + "pv_size=" + task.PvSize, + "pv_mount_path=" + task.PvMountPath, "tks_api_url=" + viper.GetString("external-address"), } - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, opts) + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, opts) if err != nil { - log.Error(err) + log.Error(ctx, err) return "", "", errors.Wrap(err, fmt.Sprintf("failed to submit workflow. %s", workflow)) } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) return appId, app.Name, nil } -func (u *AppServeAppUsecase) GetAppServeApps(organizationId string, showAll bool, pg *pagination.Pagination) ([]domain.AppServeApp, error) { - apps, err := u.repo.GetAppServeApps(organizationId, showAll, pg) +func (u *AppServeAppUsecase) GetAppServeApps(ctx context.Context, organizationId string, projectId string, showAll bool, pg *pagination.Pagination) ([]model.AppServeApp, error) { + apps, err := u.repo.GetAppServeApps(ctx, organizationId, projectId, showAll, pg) if err != nil { - fmt.Println(apps) + log.Debugf(ctx, "Apps: [%v]", apps) } return apps, nil } -func (u *AppServeAppUsecase) GetAppServeAppById(appId string) (*domain.AppServeApp, error) { - asa, err := u.repo.GetAppServeAppById(appId) +func (u *AppServeAppUsecase) GetAppServeAppById(ctx context.Context, appId string) (*model.AppServeApp, error) { + asa, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return nil, err } @@ -195,24 +205,27 @@ func (u *AppServeAppUsecase) GetAppServeAppById(appId string) (*domain.AppServeA /************************ * Construct grafana URL * ************************/ - organization, err := u.organizationRepo.Get(asa.OrganizationId) + organization, err := u.organizationRepo.Get(ctx, asa.OrganizationId) if err != nil { return asa, httpErrors.NewInternalServerError(errors.Wrap(err, fmt.Sprintf("Failed to get organization for app %s", asa.Name)), "S_FAILED_FETCH_ORGANIZATION", "") } - appGroupsInPrimaryCluster, err := u.appGroupRepo.Fetch(domain.ClusterId(organization.PrimaryClusterId), nil) + // Get app groups in primary clustser + appGroupsInPrimaryCluster, err := u.appGroupRepo.Fetch(ctx, domain.ClusterId(organization.PrimaryClusterId), nil) if err != nil { return asa, err } for _, appGroup := range appGroupsInPrimaryCluster { if appGroup.AppGroupType == domain.AppGroupType_LMA { - applications, err := u.appGroupRepo.GetApplications(appGroup.ID, domain.ApplicationType_GRAFANA) + applications, err := u.appGroupRepo.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) if err != nil { return asa, err } if len(applications) > 0 { - asa.GrafanaUrl = applications[0].Endpoint + "/d/tks_appserving_dashboard/tks-appserving-dashboard?refresh=30s&var-cluster=" + asa.TargetClusterId + "&var-kubernetes_namespace_name=" + asa.Namespace + "&var-kubernetes_pod_name=All&var-kubernetes_container_name=main&var-TopK=10" + // TODO: revert refresh param once the bug is resolved + asa.GrafanaUrl = applications[0].Endpoint + "/d/tks_appserving_dashboard/tks-appserving-dashboard?refresh=30s&var-cluster=" + asa.TargetClusterId + "&var-kubernetes_namespace_name=" + asa.Namespace + "&var-kubernetes_pod_name=All&var-kubernetes_container_name=main&var-TopK=10&refresh=30m" + log.Debugf(ctx, "Found grafanaURL: %s", asa.GrafanaUrl) } } } @@ -220,8 +233,18 @@ func (u *AppServeAppUsecase) GetAppServeAppById(appId string) (*domain.AppServeA return asa, nil } -func (u *AppServeAppUsecase) GetAppServeAppLatestTask(appId string) (*domain.AppServeAppTask, error) { - task, err := u.repo.GetAppServeAppLatestTask(appId) +func (u *AppServeAppUsecase) GetAppServeAppTasks(ctx context.Context, appId string, pg *pagination.Pagination) ([]model.AppServeAppTask, error) { + tasks, err := u.repo.GetAppServeAppTasksByAppId(ctx, appId, pg) + if err != nil { + log.Debugf(ctx, "Error while getting task list. Tasks: %v", tasks) + return nil, err + } + + return tasks, nil +} + +func (u *AppServeAppUsecase) GetAppServeAppTaskById(ctx context.Context, taskId string) (*model.AppServeAppTask, error) { + task, err := u.repo.GetAppServeAppTaskById(ctx, taskId) if err != nil { return nil, err } @@ -229,8 +252,17 @@ func (u *AppServeAppUsecase) GetAppServeAppLatestTask(appId string) (*domain.App return task, nil } -func (u *AppServeAppUsecase) GetNumOfAppsOnStack(organizationId string, clusterId string) (int64, error) { - numApps, err := u.repo.GetNumOfAppsOnStack(organizationId, clusterId) +func (u *AppServeAppUsecase) GetAppServeAppLatestTask(ctx context.Context, appId string) (*model.AppServeAppTask, error) { + task, err := u.repo.GetAppServeAppLatestTask(ctx, appId) + if err != nil { + return nil, err + } + + return task, nil +} + +func (u *AppServeAppUsecase) GetNumOfAppsOnStack(ctx context.Context, organizationId string, clusterId string) (int64, error) { + numApps, err := u.repo.GetNumOfAppsOnStack(ctx, organizationId, clusterId) if err != nil { return -1, err } @@ -238,8 +270,8 @@ func (u *AppServeAppUsecase) GetNumOfAppsOnStack(organizationId string, clusterI return numApps, nil } -func (u *AppServeAppUsecase) IsAppServeAppExist(appId string) (bool, error) { - count, err := u.repo.IsAppServeAppExist(appId) +func (u *AppServeAppUsecase) IsAppServeAppExist(ctx context.Context, appId string) (bool, error) { + count, err := u.repo.IsAppServeAppExist(ctx, appId) if err != nil { return false, err } @@ -251,8 +283,8 @@ func (u *AppServeAppUsecase) IsAppServeAppExist(appId string) (bool, error) { return false, nil } -func (u *AppServeAppUsecase) IsAppServeAppNameExist(orgId string, appName string) (bool, error) { - count, err := u.repo.IsAppServeAppNameExist(orgId, appName) +func (u *AppServeAppUsecase) IsAppServeAppNameExist(ctx context.Context, orgId string, appName string) (bool, error) { + count, err := u.repo.IsAppServeAppNameExist(ctx, orgId, appName) if err != nil { return false, err } @@ -264,65 +296,63 @@ func (u *AppServeAppUsecase) IsAppServeAppNameExist(orgId string, appName string return false, nil } -func (u *AppServeAppUsecase) IsAppServeAppNamespaceExist(clusterId string, new_ns string) (bool, error) { - clientset, err := kubernetes.GetClientFromClusterId(clusterId) +func (u *AppServeAppUsecase) IsAppServeAppNamespaceExist(ctx context.Context, clusterId string, new_ns string) (bool, error) { + clientset, err := kubernetes.GetClientFromClusterId(ctx, clusterId) if err != nil { - log.Error(err) + log.Error(ctx, err) return false, err } namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { - log.Error(err) + log.Error(ctx, err) return false, err } for _, ns := range namespaces.Items { if new_ns == ns.ObjectMeta.Name { - log.Debugf("Namespace %s already exists.", new_ns) + log.Debugf(ctx, "Namespace %s already exists.", new_ns) return true, nil } } - log.Debugf("Namespace %s is available", new_ns) + log.Debugf(ctx, "Namespace %s is available", new_ns) return false, nil } -func (u *AppServeAppUsecase) UpdateAppServeAppStatus( - appId string, - taskId string, - status string, +func (u *AppServeAppUsecase) UpdateAppServeAppStatus(ctx context.Context, appId string, taskId string, status string, output string) (string, error) { - log.Info("Starting status update process..") + log.Info(ctx, "Starting status update process..") - err := u.repo.UpdateStatus(appId, taskId, status, output) + err := u.repo.UpdateStatus(ctx, appId, taskId, status, output) if err != nil { - log.Info("appId = ", appId) - log.Info("taskId = ", taskId) + log.Info(ctx, "appId = ", appId) + log.Info(ctx, "taskId = ", taskId) return "", fmt.Errorf("failed to update app status. Err: %s", err) } return fmt.Sprintf("The appId '%s' status is being updated.", appId), nil } func (u *AppServeAppUsecase) UpdateAppServeAppEndpoint( + ctx context.Context, appId string, taskId string, endpoint string, previewEndpoint string, helmRevision int32) (string, error) { - log.Info("Starting endpoint update process..") + log.Info(ctx, "Starting endpoint update process..") - err := u.repo.UpdateEndpoint(appId, taskId, endpoint, previewEndpoint, helmRevision) + err := u.repo.UpdateEndpoint(ctx, appId, taskId, endpoint, previewEndpoint, helmRevision) if err != nil { - log.Info("appId = ", appId) - log.Info("taskId = ", taskId) + log.Info(ctx, "appId = ", appId) + log.Info(ctx, "taskId = ", taskId) return "", fmt.Errorf("failed to update endpoint. Err: %s", err) } return fmt.Sprintf("The appId '%s' endpoint is being updated.", appId), nil } -func (u *AppServeAppUsecase) DeleteAppServeApp(appId string) (res string, err error) { - app, err := u.repo.GetAppServeAppById(appId) +func (u *AppServeAppUsecase) DeleteAppServeApp(ctx context.Context, appId string) (res string, err error) { + app, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return "", fmt.Errorf("error while getting ASA Info from DB. Err: %s", err) } @@ -340,38 +370,52 @@ func (u *AppServeAppUsecase) DeleteAppServeApp(appId string) (res string, err er /******************** * Start delete task * ********************/ + latestTask, err := u.repo.GetAppServeAppLatestTask(ctx, appId) + if err != nil { + return "", err + } - appTask := &domain.AppServeAppTask{ + verInt, err := strconv.Atoi(latestTask.Version) + if err != nil { + return "", errors.Wrap(err, "Failed to convert version to integer.") + } + newVerStr := strconv.Itoa(verInt + 1) + + // Temp Debug + log.Debugf(ctx, "Old version: %s", latestTask.Version) + log.Debugf(ctx, "New version: %s", newVerStr) + + appTask := &model.AppServeAppTask{ AppServeAppId: app.ID, - Version: strconv.Itoa(len(app.AppServeAppTasks) + 1), + Version: newVerStr, ArtifactUrl: "", - ImageUrl: app.AppServeAppTasks[0].ImageUrl, + ImageUrl: latestTask.ImageUrl, Status: "DELETING", - Profile: app.AppServeAppTasks[0].Profile, + Profile: "", Output: "", CreatedAt: time.Now(), } - taskId, err := u.repo.CreateTask(appTask) + taskId, err := u.repo.CreateTask(ctx, appTask, "") if err != nil { - log.Error("taskId = ", taskId) - log.Error("Failed to create delete task. Err:", err) + log.Error(ctx, "taskId = ", taskId) + log.Error(ctx, "Failed to create delete task. Err:", err) return "", errors.Wrap(err, "Failed to create delete task.") } - log.Info("Updating app status to 'DELETING'..") + log.Info(ctx, "Updating app status to 'DELETING'..") - err = u.repo.UpdateStatus(appId, taskId, "DELETING", "") + err = u.repo.UpdateStatus(ctx, appId, taskId, "DELETING", "") if err != nil { - log.Debug("appId = ", appId) - log.Debug("taskId = ", taskId) + log.Debug(ctx, "appId = ", appId) + log.Debug(ctx, "taskId = ", taskId) return "", fmt.Errorf("failed to update app status on DeleteAppServeApp. Err: %s", err) } workflow := "delete-java-app" - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, @@ -379,35 +423,36 @@ func (u *AppServeAppUsecase) DeleteAppServeApp(appId string) (res string, err er "asa_id=" + app.ID, "asa_task_id=" + taskId, "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "tks_api_url=" + viper.GetString("external-address"), }, }) if err != nil { - log.Error("Failed to submit workflow. Err:", err) + log.Error(ctx, "Failed to submit workflow. Err:", err) return "", errors.Wrap(err, "Failed to submit workflow.") } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) return fmt.Sprintf("The app %s is being deleted. "+ "Confirm result by checking the app status after a while.", app.Name), nil } -func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask *domain.AppServeAppTask) (ret string, err error) { +func (u *AppServeAppUsecase) UpdateAppServeApp(ctx context.Context, appId string, appTask *model.AppServeAppTask) (ret string, err error) { if appTask == nil { return "", errors.New("invalid parameters. appTask is nil") } - app_, err := u.repo.GetAppServeAppById(app.ID) + app, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return "", fmt.Errorf("error while getting ASA Info from DB. Err: %s", err) } // Block update if the app's current status is one of those. - if app_.Status == "PROMOTE_WAIT" || app_.Status == "PROMOTING" || app_.Status == "ABORTING" { + if app.Status == "PROMOTE_WAIT" || app.Status == "PROMOTING" || app.Status == "ABORTING" { return "승인대기 또는 프로모트 작업 중에는 업그레이드를 수행할 수 없습니다", fmt.Errorf("Update not possible. The app is waiting for promote or in the middle of promote process.") } - log.Info("Starting normal update process..") + log.Info(ctx, "Starting normal update process..") // TODO: for more strict validation, check if immutable fields are provided by user // and those values are changed or not. (name, type, app_type, target_cluster) @@ -437,15 +482,15 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask extEnv := appTask.ExtraEnv if extEnv != "" { /* Preprocess extraEnv param */ - log.Debug("extraEnv received: ", extEnv) + log.Debug(ctx, "extraEnv received: ", extEnv) tempMap := map[string]string{} err = json.Unmarshal([]byte(extEnv), &tempMap) if err != nil { - log.Error(err) + log.Error(ctx, err) return "", errors.Wrap(err, "Failed to process extraEnv param.") } - log.Debugf("extraEnv marshalled: %v", tempMap) + log.Debugf(ctx, "extraEnv marshalled: %v", tempMap) newExtEnv := map[string]string{} for key, val := range tempMap { @@ -456,36 +501,38 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask mJson, _ := json.Marshal(newExtEnv) extEnv = string(mJson) - log.Debug("After transform, extraEnv: ", extEnv) + log.Debug(ctx, "After transform, extraEnv: ", extEnv) } - taskId, err := u.repo.CreateTask(appTask) + // TODO: Check if appId is necessary here. + taskId, err := u.repo.CreateTask(ctx, appTask, appId) if err != nil { - log.Info("taskId = ", taskId) + log.Info(ctx, "taskId = ", taskId) return "", fmt.Errorf("failed to update app-serve application. Err: %s", err) } // Sync new task status to the parent app - log.Info("Updating app status to 'PREPARING'..") + log.Info(ctx, "Updating app status to 'PREPARING'..") - err = u.repo.UpdateStatus(app.ID, taskId, "PREPARING", "") + err = u.repo.UpdateStatus(ctx, app.ID, taskId, "PREPARING", "") if err != nil { - log.Debug("appId = ", app.ID) - log.Debug("taskId = ", taskId) + log.Debug(ctx, "appId = ", app.ID) + log.Debug(ctx, "taskId = ", taskId) return "", fmt.Errorf("failed to update app status on UpdateAppServeApp. Err: %s", err) } // Call argo workflow workflow := "serve-java-app" - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ "type=" + app.Type, "strategy=" + appTask.Strategy, "app_type=" + app.AppType, "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, "namespace=" + app.Namespace, @@ -511,10 +558,10 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask }, }) if err != nil { - log.Error("Failed to submit workflow. Err:", err) + log.Error(ctx, "Failed to submit workflow. Err:", err) return "", fmt.Errorf("failed to submit workflow. Err: %s", err) } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) var message string if appTask.Strategy == "rolling-update" { @@ -526,8 +573,8 @@ func (u *AppServeAppUsecase) UpdateAppServeApp(app *domain.AppServeApp, appTask return message, nil } -func (u *AppServeAppUsecase) PromoteAppServeApp(appId string) (ret string, err error) { - app, err := u.repo.GetAppServeAppById(appId) +func (u *AppServeAppUsecase) PromoteAppServeApp(ctx context.Context, appId string) (ret string, err error) { + app, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return "", fmt.Errorf("error while getting ASA Info from DB. Err: %s", err) } @@ -537,28 +584,34 @@ func (u *AppServeAppUsecase) PromoteAppServeApp(appId string) (ret string, err e } // Get the latest task ID so that the task status can be modified inside workflow once the promotion is done. - latestTaskId := app.AppServeAppTasks[0].ID - strategy := app.AppServeAppTasks[0].Strategy - log.Info("latestTaskId = ", latestTaskId) - log.Info("strategy = ", strategy) + latestTask, err := u.repo.GetAppServeAppLatestTask(ctx, appId) + if err != nil { + return "", err + } + + latestTaskId := latestTask.ID + strategy := latestTask.Strategy + log.Debug(ctx, "latestTaskId = ", latestTaskId) + log.Debug(ctx, "strategy = ", strategy) - log.Info("Updating app status to 'PROMOTING'..") + log.Info(ctx, "Updating app status to 'PROMOTING'..") - err = u.repo.UpdateStatus(appId, latestTaskId, "PROMOTING", "") + err = u.repo.UpdateStatus(ctx, appId, latestTaskId, "PROMOTING", "") if err != nil { - log.Debug("appId = ", appId) - log.Debug("taskId = ", latestTaskId) + log.Debug(ctx, "appId = ", appId) + log.Debug(ctx, "taskId = ", latestTaskId) return "", fmt.Errorf("failed to update app status on PromoteAppServeApp. Err: %s", err) } // Call argo workflow workflow := "promote-java-app" - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, "namespace=" + app.Namespace, @@ -569,17 +622,17 @@ func (u *AppServeAppUsecase) PromoteAppServeApp(appId string) (ret string, err e }, }) if err != nil { - log.Error("failed to submit workflow. Err:", err) + log.Error(ctx, "failed to submit workflow. Err:", err) return "", fmt.Errorf("failed to submit workflow. Err: %s", err) } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) return fmt.Sprintf("The app '%s' is being promoted. "+ "Confirm result by checking the app status after a while.", app.Name), nil } -func (u *AppServeAppUsecase) AbortAppServeApp(appId string) (ret string, err error) { - app, err := u.repo.GetAppServeAppById(appId) +func (u *AppServeAppUsecase) AbortAppServeApp(ctx context.Context, appId string) (ret string, err error) { + app, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return "", fmt.Errorf("error while getting ASA Info from DB. Err: %s", err) } @@ -589,102 +642,118 @@ func (u *AppServeAppUsecase) AbortAppServeApp(appId string) (ret string, err err } // Get the latest task ID so that the task status can be modified inside workflow once the abort process is done. - latestTaskId := app.AppServeAppTasks[0].ID - strategy := app.AppServeAppTasks[0].Strategy - log.Info("latestTaskId = ", latestTaskId) - log.Info("strategy = ", strategy) + latestTask, err := u.repo.GetAppServeAppLatestTask(ctx, appId) + if err != nil { + return "", err + } - log.Info("Updating app status to 'ABORTING'..") + latestTaskId := latestTask.ID + log.Debug(ctx, "latestTaskId = ", latestTaskId) + log.Info(ctx, "Updating app status to 'ABORTING'..") - err = u.repo.UpdateStatus(appId, latestTaskId, "ABORTING", "") + err = u.repo.UpdateStatus(ctx, appId, latestTaskId, "ABORTING", "") if err != nil { - log.Debug("appId = ", appId) - log.Debug("taskId = ", latestTaskId) + log.Debug(ctx, "appId = ", appId) + log.Debug(ctx, "taskId = ", latestTaskId) return "", fmt.Errorf("failed to update app status on AbortAppServeApp. Err: %s", err) } // Call argo workflow workflow := "abort-java-app" - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) // Call argo workflow - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, "namespace=" + app.Namespace, "asa_id=" + app.ID, "asa_task_id=" + latestTaskId, - "strategy=" + strategy, "tks_api_url=" + viper.GetString("external-address"), }, }) if err != nil { return "", fmt.Errorf("failed to submit workflow. Err: %s", err) } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) return fmt.Sprintf("The app '%s' is being promoted. "+ "Confirm result by checking the app status after a while.", app.Name), nil } -func (u *AppServeAppUsecase) RollbackAppServeApp(appId string, taskId string) (ret string, err error) { - log.Info("Starting rollback process..") +func (u *AppServeAppUsecase) RollbackAppServeApp(ctx context.Context, appId string, taskId string) (ret string, err error) { + log.Info(ctx, "Starting rollback process..") - app, err := u.repo.GetAppServeAppById(appId) + app, err := u.repo.GetAppServeAppById(ctx, appId) if err != nil { return "", err } // Find target(dest) task - var task domain.AppServeAppTask - for _, t := range app.AppServeAppTasks { - if t.ID == taskId { - task = t - break - } + task, err := u.repo.GetAppServeAppTaskById(ctx, taskId) + if err != nil { + return "", err + } + + if task.AppServeAppId != appId { + return "", fmt.Errorf("Rollback target task doesn't belong to current app. It belongs to: %s", task.AppServeAppId) + } + + // Find latest task for version info + latestTask, err := u.repo.GetAppServeAppLatestTask(ctx, appId) + if err != nil { + return "", err } // Save target version targetVer := task.Version targetRev := task.HelmRevision + verInt, err := strconv.Atoi(latestTask.Version) + if err != nil { + return "", errors.Wrap(err, "Failed to convert version to integer.") + } + newVerStr := strconv.Itoa(verInt + 1) + // Insert new values to the target task object task.ID = "" task.Output = "" task.Status = "ROLLBACKING" - task.Version = strconv.Itoa(len(app.AppServeAppTasks) + 1) + task.Version = newVerStr task.CreatedAt = time.Now() task.UpdatedAt = nil task.HelmRevision = 0 task.RollbackVersion = targetVer // Creates new task record from the target task - newTaskId, err := u.repo.CreateTask(&task) + newTaskId, err := u.repo.CreateTask(ctx, task, "") if err != nil { - log.Info("taskId = ", newTaskId) + log.Info(ctx, "taskId = ", newTaskId) return "", fmt.Errorf("failed to rollback app-serve application. Err: %s", err) } - log.Info("Updating app status to 'ROLLBACKING'..") + log.Info(ctx, "Updating app status to 'ROLLBACKING'..") - err = u.repo.UpdateStatus(appId, newTaskId, "ROLLBACKING", "") + err = u.repo.UpdateStatus(ctx, appId, newTaskId, "ROLLBACKING", "") if err != nil { - log.Debug("appId = ", appId) - log.Debug("taskId = ", newTaskId) + log.Debug(ctx, "appId = ", appId) + log.Debug(ctx, "taskId = ", newTaskId) return "", fmt.Errorf("failed to update app status on RollbackAppServeApp. Err: %s", err) } // Call argo workflow workflow := "rollback-java-app" - log.Info("Submitting workflow: ", workflow) + log.Info(ctx, "Submitting workflow: ", workflow) - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ "organization_id=" + app.OrganizationId, + "project_id=" + app.ProjectId, "target_cluster_id=" + app.TargetClusterId, "app_name=" + app.Name, "namespace=" + app.Namespace, @@ -695,10 +764,10 @@ func (u *AppServeAppUsecase) RollbackAppServeApp(appId string, taskId string) (r }, }) if err != nil { - log.Error("Failed to submit workflow. Err:", err) + log.Error(ctx, "Failed to submit workflow. Err:", err) return "", fmt.Errorf("failed to submit workflow. Err: %s", err) } - log.Info("Successfully submitted workflow: ", workflowId) + log.Info(ctx, "Successfully submitted workflow: ", workflowId) return fmt.Sprintf("Rollback app Request '%v' is successfully submitted", taskId), nil } diff --git a/internal/usecase/audit.go b/internal/usecase/audit.go new file mode 100644 index 00000000..4e9ec1a3 --- /dev/null +++ b/internal/usecase/audit.go @@ -0,0 +1,91 @@ +package usecase + +import ( + "context" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/httpErrors" +) + +type IAuditUsecase interface { + Get(ctx context.Context, auditId uuid.UUID) (model.Audit, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.Audit, error) + Create(ctx context.Context, dto model.Audit) (auditId uuid.UUID, err error) + Delete(ctx context.Context, dto model.Audit) error +} + +type AuditUsecase struct { + repo repository.IAuditRepository + userRepo repository.IUserRepository + organizationRepo repository.IOrganizationRepository +} + +func NewAuditUsecase(r repository.Repository) IAuditUsecase { + return &AuditUsecase{ + repo: r.Audit, + userRepo: r.User, + organizationRepo: r.Organization, + } +} + +func (u *AuditUsecase) Create(ctx context.Context, dto model.Audit) (auditId uuid.UUID, err error) { + if dto.OrganizationId != "" { + organization, err := u.organizationRepo.Get(ctx, dto.OrganizationId) + if err == nil { + dto.OrganizationName = organization.Name + } + } + + if dto.UserId != nil && *dto.UserId != uuid.Nil { + user, err := u.userRepo.GetByUuid(ctx, *dto.UserId) + if err != nil { + return auditId, err + } + + userRoles := "" + for i, role := range user.Roles { + if i > 0 { + userRoles = userRoles + "," + } + userRoles = userRoles + role.Name + } + dto.OrganizationId = user.Organization.ID + dto.OrganizationName = user.Organization.Name + dto.UserAccountId = user.AccountId + dto.UserName = user.Name + dto.UserRoles = userRoles + } + + auditId, err = u.repo.Create(ctx, dto) + if err != nil { + return uuid.Nil, httpErrors.NewInternalServerError(err, "", "") + } + return auditId, nil +} + +func (u *AuditUsecase) Get(ctx context.Context, auditId uuid.UUID) (res model.Audit, err error) { + res, err = u.repo.Get(ctx, auditId) + if err != nil { + return model.Audit{}, err + } + return +} + +func (u *AuditUsecase) Fetch(ctx context.Context, pg *pagination.Pagination) (audits []model.Audit, err error) { + audits, err = u.repo.Fetch(ctx, pg) + if err != nil { + return nil, err + } + return +} + +func (u *AuditUsecase) Delete(ctx context.Context, dto model.Audit) (err error) { + err = u.repo.Delete(ctx, dto.ID) + if err != nil { + return httpErrors.NewNotFoundError(err, "", "") + } + return nil +} diff --git a/internal/usecase/auth.go b/internal/usecase/auth.go index 3455505b..f8edf598 100644 --- a/internal/usecase/auth.go +++ b/internal/usecase/auth.go @@ -1,6 +1,7 @@ package usecase import ( + "context" "crypto/rand" "crypto/tls" "encoding/base64" @@ -11,34 +12,32 @@ import ( "strings" "time" - jwtWithouKey "github.com/dgrijalva/jwt-go" - "github.com/openinfradev/tks-api/pkg/log" "github.com/spf13/viper" "golang.org/x/net/html" "golang.org/x/oauth2" "github.com/Nerzal/gocloak/v13" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/mail" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" ) type IAuthUsecase interface { - Login(accountId string, password string, organizationId string) (domain.User, error) - Logout(accessToken string, organizationId string) error - PingToken(accessToken string, organizationId string) error - FindId(code string, email string, userName string, organizationId string) (string, error) - FindPassword(code string, accountId string, email string, userName string, organizationId string) error - VerifyIdentity(accountId string, email string, userName string, organizationId string) error - FetchRoles() (out []domain.Role, err error) - SingleSignIn(organizationId, accountId, password string) ([]*http.Cookie, error) - SingleSignOut(organizationId string) (string, []*http.Cookie, error) + Login(ctx context.Context, accountId string, password string, organizationId string) (model.User, error) + Logout(ctx context.Context, sessionId string, organizationId string) error + FindId(ctx context.Context, code string, email string, userName string, organizationId string) (string, error) + FindPassword(ctx context.Context, code string, accountId string, email string, userName string, organizationId string) error + VerifyIdentity(ctx context.Context, accountId string, email string, userName string, organizationId string) error + SingleSignIn(ctx context.Context, organizationId, accountId, password string) ([]*http.Cookie, error) + SingleSignOut(ctx context.Context, organizationId string) (string, []*http.Cookie, error) + VerifyToken(ctx context.Context, token string) (bool, error) + UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error } const ( @@ -67,25 +66,23 @@ func NewAuthUsecase(r repository.Repository, kc keycloak.IKeycloak) IAuthUsecase } } -func (u *AuthUsecase) Login(accountId string, password string, organizationId string) (domain.User, error) { +func (u *AuthUsecase) Login(ctx context.Context, accountId string, password string, organizationId string) (model.User, error) { // Authentication with DB - user, err := u.userRepository.Get(accountId, organizationId) + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { - return domain.User{}, httpErrors.NewBadRequestError(err, "A_INVALID_ID", "") - } - if !helper.CheckPasswordHash(user.Password, password) { - return domain.User{}, httpErrors.NewBadRequestError(fmt.Errorf("Mismatch password"), "A_INVALID_PASSWORD", "") - } - var accountToken *domain.User - // Authentication with Keycloak - if organizationId == "master" && accountId == "admin" { - accountToken, err = u.kc.LoginAdmin(accountId, password) - } else { - accountToken, err = u.kc.Login(accountId, password, organizationId) + return model.User{}, httpErrors.NewBadRequestError(err, "A_INVALID_ID", "") } + + var accountToken *model.User + accountToken, err = u.kc.Login(ctx, accountId, password, organizationId) if err != nil { - //TODO: implement not found handling - return domain.User{}, err + apiErr, ok := err.(*gocloak.APIError) + if ok { + if apiErr.Code == 401 { + return model.User{}, httpErrors.NewBadRequestError(fmt.Errorf("Mismatch password"), "A_INVALID_PASSWORD", "") + } + } + return model.User{}, httpErrors.NewInternalServerError(err, "", "") } // Insert token @@ -98,69 +95,17 @@ func (u *AuthUsecase) Login(accountId string, password string, organizationId st return user, nil } -func (u *AuthUsecase) Logout(accessToken string, organizationName string) error { +func (u *AuthUsecase) Logout(ctx context.Context, sessionId string, organizationName string) error { // [TODO] refresh token 을 추가하고, session timeout 을 줄이는 방향으로 고려할 것 - err := u.kc.Logout(accessToken, organizationName) - if err != nil { - return err - } - return nil -} - -func (u *AuthUsecase) PingToken(accessToken string, organizationId string) error { - parsedToken, _, err := new(jwtWithouKey.Parser).ParseUnverified(accessToken, jwtWithouKey.MapClaims{}) - if err != nil { - return err - } - - if parsedToken.Method.Alg() != "RS256" { - return fmt.Errorf("invalid token") - } - - if parsedToken.Claims.Valid() != nil { - return fmt.Errorf("invalid token") - } - - if err := u.kc.VerifyAccessToken(accessToken, organizationId); err != nil { - log.Errorf("failed to verify access token: %v", err) - return err - } - - userId, err := uuid.Parse(parsedToken.Claims.(jwtWithouKey.MapClaims)["sub"].(string)) + err := u.kc.Logout(ctx, sessionId, organizationName) if err != nil { - log.Errorf("failed to verify access token: %v", err) - return err } - requestSessionId, ok := parsedToken.Claims.(jwtWithouKey.MapClaims)["sid"].(string) - if !ok { - return fmt.Errorf("session id is not found in token") - } - - sessionIds, err := u.kc.GetSessions(userId.String(), organizationId) - if err != nil { - log.Errorf("failed to get sessions: %v", err) - - return err - } - if len(*sessionIds) == 0 { - return fmt.Errorf("invalid session") - } - var matched bool = false - for _, id := range *sessionIds { - if id == requestSessionId { - matched = true - break - } - } - if !matched { - return fmt.Errorf("invalid session") - } return nil } -func (u *AuthUsecase) FindId(code string, email string, userName string, organizationId string) (string, error) { - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), +func (u *AuthUsecase) FindId(ctx context.Context, code string, email string, userName string, organizationId string) (string, error) { + users, err := u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.NameFilter(userName), u.userRepository.EmailFilter(email)) if err != nil && users == nil { return "", httpErrors.NewBadRequestError(err, "A_INVALID_ID", "") @@ -168,11 +113,7 @@ func (u *AuthUsecase) FindId(code string, email string, userName string, organiz if err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return "", httpErrors.NewInternalServerError(err, "", "") - } - emailCode, err := u.authRepository.GetEmailCode(userUuid) + emailCode, err := u.authRepository.GetEmailCode(ctx, (*users)[0].ID) if err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } @@ -182,15 +123,15 @@ func (u *AuthUsecase) FindId(code string, email string, userName string, organiz if emailCode.Code != code { return "", httpErrors.NewBadRequestError(fmt.Errorf("invalid code"), "A_INVALID_CODE", "") } - if err := u.authRepository.DeleteEmailCode(userUuid); err != nil { + if err := u.authRepository.DeleteEmailCode(ctx, (*users)[0].ID); err != nil { return "", httpErrors.NewInternalServerError(err, "", "") } return (*users)[0].AccountId, nil } -func (u *AuthUsecase) FindPassword(code string, accountId string, email string, userName string, organizationId string) error { - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), +func (u *AuthUsecase) FindPassword(ctx context.Context, code string, accountId string, email string, userName string, organizationId string) error { + users, err := u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.AccountIdFilter(accountId), u.userRepository.NameFilter(userName), u.userRepository.EmailFilter(email)) if err != nil && users == nil { @@ -200,11 +141,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, return httpErrors.NewInternalServerError(err, "", "") } user := (*users)[0] - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - emailCode, err := u.authRepository.GetEmailCode(userUuid) + emailCode, err := u.authRepository.GetEmailCode(ctx, user.ID) if err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -216,7 +153,7 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, } randomPassword := helper.GenerateRandomString(passwordLength) - originUser, err := u.kc.GetUser(organizationId, accountId) + originUser, err := u.kc.GetUser(ctx, organizationId, accountId) if err != nil { return httpErrors.NewInternalServerError(err, "", "") } @@ -227,45 +164,42 @@ func (u *AuthUsecase) FindPassword(code string, accountId string, email string, Temporary: gocloak.BoolP(false), }, } - if err = u.kc.UpdateUser(organizationId, originUser); err != nil { + if err = u.kc.UpdateUser(ctx, organizationId, originUser); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - if user.Password, err = helper.HashPassword(randomPassword); err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - if err = u.userRepository.UpdatePassword(userUuid, organizationId, user.Password, true); err != nil { + if err = u.userRepository.UpdatePasswordAt(ctx, user.ID, organizationId, true); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - message, err := mail.MakeTemporaryPasswordMessage(email, organizationId, accountId, randomPassword) + message, err := mail.MakeTemporaryPasswordMessage(ctx, email, organizationId, accountId, randomPassword) if err != nil { - log.Errorf("mail.MakeVerityIdentityMessage error. %v", err) + log.Errorf(ctx, "mail.MakeVerityIdentityMessage error. %v", err) return httpErrors.NewInternalServerError(err, "", "") } mailer := mail.New(message) - if err := mailer.SendMail(); err != nil { + if err := mailer.SendMail(ctx); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - if err = u.authRepository.DeleteEmailCode(userUuid); err != nil { + if err = u.authRepository.DeleteEmailCode(ctx, user.ID); err != nil { return httpErrors.NewInternalServerError(err, "", "") } return nil } -func (u *AuthUsecase) VerifyIdentity(accountId string, email string, userName string, organizationId string) error { - var users *[]domain.User +func (u *AuthUsecase) VerifyIdentity(ctx context.Context, accountId string, email string, userName string, organizationId string) error { + var users *[]model.User var err error if accountId == "" { - users, err = u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), + users, err = u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.NameFilter(userName), u.userRepository.EmailFilter(email)) } else { - users, err = u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), + users, err = u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.AccountIdFilter(accountId), u.userRepository.NameFilter(userName), u.userRepository.EmailFilter(email)) } @@ -276,51 +210,39 @@ func (u *AuthUsecase) VerifyIdentity(accountId string, email string, userName st return httpErrors.NewInternalServerError(err, "", "") } - code, err := helper.GenerateEmailCode() - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - userUuid, err := uuid.Parse((*users)[0].ID) + code, err := helper.GenerateEmailCode(ctx) if err != nil { return httpErrors.NewInternalServerError(err, "", "") } - _, err = u.authRepository.GetEmailCode(userUuid) + _, err = u.authRepository.GetEmailCode(ctx, (*users)[0].ID) if err != nil { - if err := u.authRepository.CreateEmailCode(userUuid, code); err != nil { + if err := u.authRepository.CreateEmailCode(ctx, (*users)[0].ID, code); err != nil { return httpErrors.NewInternalServerError(err, "", "") } } else { - if err := u.authRepository.UpdateEmailCode(userUuid, code); err != nil { + if err := u.authRepository.UpdateEmailCode(ctx, (*users)[0].ID, code); err != nil { return httpErrors.NewInternalServerError(err, "", "") } } - message, err := mail.MakeVerityIdentityMessage(email, code) + message, err := mail.MakeVerityIdentityMessage(ctx, email, code) if err != nil { - log.Errorf("mail.MakeVerityIdentityMessage error. %v", err) + log.Errorf(ctx, "mail.MakeVerityIdentityMessage error. %v", err) return httpErrors.NewInternalServerError(err, "", "") } mailer := mail.New(message) - if err := mailer.SendMail(); err != nil { - log.Errorf("mailer.SendMail error. %v", err) + if err := mailer.SendMail(ctx); err != nil { + log.Errorf(ctx, "mailer.SendMail error. %v", err) return httpErrors.NewInternalServerError(err, "", "") } return nil } -func (u *AuthUsecase) FetchRoles() (out []domain.Role, err error) { - roles, err := u.userRepository.FetchRoles() - if err != nil { - return nil, err - } - return *roles, nil -} - -func (u *AuthUsecase) SingleSignIn(organizationId, accountId, password string) ([]*http.Cookie, error) { - cookies, err := makingCookie(organizationId, accountId, password) +func (u *AuthUsecase) SingleSignIn(ctx context.Context, organizationId, accountId, password string) ([]*http.Cookie, error) { + cookies, err := makingCookie(ctx, organizationId, accountId, password) if err != nil { return nil, err } @@ -331,22 +253,22 @@ func (u *AuthUsecase) SingleSignIn(organizationId, accountId, password string) ( return cookies, nil } -func (u *AuthUsecase) SingleSignOut(organizationId string) (string, []*http.Cookie, error) { +func (u *AuthUsecase) SingleSignOut(ctx context.Context, organizationId string) (string, []*http.Cookie, error) { var redirectUrl string - organization, err := u.organizationRepository.Get(organizationId) + organization, err := u.organizationRepository.Get(ctx, organizationId) if err != nil { return "", nil, err } - appGroupsInPrimaryCluster, err := u.appgroupRepository.Fetch(domain.ClusterId(organization.PrimaryClusterId), nil) + appGroupsInPrimaryCluster, err := u.appgroupRepository.Fetch(ctx, domain.ClusterId(organization.PrimaryClusterId), nil) if err != nil { return "", nil, err } for _, appGroup := range appGroupsInPrimaryCluster { if appGroup.AppGroupType == domain.AppGroupType_LMA { - applications, err := u.appgroupRepository.GetApplications(appGroup.ID, domain.ApplicationType_GRAFANA) + applications, err := u.appgroupRepository.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) if err != nil { return "", nil, err } @@ -381,7 +303,37 @@ func (u *AuthUsecase) SingleSignOut(organizationId string) (string, []*http.Cook return redirectUrl, cookies, nil } -func (u *AuthUsecase) isExpiredEmailCode(code repository.CacheEmailCode) bool { +func (u *AuthUsecase) VerifyToken(ctx context.Context, token string) (bool, error) { + parsedToken, err := helper.StringToTokenWithoutVerification(token) + if err != nil { + return false, err + } + claims, err := helper.RetrieveClaims(parsedToken) + if err != nil { + return false, err + } + + org, ok := claims["organization"].(string) + if !ok { + return false, fmt.Errorf("organization is not found in token") + } + + isActive, err := u.kc.VerifyAccessToken(ctx, token, org) + if err != nil { + return false, err + } + if !isActive { + return false, nil + } + + return true, nil +} + +func (u *AuthUsecase) UpdateExpiredTimeOnToken(ctx context.Context, organizationId string, userId string) error { + return u.authRepository.UpdateExpiredTimeOnToken(ctx, organizationId, userId) +} + +func (u *AuthUsecase) isExpiredEmailCode(code model.CacheEmailCode) bool { return !helper.IsDurationExpired(code.UpdatedAt, internal.EmailCodeExpireTime) } @@ -415,7 +367,7 @@ func extractFormAction(htmlContent string) (string, error) { return f(doc), nil } -func makingCookie(organizationId, userName, password string) ([]*http.Cookie, error) { +func makingCookie(ctx context.Context, organizationId, userName, password string) ([]*http.Cookie, error) { stateCode, err := genStateString() if err != nil { return nil, err @@ -445,7 +397,7 @@ func makingCookie(organizationId, userName, password string) ([]*http.Cookie, er req.Header.Add("Accept", "text/html") resp, err := client.Do(req) if err != nil { - log.Errorf("Error while creating new request: %v", err) + log.Errorf(ctx, "Error while creating new request: %v", err) return nil, err } cookies := resp.Cookies() @@ -461,7 +413,7 @@ func makingCookie(organizationId, userName, password string) ([]*http.Cookie, er s, err := extractFormAction(htmlContent) if err != nil { - log.Errorf("Error while creating new request: %v", err) + log.Errorf(ctx, "Error while creating new request: %v", err) return nil, err } diff --git a/internal/usecase/auth_test.go b/internal/usecase/auth_test.go deleted file mode 100644 index 85a2c829..00000000 --- a/internal/usecase/auth_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package usecase_test - -import ( - "testing" -) - -func TestGet(t *testing.T) { - -} diff --git a/internal/usecase/cloud-account.go b/internal/usecase/cloud-account.go index a938066f..45853c1b 100644 --- a/internal/usecase/cloud-account.go +++ b/internal/usecase/cloud-account.go @@ -15,15 +15,15 @@ import ( "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" "github.com/aws/aws-sdk-go-v2/service/servicequotas" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/openinfradev/tks-api/internal/kubernetes" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" - "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" "github.com/openinfradev/tks-api/pkg/log" "github.com/pkg/errors" "gorm.io/gorm" @@ -32,15 +32,15 @@ import ( const MAX_WORKFLOW_TIME = 30 type ICloudAccountUsecase interface { - Get(ctx context.Context, cloudAccountId uuid.UUID) (domain.CloudAccount, error) - GetByName(ctx context.Context, organizationId string, name string) (domain.CloudAccount, error) - GetByAwsAccountId(ctx context.Context, awsAccountId string) (domain.CloudAccount, error) + Get(ctx context.Context, cloudAccountId uuid.UUID) (model.CloudAccount, error) + GetByName(ctx context.Context, organizationId string, name string) (model.CloudAccount, error) + GetByAwsAccountId(ctx context.Context, awsAccountId string) (model.CloudAccount, error) GetResourceQuota(ctx context.Context, cloudAccountId uuid.UUID) (available bool, out domain.ResourceQuota, err error) - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.CloudAccount, error) - Create(ctx context.Context, dto domain.CloudAccount) (cloudAccountId uuid.UUID, err error) - Update(ctx context.Context, dto domain.CloudAccount) error - Delete(ctx context.Context, dto domain.CloudAccount) error - DeleteForce(ctx context.Context, cloudAccountId uuid.UUID) error + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.CloudAccount, error) + Create(ctx context.Context, dto model.CloudAccount) (cloudAccountId uuid.UUID, err error) + Update(ctx context.Context, dto model.CloudAccount) error + Delete(ctx context.Context, dto model.CloudAccount) (model.CloudAccount, error) + DeleteForce(ctx context.Context, cloudAccountId uuid.UUID) (model.CloudAccount, error) } type CloudAccountUsecase struct { @@ -57,14 +57,15 @@ func NewCloudAccountUsecase(r repository.Repository, argoClient argowf.ArgoClien } } -func (u *CloudAccountUsecase) Create(ctx context.Context, dto domain.CloudAccount) (cloudAccountId uuid.UUID, err error) { +func (u *CloudAccountUsecase) Create(ctx context.Context, dto model.CloudAccount) (cloudAccountId uuid.UUID, err error) { user, ok := request.UserFrom(ctx) if !ok { return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } + userId := user.GetUserId() dto.Resource = "TODO server result or additional information" - dto.CreatorId = user.GetUserId() + dto.CreatorId = &userId _, err = u.GetByName(ctx, dto.OrganizationId, dto.Name) if err == nil { @@ -75,21 +76,22 @@ func (u *CloudAccountUsecase) Create(ctx context.Context, dto domain.CloudAccoun return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "", "사용 중인 AwsAccountId 입니다. 관리자에게 문의하세요.") } - cloudAccountId, err = u.repo.Create(dto) + cloudAccountId, err = u.repo.Create(ctx, dto) if err != nil { return uuid.Nil, httpErrors.NewInternalServerError(err, "", "") } - log.InfoWithContext(ctx, "newly created CloudAccount ID:", cloudAccountId) + log.Info(ctx, "newly created CloudAccount ID:", cloudAccountId) // FOR TEST. ADD MAGIC KEYWORD if strings.Contains(dto.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { - if err := u.repo.InitWorkflow(cloudAccountId, "", domain.CloudAccountStatus_CREATED); err != nil { + if err := u.repo.InitWorkflow(ctx, cloudAccountId, "", domain.CloudAccountStatus_CREATED); err != nil { return uuid.Nil, errors.Wrap(err, "Failed to initialize status") } return cloudAccountId, nil } workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "tks-create-aws-cloud-account", argowf.SubmitOptions{ Parameters: []string{ @@ -102,97 +104,100 @@ func (u *CloudAccountUsecase) Create(ctx context.Context, dto domain.CloudAccoun }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return uuid.Nil, fmt.Errorf("Failed to call argo workflow : %s", err) } - log.InfoWithContext(ctx, "submited workflow :", workflowId) + log.Info(ctx, "submited workflow :", workflowId) - if err := u.repo.InitWorkflow(cloudAccountId, workflowId, domain.CloudAccountStatus_CREATING); err != nil { + if err := u.repo.InitWorkflow(ctx, cloudAccountId, workflowId, domain.CloudAccountStatus_CREATING); err != nil { return uuid.Nil, errors.Wrap(err, "Failed to initialize status") } return cloudAccountId, nil } -func (u *CloudAccountUsecase) Update(ctx context.Context, dto domain.CloudAccount) error { +func (u *CloudAccountUsecase) Update(ctx context.Context, dto model.CloudAccount) error { user, ok := request.UserFrom(ctx) if !ok { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } + userId := user.GetUserId() dto.Resource = "TODO server result or additional information" - dto.UpdatorId = user.GetUserId() - err := u.repo.Update(dto) + dto.UpdatorId = &userId + err := u.repo.Update(ctx, dto) if err != nil { return httpErrors.NewInternalServerError(err, "", "") } return nil } -func (u *CloudAccountUsecase) Get(ctx context.Context, cloudAccountId uuid.UUID) (res domain.CloudAccount, err error) { - res, err = u.repo.Get(cloudAccountId) +func (u *CloudAccountUsecase) Get(ctx context.Context, cloudAccountId uuid.UUID) (res model.CloudAccount, err error) { + res, err = u.repo.Get(ctx, cloudAccountId) if err != nil { - return domain.CloudAccount{}, err + return model.CloudAccount{}, err } - res.Clusters = u.getClusterCnt(cloudAccountId) + res.Clusters = u.getClusterCnt(ctx, cloudAccountId) return } -func (u *CloudAccountUsecase) GetByName(ctx context.Context, organizationId string, name string) (res domain.CloudAccount, err error) { - res, err = u.repo.GetByName(organizationId, name) +func (u *CloudAccountUsecase) GetByName(ctx context.Context, organizationId string, name string) (res model.CloudAccount, err error) { + res, err = u.repo.GetByName(ctx, organizationId, name) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return domain.CloudAccount{}, httpErrors.NewNotFoundError(err, "", "") + return model.CloudAccount{}, httpErrors.NewNotFoundError(err, "", "") } - return domain.CloudAccount{}, err + return model.CloudAccount{}, err } - res.Clusters = u.getClusterCnt(res.ID) + res.Clusters = u.getClusterCnt(ctx, res.ID) return } -func (u *CloudAccountUsecase) GetByAwsAccountId(ctx context.Context, awsAccountId string) (res domain.CloudAccount, err error) { - res, err = u.repo.GetByAwsAccountId(awsAccountId) +func (u *CloudAccountUsecase) GetByAwsAccountId(ctx context.Context, awsAccountId string) (res model.CloudAccount, err error) { + res, err = u.repo.GetByAwsAccountId(ctx, awsAccountId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return domain.CloudAccount{}, httpErrors.NewNotFoundError(err, "", "") + return model.CloudAccount{}, httpErrors.NewNotFoundError(err, "", "") } - return domain.CloudAccount{}, err + return model.CloudAccount{}, err } - res.Clusters = u.getClusterCnt(res.ID) + res.Clusters = u.getClusterCnt(ctx, res.ID) return } -func (u *CloudAccountUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (cloudAccounts []domain.CloudAccount, err error) { - cloudAccounts, err = u.repo.Fetch(organizationId, pg) +func (u *CloudAccountUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (cloudAccounts []model.CloudAccount, err error) { + cloudAccounts, err = u.repo.Fetch(ctx, organizationId, pg) if err != nil { return nil, err } for i, cloudAccount := range cloudAccounts { - cloudAccounts[i].Clusters = u.getClusterCnt(cloudAccount.ID) + cloudAccounts[i].Clusters = u.getClusterCnt(ctx, cloudAccount.ID) } return } -func (u *CloudAccountUsecase) Delete(ctx context.Context, dto domain.CloudAccount) (err error) { +func (u *CloudAccountUsecase) Delete(ctx context.Context, dto model.CloudAccount) (out model.CloudAccount, err error) { user, ok := request.UserFrom(ctx) if !ok { - return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + return out, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } + userId := user.GetUserId() cloudAccount, err := u.Get(ctx, dto.ID) if err != nil { - return httpErrors.NewNotFoundError(err, "", "") + return cloudAccount, httpErrors.NewNotFoundError(err, "", "") } - dto.UpdatorId = user.GetUserId() + dto.UpdatorId = &userId - if u.getClusterCnt(dto.ID) > 0 { - return fmt.Errorf("사용 중인 클러스터가 있어 삭제할 수 없습니다.") + if u.getClusterCnt(ctx, dto.ID) > 0 { + return cloudAccount, fmt.Errorf("사용 중인 클러스터가 있어 삭제할 수 없습니다.") } workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "tks-delete-aws-cloud-account", argowf.SubmitOptions{ Parameters: []string{ @@ -205,50 +210,50 @@ func (u *CloudAccountUsecase) Delete(ctx context.Context, dto domain.CloudAccoun }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) - return fmt.Errorf("Failed to call argo workflow : %s", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) + return cloudAccount, fmt.Errorf("Failed to call argo workflow : %s", err) } - log.InfoWithContext(ctx, "submited workflow :", workflowId) + log.Info(ctx, "submited workflow :", workflowId) - if err := u.repo.InitWorkflow(dto.ID, workflowId, domain.CloudAccountStatus_DELETING); err != nil { - return errors.Wrap(err, "Failed to initialize status") + if err := u.repo.InitWorkflow(ctx, dto.ID, workflowId, domain.CloudAccountStatus_DELETING); err != nil { + return cloudAccount, errors.Wrap(err, "Failed to initialize status") } - return nil + return cloudAccount, nil } -func (u *CloudAccountUsecase) DeleteForce(ctx context.Context, cloudAccountId uuid.UUID) (err error) { - cloudAccount, err := u.repo.Get(cloudAccountId) +func (u *CloudAccountUsecase) DeleteForce(ctx context.Context, cloudAccountId uuid.UUID) (out model.CloudAccount, err error) { + cloudAccount, err := u.repo.Get(ctx, cloudAccountId) if err != nil { - return err + return cloudAccount, err } if !strings.Contains(cloudAccount.Name, domain.CLOUD_ACCOUNT_INCLUSTER) && cloudAccount.Status != domain.CloudAccountStatus_CREATE_ERROR { - return fmt.Errorf("The status is not CREATE_ERROR. %s", cloudAccount.Status) + return cloudAccount, fmt.Errorf("The status is not CREATE_ERROR. %s", cloudAccount.Status) } - if u.getClusterCnt(cloudAccountId) > 0 { - return fmt.Errorf("사용 중인 클러스터가 있어 삭제할 수 없습니다.") + if u.getClusterCnt(ctx, cloudAccountId) > 0 { + return cloudAccount, fmt.Errorf("사용 중인 클러스터가 있어 삭제할 수 없습니다.") } - err = u.repo.Delete(cloudAccountId) + err = u.repo.Delete(ctx, cloudAccountId) if err != nil { - return err + return cloudAccount, err } - return nil + return cloudAccount, nil } func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccountId uuid.UUID) (available bool, out domain.ResourceQuota, err error) { - cloudAccount, err := u.repo.Get(cloudAccountId) + cloudAccount, err := u.repo.Get(ctx, cloudAccountId) if err != nil { return false, out, err } - awsAccessKeyId, awsSecretAccessKey, _ := kubernetes.GetAwsSecret() + awsAccessKeyId, awsSecretAccessKey, _ := kubernetes.GetAwsSecret(ctx) if err != nil || awsAccessKeyId == "" || awsSecretAccessKey == "" { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return false, out, httpErrors.NewInternalServerError(fmt.Errorf("Invalid aws secret."), "", "") } @@ -259,13 +264,13 @@ func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccount }, })) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) } stsSvc := sts.NewFromConfig(cfg) if !strings.Contains(cloudAccount.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { - log.InfoWithContext(ctx, "Use assume role. awsAccountId : ", cloudAccount.AwsAccountId) + log.Info(ctx, "Use assume role. awsAccountId : ", cloudAccount.AwsAccountId) creds := stscreds.NewAssumeRoleProvider(stsSvc, "arn:aws:iam::"+cloudAccount.AwsAccountId+":role/controllers.cluster-api-provider-aws.sigs.k8s.io") cfg.Credentials = aws.NewCredentialsCache(creds) } @@ -354,7 +359,7 @@ func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccount o.Region = "ap-northeast-2" }) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return false, out, err } currentUsage.EIP = len(res.Addresses) @@ -365,7 +370,7 @@ func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccount if err != nil { return false, out, err } - log.DebugfWithContext(ctx, "%s %s %v", *res.Quota.QuotaName, *res.Quota.QuotaCode, *res.Quota.Value) + log.Debugf(ctx, "%s %s %v", *res.Quota.QuotaName, *res.Quota.QuotaCode, *res.Quota.Value) quotaValue := int(*res.Quota.Value) @@ -377,74 +382,74 @@ func (u *CloudAccountUsecase) GetResourceQuota(ctx context.Context, cloudAccount // Cluster 1 switch key { case "L-69A177A2": // NLB - log.InfofWithContext(ctx, "NLB : usage %d, quota %d", currentUsage.NLB, quotaValue) + log.Infof(ctx, "NLB : usage %d, quota %d", currentUsage.NLB, quotaValue) out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ Type: "NLB", Usage: currentUsage.NLB, Quota: quotaValue, Required: 5, }) - if quotaValue < currentUsage.NLB+5 { - available = false + if quotaValue >= currentUsage.NLB+5 { + available = true } case "L-E9E9831D": // Classic - log.InfofWithContext(ctx, "CLB : usage %d, quota %d", currentUsage.CLB, quotaValue) + log.Infof(ctx, "CLB : usage %d, quota %d", currentUsage.CLB, quotaValue) out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ Type: "CLB", Usage: currentUsage.CLB, Quota: quotaValue, Required: 1, }) - if quotaValue < currentUsage.CLB+1 { - available = false + if quotaValue >= currentUsage.CLB+1 { + available = true } case "L-A4707A72": // IGW - log.InfofWithContext(ctx, "IGW : usage %d, quota %d", currentUsage.IGW, quotaValue) + log.Infof(ctx, "IGW : usage %d, quota %d", currentUsage.IGW, quotaValue) out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ Type: "IGW", Usage: currentUsage.IGW, Quota: quotaValue, Required: 1, }) - if quotaValue < currentUsage.IGW+1 { - available = false + if quotaValue >= currentUsage.IGW+1 { + available = true } case "L-1194D53C": // Cluster - log.InfofWithContext(ctx, "Cluster : usage %d, quota %d", currentUsage.Cluster, quotaValue) + log.Infof(ctx, "Cluster : usage %d, quota %d", currentUsage.Cluster, quotaValue) out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ Type: "EKS", Usage: currentUsage.Cluster, Quota: quotaValue, Required: 1, }) - if quotaValue < currentUsage.Cluster+1 { - available = false + if quotaValue >= currentUsage.Cluster+1 { + available = true } case "L-0263D0A3": // Elastic IP - log.InfofWithContext(ctx, "Elastic IP : usage %d, quota %d", currentUsage.EIP, quotaValue) + log.Infof(ctx, "Elastic IP : usage %d, quota %d", currentUsage.EIP, quotaValue) out.Quotas = append(out.Quotas, domain.ResourceQuotaAttr{ Type: "EIP", Usage: currentUsage.EIP, Quota: quotaValue, Required: 3, }) - if quotaValue < currentUsage.EIP+3 { - available = false + if quotaValue >= currentUsage.EIP+3 { + available = true } } } //return fmt.Errorf("Always return err") - return true, out, nil + return available, out, nil } -func (u *CloudAccountUsecase) getClusterCnt(cloudAccountId uuid.UUID) (cnt int) { +func (u *CloudAccountUsecase) getClusterCnt(ctx context.Context, cloudAccountId uuid.UUID) (cnt int) { cnt = 0 - clusters, err := u.clusterRepo.FetchByCloudAccountId(cloudAccountId, nil) + clusters, err := u.clusterRepo.FetchByCloudAccountId(ctx, cloudAccountId, nil) if err != nil { - log.Error("Failed to get clusters by cloudAccountId. err : ", err) + log.Error(ctx, "Failed to get clusters by cloudAccountId. err : ", err) return cnt } diff --git a/internal/usecase/cluster.go b/internal/usecase/cluster.go index f298fff7..ce7574e8 100644 --- a/internal/usecase/cluster.go +++ b/internal/usecase/cluster.go @@ -8,36 +8,36 @@ import ( "strings" "time" + "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/internal/kubernetes" "github.com/openinfradev/tks-api/internal/middleware/auth/request" - byoh "github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/apis/infrastructure/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/serializer" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" "github.com/openinfradev/tks-api/pkg/log" gcache "github.com/patrickmn/go-cache" "github.com/pkg/errors" "github.com/spf13/viper" + byoh "github.com/vmware-tanzu/cluster-api-provider-bringyourownhost/apis/infrastructure/v1beta1" "gopkg.in/yaml.v3" "gorm.io/gorm" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type IClusterUsecase interface { WithTrx(*gorm.DB) IClusterUsecase - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.Cluster, error) - FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) - Create(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) - Import(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) - Bootstrap(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.Cluster, error) + FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []model.Cluster, err error) + Create(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) + Import(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) + Bootstrap(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) Install(ctx context.Context, clusterId domain.ClusterId) (err error) - Get(ctx context.Context, clusterId domain.ClusterId) (out domain.Cluster, err error) + Get(ctx context.Context, clusterId domain.ClusterId) (out model.Cluster, err error) GetClusterSiteValues(ctx context.Context, clusterId domain.ClusterId) (out domain.ClusterSiteValuesResponse, err error) Delete(ctx context.Context, clusterId domain.ClusterId) (err error) CreateBootstrapKubeconfig(ctx context.Context, clusterId domain.ClusterId) (out domain.BootstrapKubeconfig, err error) @@ -101,7 +101,7 @@ func (u *ClusterUsecase) WithTrx(trxHandle *gorm.DB) IClusterUsecase { return u } -func (u *ClusterUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []domain.Cluster, err error) { +func (u *ClusterUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.Cluster, err error) { user, ok := request.UserFrom(ctx) if !ok { return out, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") @@ -109,9 +109,9 @@ func (u *ClusterUsecase) Fetch(ctx context.Context, organizationId string, pg *p if organizationId == "" { // [TODO] 사용자가 속한 organization 리스트 - out, err = u.repo.Fetch(pg) + out, err = u.repo.Fetch(ctx, pg) } else { - out, err = u.repo.FetchByOrganizationId(organizationId, user.GetUserId(), pg) + out, err = u.repo.FetchByOrganizationId(ctx, organizationId, user.GetUserId(), pg) } if err != nil { @@ -120,12 +120,12 @@ func (u *ClusterUsecase) Fetch(ctx context.Context, organizationId string, pg *p return out, nil } -func (u *ClusterUsecase) FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []domain.Cluster, err error) { +func (u *ClusterUsecase) FetchByCloudAccountId(ctx context.Context, cloudAccountId uuid.UUID, pg *pagination.Pagination) (out []model.Cluster, err error) { if cloudAccountId == uuid.Nil { return nil, fmt.Errorf("Invalid cloudAccountId") } - out, err = u.repo.Fetch(pg) + out, err = u.repo.Fetch(ctx, pg) if err != nil { return nil, err @@ -133,19 +133,19 @@ func (u *ClusterUsecase) FetchByCloudAccountId(ctx context.Context, cloudAccount return out, nil } -func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) { +func (u *ClusterUsecase) Create(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - _, err = u.repo.GetByName(dto.OrganizationId, dto.Name) + _, err = u.repo.GetByName(ctx, dto.OrganizationId, dto.Name) if err == nil { return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "", "") } // check cloudAccount - cloudAccounts, err := u.cloudAccountRepo.Fetch(dto.OrganizationId, nil) + cloudAccounts, err := u.cloudAccountRepo.Fetch(ctx, dto.OrganizationId, nil) if err != nil { return "", httpErrors.NewBadRequestError(fmt.Errorf("Failed to get cloudAccounts"), "", "") } @@ -153,7 +153,7 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste tksCloudAccountId := dto.CloudAccountId.String() isExist := false for _, ca := range cloudAccounts { - if ca.ID == dto.CloudAccountId { + if ca.ID == *dto.CloudAccountId { // FOR TEST. ADD MAGIC KEYWORD if strings.Contains(ca.Name, domain.CLOUD_ACCOUNT_INCLUSTER) { @@ -168,7 +168,7 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste } // check stackTemplate - stackTemplate, err := u.stackTemplateRepo.Get(dto.StackTemplateId) + stackTemplate, err := u.stackTemplateRepo.Get(ctx, dto.StackTemplateId) if err != nil { return "", httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") } @@ -178,12 +178,13 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste userId := user.GetUserId() dto.CreatorId = &userId - clusterId, err = u.repo.Create(dto) + clusterId, err = u.repo.Create(ctx, dto) if err != nil { return "", errors.Wrap(err, "Failed to create cluster") } workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "create-tks-usercluster", argowf.SubmitOptions{ Parameters: []string{ @@ -197,40 +198,40 @@ func (u *ClusterUsecase) Create(ctx context.Context, dto domain.Cluster) (cluste "cloud_account_id=" + tksCloudAccountId, "base_repo_branch=" + viper.GetString("revision"), "keycloak_url=" + viper.GetString("keycloak-address"), - //"manifest_repo_url=" + viper.GetString("git-base-url") + "/" + viper.GetString("git-account") + "/" + clusterId + "-manifests", + "policy_ids=" + strings.Join(dto.PolicyIds, ","), }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return "", err } - log.InfoWithContext(ctx, "Successfully submited workflow: ", workflowId) + log.Info(ctx, "Successfully submited workflow: ", workflowId) - if err := u.repo.InitWorkflow(clusterId, workflowId, domain.ClusterStatus_INSTALLING); err != nil { + if err := u.repo.InitWorkflow(ctx, clusterId, workflowId, domain.ClusterStatus_INSTALLING); err != nil { return "", errors.Wrap(err, "Failed to initialize status") } return clusterId, nil } -func (u *ClusterUsecase) Import(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) { +func (u *ClusterUsecase) Import(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - _, err = u.repo.GetByName(dto.OrganizationId, dto.Name) + _, err = u.repo.GetByName(ctx, dto.OrganizationId, dto.Name) if err == nil { return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "", "") } - _, err = u.organizationRepo.Get(dto.OrganizationId) + _, err = u.organizationRepo.Get(ctx, dto.OrganizationId) if err != nil { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid organizationId"), "", "") } // check stackTemplate - stackTemplate, err := u.stackTemplateRepo.Get(dto.StackTemplateId) + stackTemplate, err := u.stackTemplateRepo.Get(ctx, dto.StackTemplateId) if err != nil { return "", httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") } @@ -241,7 +242,7 @@ func (u *ClusterUsecase) Import(ctx context.Context, dto domain.Cluster) (cluste dto.ID = "tks-admin" dto.Name = "tks-admin" } - clusterId, err = u.repo.Create(dto) + clusterId, err = u.repo.Create(ctx, dto) if err != nil { return "", errors.Wrap(err, "Failed to create cluster") } @@ -249,6 +250,7 @@ func (u *ClusterUsecase) Import(ctx context.Context, dto domain.Cluster) (cluste kubeconfigBase64 := base64.StdEncoding.EncodeToString([]byte(dto.Kubeconfig)) workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "import-tks-usercluster", argowf.SubmitOptions{ Parameters: []string{ @@ -263,59 +265,59 @@ func (u *ClusterUsecase) Import(ctx context.Context, dto domain.Cluster) (cluste }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return "", err } - log.InfoWithContext(ctx, "Successfully submited workflow: ", workflowId) + log.Info(ctx, "Successfully submited workflow: ", workflowId) - if err := u.repo.InitWorkflow(clusterId, workflowId, domain.ClusterStatus_INSTALLING); err != nil { + if err := u.repo.InitWorkflow(ctx, clusterId, workflowId, domain.ClusterStatus_INSTALLING); err != nil { return "", errors.Wrap(err, "Failed to initialize status") } return clusterId, nil } -func (u *ClusterUsecase) Bootstrap(ctx context.Context, dto domain.Cluster) (clusterId domain.ClusterId, err error) { +func (u *ClusterUsecase) Bootstrap(ctx context.Context, dto model.Cluster) (clusterId domain.ClusterId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - _, err = u.repo.GetByName(dto.OrganizationId, dto.Name) + _, err = u.repo.GetByName(ctx, dto.OrganizationId, dto.Name) if err == nil { return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "", "") } - stackTemplate, err := u.stackTemplateRepo.Get(dto.StackTemplateId) + stackTemplate, err := u.stackTemplateRepo.Get(ctx, dto.StackTemplateId) if err != nil { return "", httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") } - log.InfofWithContext(ctx, "%s %s", stackTemplate.CloudService, dto.CloudService) + log.Infof(ctx, "%s %s", stackTemplate.CloudService, dto.CloudService) if stackTemplate.CloudService != dto.CloudService { return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudService for stackTemplate "), "", "") } userId := user.GetUserId() dto.CreatorId = &userId - clusterId, err = u.repo.Create(dto) + clusterId, err = u.repo.Create(ctx, dto) if err != nil { return "", errors.Wrap(err, "Failed to create cluster") } workflow := "create-byoh-bootstrapkubeconfig" - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), "cluster_id=" + clusterId.String(), }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return "", err } - log.InfoWithContext(ctx, "Successfully submited workflow: ", workflowId) + log.Info(ctx, "Successfully submited workflow: ", workflowId) - if err := u.repo.InitWorkflow(clusterId, workflowId, domain.ClusterStatus_BOOTSTRAPPING); err != nil { + if err := u.repo.InitWorkflow(ctx, clusterId, workflowId, domain.ClusterStatus_BOOTSTRAPPING); err != nil { return "", errors.Wrap(err, "Failed to initialize status") } @@ -328,7 +330,7 @@ func (u *ClusterUsecase) Install(ctx context.Context, clusterId domain.ClusterId return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - cluster, err := u.repo.Get(clusterId) + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "C_INVALID_CLUSTER_ID", "") } @@ -336,7 +338,7 @@ func (u *ClusterUsecase) Install(ctx context.Context, clusterId domain.ClusterId return httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloudService"), "C_INVALID_CLOUD_SERVICE", "") } - stackTemplate, err := u.stackTemplateRepo.Get(cluster.StackTemplateId) + stackTemplate, err := u.stackTemplateRepo.Get(ctx, cluster.StackTemplateId) if err != nil { return httpErrors.NewBadRequestError(errors.Wrap(err, "Invalid stackTemplateId"), "", "") } @@ -345,6 +347,7 @@ func (u *ClusterUsecase) Install(ctx context.Context, clusterId domain.ClusterId } workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "create-tks-usercluster", argowf.SubmitOptions{ Parameters: []string{ @@ -362,29 +365,29 @@ func (u *ClusterUsecase) Install(ctx context.Context, clusterId domain.ClusterId }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return err } - log.InfoWithContext(ctx, "Successfully submited workflow: ", workflowId) + log.Info(ctx, "Successfully submited workflow: ", workflowId) - if err := u.repo.InitWorkflow(cluster.ID, workflowId, domain.ClusterStatus_INSTALLING); err != nil { + if err := u.repo.InitWorkflow(ctx, cluster.ID, workflowId, domain.ClusterStatus_INSTALLING); err != nil { return errors.Wrap(err, "Failed to initialize status") } return nil } -func (u *ClusterUsecase) Get(ctx context.Context, clusterId domain.ClusterId) (out domain.Cluster, err error) { - cluster, err := u.repo.Get(clusterId) +func (u *ClusterUsecase) Get(ctx context.Context, clusterId domain.ClusterId) (out model.Cluster, err error) { + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { - return domain.Cluster{}, err + return model.Cluster{}, err } return cluster, nil } func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) (err error) { - cluster, err := u.repo.Get(clusterId) + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { return httpErrors.NewNotFoundError(err, "", "") } @@ -393,7 +396,7 @@ func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) return fmt.Errorf("The cluster can not be deleted. cluster status : %s", cluster.Status) } - resAppGroups, err := u.appGroupRepo.Fetch(clusterId, nil) + resAppGroups, err := u.appGroupRepo.Fetch(ctx, clusterId, nil) if err != nil { return errors.Wrap(err, "Failed to get appgroup") } @@ -408,7 +411,7 @@ func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) // check cloudAccount tksCloudAccountId := "NULL" if cluster.CloudService != domain.CloudService_BYOH { - cloudAccount, err := u.cloudAccountRepo.Get(cluster.CloudAccount.ID) + cloudAccount, err := u.cloudAccountRepo.Get(ctx, cluster.CloudAccount.ID) if err != nil { return httpErrors.NewInternalServerError(fmt.Errorf("Failed to get cloudAccount"), "", "") } @@ -419,6 +422,7 @@ func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) } workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "tks-remove-usercluster", argowf.SubmitOptions{ Parameters: []string{ @@ -431,13 +435,13 @@ func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return errors.Wrap(err, "Failed to call argo workflow") } - log.DebugWithContext(ctx, "submited workflow name : ", workflowId) + log.Debug(ctx, "submited workflow name : ", workflowId) - if err := u.repo.InitWorkflow(clusterId, workflowId, domain.ClusterStatus_DELETING); err != nil { + if err := u.repo.InitWorkflow(ctx, clusterId, workflowId, domain.ClusterStatus_DELETING); err != nil { return errors.Wrap(err, "Failed to initialize status") } @@ -445,7 +449,7 @@ func (u *ClusterUsecase) Delete(ctx context.Context, clusterId domain.ClusterId) } func (u *ClusterUsecase) GetClusterSiteValues(ctx context.Context, clusterId domain.ClusterId) (out domain.ClusterSiteValuesResponse, err error) { - cluster, err := u.repo.Get(clusterId) + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { return domain.ClusterSiteValuesResponse{}, errors.Wrap(err, "Failed to get cluster") } @@ -453,17 +457,17 @@ func (u *ClusterUsecase) GetClusterSiteValues(ctx context.Context, clusterId dom out.SshKeyName = "tks-seoul" out.ClusterRegion = "ap-northeast-2" - if err := serializer.Map(cluster.Conf, &out); err != nil { - log.ErrorWithContext(ctx, err) + if err := serializer.Map(ctx, cluster, &out); err != nil { + log.Error(ctx, err) } if cluster.StackTemplate.CloudService == "AWS" && cluster.StackTemplate.KubeType == "AWS" { - out.TksUserNode = cluster.Conf.TksUserNode / domain.MAX_AZ_NUM - out.TksUserNodeMax = cluster.Conf.TksUserNodeMax / domain.MAX_AZ_NUM + out.TksUserNode = cluster.TksUserNode / domain.MAX_AZ_NUM + out.TksUserNodeMax = cluster.TksUserNodeMax / domain.MAX_AZ_NUM } - if err := serializer.Map(cluster, &out); err != nil { - log.ErrorWithContext(ctx, err) + if err := serializer.Map(ctx, cluster, &out); err != nil { + log.Error(ctx, err) } /* @@ -483,33 +487,33 @@ func (u *ClusterUsecase) GetClusterSiteValues(ctx context.Context, clusterId dom } func (u *ClusterUsecase) CreateBootstrapKubeconfig(ctx context.Context, clusterId domain.ClusterId) (out domain.BootstrapKubeconfig, err error) { - _, err = u.repo.Get(clusterId) + _, err = u.repo.Get(ctx, clusterId) if err != nil { return out, httpErrors.NewNotFoundError(err, "", "") } workflow := "create-byoh-bootstrapkubeconfig" - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), "cluster_id=" + clusterId.String(), }, }) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return out, httpErrors.NewInternalServerError(err, "S_FAILED_TO_CALL_WORKFLOW", "") } - log.DebugWithContext(ctx, "Submitted workflow: ", workflowId) + log.Debug(ctx, "Submitted workflow: ", workflowId) // wait & get clusterId ( max 1min ) for i := 0; i < 60; i++ { time.Sleep(time.Second * 3) - workflow, err := u.argo.GetWorkflow("argo", workflowId) + workflow, err := u.argo.GetWorkflow(ctx, "argo", workflowId) if err != nil { return out, err } - log.DebugWithContext(ctx, "workflow ", workflow) + log.Debug(ctx, "workflow ", workflow) if workflow.Status.Phase == "Succeeded" { break @@ -528,11 +532,11 @@ func (u *ClusterUsecase) CreateBootstrapKubeconfig(ctx context.Context, clusterI } func (u *ClusterUsecase) GetBootstrapKubeconfig(ctx context.Context, clusterId domain.ClusterId) (out domain.BootstrapKubeconfig, err error) { - cluster, err := u.repo.Get(clusterId) + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { return out, httpErrors.NewNotFoundError(err, "", "") } - client, err := kubernetes.GetClientAdminCluster() + client, err := kubernetes.GetClientAdminCluster(ctx) if err != nil { return out, err } @@ -553,7 +557,7 @@ func (u *ClusterUsecase) GetBootstrapKubeconfig(ctx context.Context, clusterId d return out, err } - log.DebugWithContext(ctx, helper.ModelToJson(kubeconfig.Status.BootstrapKubeconfigData)) + log.Debug(ctx, helper.ModelToJson(kubeconfig.Status.BootstrapKubeconfigData)) type BootstrapKubeconfigUser struct { Users []struct { @@ -572,15 +576,15 @@ func (u *ClusterUsecase) GetBootstrapKubeconfig(ctx context.Context, clusterId d } token := kubeconfigData.Users[0].User.Token[:6] - log.InfoWithContext(ctx, "token : ", token) + log.Info(ctx, "token : ", token) secrets, err := client.CoreV1().Secrets("kube-system").Get(context.TODO(), "bootstrap-token-"+token, metav1.GetOptions{}) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return out, err } - log.Info(secrets.Data["expiration"][:]) + log.Info(ctx, secrets.Data["expiration"][:]) // 2023-10-17T11:05:33Z now := time.Now() @@ -599,7 +603,7 @@ func (u *ClusterUsecase) GetBootstrapKubeconfig(ctx context.Context, clusterId d } func (u *ClusterUsecase) GetNodes(ctx context.Context, clusterId domain.ClusterId) (out []domain.ClusterNode, err error) { - cluster, err := u.repo.Get(clusterId) + cluster, err := u.repo.Get(ctx, clusterId) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return out, httpErrors.NewNotFoundError(err, "S_FAILED_FETCH_CLUSTER", "") @@ -610,7 +614,7 @@ func (u *ClusterUsecase) GetNodes(ctx context.Context, clusterId domain.ClusterI return out, httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloud service"), "", "") } - client, err := kubernetes.GetClientAdminCluster() + client, err := kubernetes.GetClientAdminCluster(ctx) if err != nil { return out, err } @@ -651,7 +655,7 @@ func (u *ClusterUsecase) GetNodes(ctx context.Context, clusterId domain.ClusterI tksUserNodeRegistered, tksUserNodeRegistering, tksUserHosts := 0, 0, make([]domain.ClusterHost, 0) for _, host := range hosts.Items { label := host.Labels["role"] - log.InfoWithContext(ctx, "label : ", label) + log.Info(ctx, "label : ", label) if len(label) < 12 { continue } @@ -712,30 +716,30 @@ func (u *ClusterUsecase) GetNodes(ctx context.Context, clusterId domain.ClusterI out = []domain.ClusterNode{ { Type: "TKS_CP_NODE", - Targeted: cluster.Conf.TksCpNode, + Targeted: cluster.TksCpNode, Registered: tksCpNodeRegistered, Registering: tksCpNodeRegistering, - Status: clusterNodeStatus(cluster.Conf.TksCpNode, tksCpNodeRegistered), + Status: clusterNodeStatus(cluster.TksCpNode, tksCpNodeRegistered), Command: command + "control-plane", Validity: bootstrapKubeconfig.Expiration, Hosts: tksCpHosts, }, { Type: "TKS_INFRA_NODE", - Targeted: cluster.Conf.TksInfraNode, + Targeted: cluster.TksInfraNode, Registered: tksInfraNodeRegistered, Registering: tksInfraNodeRegistering, - Status: clusterNodeStatus(cluster.Conf.TksInfraNode, tksInfraNodeRegistered), + Status: clusterNodeStatus(cluster.TksInfraNode, tksInfraNodeRegistered), Command: command + "tks", Validity: bootstrapKubeconfig.Expiration, Hosts: tksInfraHosts, }, { Type: "TKS_USER_NODE", - Targeted: cluster.Conf.TksUserNode, + Targeted: cluster.TksUserNode, Registered: tksUserNodeRegistered, Registering: tksUserNodeRegistering, - Status: clusterNodeStatus(cluster.Conf.TksUserNode, tksUserNodeRegistered), + Status: clusterNodeStatus(cluster.TksUserNode, tksUserNodeRegistered), Command: command + "worker", Validity: bootstrapKubeconfig.Expiration, Hosts: tksUserHosts, @@ -779,7 +783,7 @@ func (u *ClusterUsecase) GetNodes(ctx context.Context, clusterId domain.ClusterI } /* -func (u *ClusterUsecase) constructClusterConf(rawConf *domain.ClusterConf) (clusterConf *domain.ClusterConf, err error) { +func (u *ClusterUsecase) constructClusterConf(rawConf *model.ClusterConf) (clusterConf *model.ClusterConf, err error) { region := "ap-northeast-2" if rawConf != nil && rawConf.Region != "" { region = rawConf.Region @@ -790,7 +794,7 @@ func (u *ClusterUsecase) constructClusterConf(rawConf *domain.ClusterConf) (clus numOfAz = int(rawConf.NumOfAz) if numOfAz > 3 { - log.ErrorWithContext(ctx,"Error: numOfAz cannot exceed 3.") + log.Error(ctx,"Error: numOfAz cannot exceed 3.") temp_err := fmt.Errorf("Error: numOfAz cannot exceed 3.") return nil, temp_err } @@ -815,19 +819,19 @@ func (u *ClusterUsecase) constructClusterConf(rawConf *domain.ClusterConf) (clus var found bool = false for key, val := range azPerRegion { if strings.Contains(key, region) { - log.DebugWithContext(ctx,"Found region : ", key) + log.Debug(ctx,"Found region : ", key) maxAzForSelectedRegion = val - log.DebugWithContext(ctx,"Trimmed azNum var: ", maxAzForSelectedRegion) + log.Debug(ctx,"Trimmed azNum var: ", maxAzForSelectedRegion) found = true } } if !found { - log.ErrorWithContext(ctx,"Couldn't find entry for region ", region) + log.Error(ctx,"Couldn't find entry for region ", region) } if numOfAz > maxAzForSelectedRegion { - log.ErrorWithContext(ctx,"Invalid numOfAz: exceeded the number of Az in region ", region) + log.Error(ctx,"Invalid numOfAz: exceeded the number of Az in region ", region) temp_err := fmt.Errorf("Invalid numOfAz: exceeded the number of Az in region %s", region) return nil, temp_err } @@ -835,14 +839,14 @@ func (u *ClusterUsecase) constructClusterConf(rawConf *domain.ClusterConf) (clus // Validate if machineReplicas is multiple of number of AZ replicas := int(rawConf.MachineReplicas) if replicas == 0 { - log.DebugWithContext(ctx,"No machineReplicas param. Using default values..") + log.Debug(ctx,"No machineReplicas param. Using default values..") } else { if remainder := replicas % numOfAz; remainder != 0 { - log.ErrorWithContext(ctx,"Invalid machineReplicas: it should be multiple of numOfAz ", numOfAz) + log.Error(ctx,"Invalid machineReplicas: it should be multiple of numOfAz ", numOfAz) temp_err := fmt.Errorf("Invalid machineReplicas: it should be multiple of numOfAz %d", numOfAz) return nil, temp_err } else { - log.DebugWithContext(ctx,"Valid replicas and numOfAz. Caculating minSize & maxSize..") + log.Debug(ctx,"Valid replicas and numOfAz. Caculating minSize & maxSize..") minSizePerAz = int(replicas / numOfAz) maxSizePerAz = minSizePerAz * 5 @@ -851,13 +855,13 @@ func (u *ClusterUsecase) constructClusterConf(rawConf *domain.ClusterConf) (clus fmt.Printf("maxSizePerAz exceeded maximum value %d, so adjusted to %d", MAX_SIZE_PER_AZ, MAX_SIZE_PER_AZ) maxSizePerAz = MAX_SIZE_PER_AZ } - log.DebugWithContext(ctx,"Derived minSizePerAz: ", minSizePerAz) - log.DebugWithContext(ctx,"Derived maxSizePerAz: ", maxSizePerAz) + log.Debug(ctx,"Derived minSizePerAz: ", minSizePerAz) + log.Debug(ctx,"Derived maxSizePerAz: ", maxSizePerAz) } } // Construct cluster conf - tempConf := domain.ClusterConf{ + tempConf := model.ClusterConf{ SshKeyName: sshKeyName, Region: region, NumOfAz: int(numOfAz), diff --git a/internal/usecase/dashboard.go b/internal/usecase/dashboard.go index 18ceda50..a47f3fae 100644 --- a/internal/usecase/dashboard.go +++ b/internal/usecase/dashboard.go @@ -6,15 +6,18 @@ import ( "math" "sort" "strconv" + "strings" "time" "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/internal/kubernetes" + "github.com/openinfradev/tks-api/internal/model" + policytemplate "github.com/openinfradev/tks-api/internal/policy-template" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/serializer" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" "github.com/openinfradev/tks-api/pkg/log" thanos "github.com/openinfradev/tks-api/pkg/thanos-client" gcache "github.com/patrickmn/go-cache" @@ -26,31 +29,71 @@ import ( ) type IDashboardUsecase interface { + CreateDashboard(ctx context.Context, dashboard *model.Dashboard) (string, error) + GetDashboard(ctx context.Context, organizationId string, userId string, dashboardKey string) (*model.Dashboard, error) + UpdateDashboard(ctx context.Context, dashboard *model.Dashboard) error GetCharts(ctx context.Context, organizationId string, chartType domain.ChartType, duration string, interval string, year string, month string) (res []domain.DashboardChart, err error) GetStacks(ctx context.Context, organizationId string) (out []domain.DashboardStack, err error) GetResources(ctx context.Context, organizationId string) (out domain.DashboardResource, err error) + GetPolicyUpdate(ctx context.Context, policyTemplates []policytemplate.TKSPolicyTemplate, policies []policytemplate.TKSPolicy) (domain.DashboardPolicyUpdate, error) + GetPolicyEnforcement(ctx context.Context, organizationId string, primaryClusterId string) (*domain.BarChartData, error) + GetPolicyViolation(ctx context.Context, organizationId string, duration string, interval string) (*domain.BarChartData, error) + GetPolicyViolationLog(ctx context.Context, organizationId string) (*domain.GetDashboardPolicyViolationLogResponse, error) + GetWorkload(ctx context.Context, organizationId string) (*domain.GetDashboardWorkloadResponse, error) + GetPolicyViolationTop5(ctx context.Context, organizationId string, duration string, interval string) (*domain.BarChartData, error) + GetThanosClient(ctx context.Context, organizationId string) (thanos.ThanosClient, error) } type DashboardUsecase struct { - organizationRepo repository.IOrganizationRepository - clusterRepo repository.IClusterRepository - appGroupRepo repository.IAppGroupRepository - alertRepo repository.IAlertRepository - cache *gcache.Cache + dashboardRepo repository.IDashboardRepository + organizationRepo repository.IOrganizationRepository + clusterRepo repository.IClusterRepository + appGroupRepo repository.IAppGroupRepository + systemNotificationRepo repository.ISystemNotificationRepository + policyTemplateRepo repository.IPolicyTemplateRepository + policyRepo repository.IPolicyRepository + cache *gcache.Cache } func NewDashboardUsecase(r repository.Repository, cache *gcache.Cache) IDashboardUsecase { return &DashboardUsecase{ - organizationRepo: r.Organization, - clusterRepo: r.Cluster, - appGroupRepo: r.AppGroup, - alertRepo: r.Alert, - cache: cache, + dashboardRepo: r.Dashboard, + organizationRepo: r.Organization, + clusterRepo: r.Cluster, + appGroupRepo: r.AppGroup, + systemNotificationRepo: r.SystemNotification, + policyTemplateRepo: r.PolicyTemplate, + policyRepo: r.Policy, + cache: cache, } } +func (u *DashboardUsecase) CreateDashboard(ctx context.Context, dashboard *model.Dashboard) (string, error) { + dashboardId, err := u.dashboardRepo.CreateDashboard(ctx, dashboard) + if err != nil { + return "", errors.Wrap(err, "Failed to create dashboard.") + } + + return dashboardId, nil +} + +func (u *DashboardUsecase) GetDashboard(ctx context.Context, organizationId string, userId string, dashboardKey string) (*model.Dashboard, error) { + dashboard, err := u.dashboardRepo.GetDashboardByUserId(ctx, organizationId, userId, dashboardKey) + if err != nil { + return nil, errors.Wrap(err, "Failed to get dashboard.") + } + return dashboard, err +} + +func (u *DashboardUsecase) UpdateDashboard(ctx context.Context, dashboard *model.Dashboard) error { + if err := u.dashboardRepo.UpdateDashboard(ctx, dashboard); err != nil { + return errors.Wrap(err, "Failed to update dashboard") + } + return nil +} + func (u *DashboardUsecase) GetCharts(ctx context.Context, organizationId string, chartType domain.ChartType, duration string, interval string, year string, month string) (out []domain.DashboardChart, err error) { - _, err = u.organizationRepo.Get(organizationId) + _, err = u.organizationRepo.Get(ctx, organizationId) if err != nil { return nil, errors.Wrap(err, "invalid organization") } @@ -60,7 +103,7 @@ func (u *DashboardUsecase) GetCharts(ctx context.Context, organizationId string, continue } - chart, err := u.getChartFromPrometheus(organizationId, strType, duration, interval, year, month) + chart, err := u.getChartFromPrometheus(ctx, organizationId, strType, duration, interval, year, month) if err != nil { return nil, err } @@ -72,40 +115,40 @@ func (u *DashboardUsecase) GetCharts(ctx context.Context, organizationId string, } func (u *DashboardUsecase) GetStacks(ctx context.Context, organizationId string) (out []domain.DashboardStack, err error) { - clusters, err := u.clusterRepo.FetchByOrganizationId(organizationId, uuid.Nil, nil) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, uuid.Nil, nil) if err != nil { return out, err } - thanosUrl, err := u.getThanosUrl(organizationId) + thanosUrl, err := u.getThanosUrl(ctx, organizationId) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return out, httpErrors.NewInternalServerError(err, "D_INVALID_PRIMARY_STACK", "") } - address, port := helper.SplitAddress(thanosUrl) + address, port := helper.SplitAddress(ctx, thanosUrl) thanosClient, err := thanos.New(address, port, false, "") if err != nil { return out, errors.Wrap(err, "failed to create thanos client") } - stackMemoryDisk, err := thanosClient.Get("sum by (__name__, taco_cluster) ({__name__=~\"node_memory_MemFree_bytes|machine_memory_bytes|kubelet_volume_stats_used_bytes|kubelet_volume_stats_capacity_bytes\"})") + stackMemoryDisk, err := thanosClient.Get(ctx, "sum by (__name__, taco_cluster) ({__name__=~\"node_memory_MemFree_bytes|machine_memory_bytes|kubelet_volume_stats_used_bytes|kubelet_volume_stats_capacity_bytes\"})") if err != nil { return out, err } - stackCpu, err := thanosClient.Get("avg by (taco_cluster) (instance:node_cpu:ratio*100)") + stackCpu, err := thanosClient.Get(ctx, "avg by (taco_cluster) (instance:node_cpu:ratio*100)") if err != nil { return out, err } for _, cluster := range clusters { - appGroups, err := u.appGroupRepo.Fetch(cluster.ID, nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, cluster.ID, nil) if err != nil { return nil, err } - stack := reflectClusterToStack(cluster, appGroups) + stack := reflectClusterToStack(ctx, cluster, appGroups) dashboardStack := domain.DashboardStack{} - if err := serializer.Map(stack, &dashboardStack); err != nil { - log.InfoWithContext(ctx, err) + if err := serializer.Map(ctx, stack, &dashboardStack); err != nil { + log.Info(ctx, err) } memory, disk := u.getStackMemoryDisk(stackMemoryDisk.Data.Result, cluster.ID.String()) @@ -136,38 +179,61 @@ func (u *DashboardUsecase) GetStacks(ctx context.Context, organizationId string) } func (u *DashboardUsecase) GetResources(ctx context.Context, organizationId string) (out domain.DashboardResource, err error) { - thanosUrl, err := u.getThanosUrl(organizationId) + thanosUrl, err := u.getThanosUrl(ctx, organizationId) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return out, httpErrors.NewInternalServerError(err, "D_INVALID_PRIMARY_STACK", "") } - address, port := helper.SplitAddress(thanosUrl) + address, port := helper.SplitAddress(ctx, thanosUrl) thanosClient, err := thanos.New(address, port, false, "") if err != nil { return out, errors.Wrap(err, "failed to create thanos client") } // Stack - clusters, err := u.clusterRepo.FetchByOrganizationId(organizationId, uuid.Nil, nil) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, uuid.Nil, nil) if err != nil { + log.Error(ctx, err) return out, err } - filteredClusters := funk.Filter(clusters, func(x domain.Cluster) bool { - return x.Status != domain.ClusterStatus_DELETED + filteredClusters := funk.Filter(clusters, func(x model.Cluster) bool { + return x.Status == domain.ClusterStatus_RUNNING }) + + var normal, abnormal int if filteredClusters != nil { - out.Stack = fmt.Sprintf("%d 개", len(filteredClusters.([]domain.Cluster))) - } else { - out.Stack = "0 개" + for _, cluster := range filteredClusters.([]model.Cluster) { + clientSet, err := kubernetes.GetClientFromClusterId(ctx, cluster.ID.String()) + if err != nil { + return out, errors.Wrap(err, "Failed to get client set for user cluster") + } + // get cluster info + clusterInfo, err := clientSet.CoreV1().Services("kube-system").List(context.TODO(), metav1.ListOptions{LabelSelector: "kubernetes.io/cluster-service"}) + if err != nil { + abnormal++ + log.Debugf(ctx, "Failed to get cluster info: %v\n", err) + continue + } + if clusterInfo != nil && len(clusterInfo.Items) > 0 { + if clusterInfo.Items[0].ObjectMeta.Labels["kubernetes.io/cluster-service"] == "true" { + normal++ + } else { + abnormal++ + } + } + } } + out.Stack.Normal = strconv.Itoa(normal) + out.Stack.Abnormal = strconv.Itoa(abnormal) // CPU /* {"data":{"result":[{"metric":{"taco_cluster":"cmsai5k5l"},"value":[1683608185.65,"32"]},{"metric":{"taco_cluster":"crjfh12oc"},"value":[1683608185.65,"12"]}],"vector":""},"status":"success"} */ - result, err := thanosClient.Get("sum by (taco_cluster) (machine_cpu_cores)") + result, err := thanosClient.Get(ctx, "sum by (taco_cluster) (machine_cpu_cores)") if err != nil { + log.Error(ctx, err) return out, err } cpu := 0 @@ -180,11 +246,12 @@ func (u *DashboardUsecase) GetResources(ctx context.Context, organizationId stri cpu = cpu + cpuVal } } - out.Cpu = fmt.Sprintf("%d 개", cpu) + out.Cpu = strconv.Itoa(cpu) // Memory - result, err = thanosClient.Get("sum by (taco_cluster) (machine_memory_bytes)") + result, err = thanosClient.Get(ctx, "sum by (taco_cluster) (machine_memory_bytes)") if err != nil { + log.Error(ctx, err) return out, err } memory := float64(0) @@ -198,11 +265,12 @@ func (u *DashboardUsecase) GetResources(ctx context.Context, organizationId stri memory = memory + memory_ } } - out.Memory = fmt.Sprintf("%v GiB", math.Round(memory)) + out.Memory = fmt.Sprintf("%v", math.Round(memory)) // Storage - result, err = thanosClient.Get("sum by (taco_cluster) (kubelet_volume_stats_capacity_bytes)") + result, err = thanosClient.Get(ctx, "sum by (taco_cluster) (kubelet_volume_stats_capacity_bytes)") if err != nil { + log.Error(ctx, err) return out, err } storage := float64(0) @@ -216,18 +284,18 @@ func (u *DashboardUsecase) GetResources(ctx context.Context, organizationId stri storage = storage + storage_ } } - out.Storage = fmt.Sprintf("%v GiB", math.Round(storage)) + out.Storage = fmt.Sprintf("%v", math.Round(storage)) return } -func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartType string, duration string, interval string, year string, month string) (res domain.DashboardChart, err error) { - thanosUrl, err := u.getThanosUrl(organizationId) +func (u *DashboardUsecase) getChartFromPrometheus(ctx context.Context, organizationId string, chartType string, duration string, interval string, year string, month string) (res domain.DashboardChart, err error) { + thanosUrl, err := u.getThanosUrl(ctx, organizationId) if err != nil { - log.Error(err) + log.Error(ctx, err) return res, httpErrors.NewInternalServerError(err, "D_INVALID_PRIMARY_STACK", "") } - address, port := helper.SplitAddress(thanosUrl) + address, port := helper.SplitAddress(ctx, thanosUrl) thanosClient, err := thanos.New(address, port, false, "") if err != nil { return res, errors.Wrap(err, "failed to create thanos client") @@ -285,17 +353,17 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy return res, fmt.Errorf("Invalid month") } - alerts, err := u.alertRepo.FetchPodRestart(organizationId, startDate, endDate) + systemNotifications, err := u.systemNotificationRepo.FetchPodRestart(ctx, organizationId, startDate, endDate) if err != nil { return res, err } - organization, err := u.organizationRepo.Get(organizationId) + organization, err := u.organizationRepo.Get(ctx, organizationId) if err != nil { return res, err } - log.Info(organization.CreatedAt.Format("2006-01-02")) + log.Info(ctx, organization.CreatedAt.Format("2006-01-02")) podCounts := []domain.PodCount{} for day := rangeDate(startDate, endDate); ; { @@ -308,8 +376,8 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy cntPodRestart := 0 if baseDate <= now.Format("2006-01-02") && baseDate >= organization.CreatedAt.Format("2006-01-02") { - for _, alert := range alerts { - strDate := alert.CreatedAt.Format("2006-01-02") + for _, systemNotification := range systemNotifications { + strDate := systemNotification.CreatedAt.Format("2006-01-02") if strDate == baseDate { cntPodRestart += 1 @@ -340,7 +408,7 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy return domain.DashboardChart{}, fmt.Errorf("No data") } - result, err := thanosClient.FetchRange(query, int(now.Unix())-durationSec, int(now.Unix()), intervalSec) + result, err := thanosClient.FetchRange(ctx, query, int(now.Unix())-durationSec, int(now.Unix()), intervalSec) if err != nil { return res, err } @@ -374,7 +442,7 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy yAxisData = append(yAxisData, u.getChartYValue(val.Values, xAxis, percentage)) } - clusterName, err := u.getClusterNameFromId(val.Metric.TacoCluster) + clusterName, err := u.getClusterNameFromId(ctx, val.Metric.TacoCluster) if err != nil { clusterName = val.Metric.TacoCluster } @@ -401,14 +469,14 @@ func (u *DashboardUsecase) getChartFromPrometheus(organizationId string, chartTy } -func (u *DashboardUsecase) getThanosUrl(organizationId string) (out string, err error) { +func (u *DashboardUsecase) getThanosUrl(ctx context.Context, organizationId string) (out string, err error) { const prefix = "CACHE_KEY_THANOS_URL" value, found := u.cache.Get(prefix + organizationId) if found { return value.(string), nil } - organization, err := u.organizationRepo.Get(organizationId) + organization, err := u.organizationRepo.Get(ctx, organizationId) if err != nil { return out, errors.Wrap(err, "Failed to get organization") } @@ -418,7 +486,7 @@ func (u *DashboardUsecase) getThanosUrl(organizationId string) (out string, err return out, fmt.Errorf("Invalid primary clusterId") } - clientset_admin, err := kubernetes.GetClientAdminCluster() + clientset_admin, err := kubernetes.GetClientAdminCluster(ctx) if err != nil { return out, errors.Wrap(err, "Failed to get client set for user cluster") } @@ -426,9 +494,9 @@ func (u *DashboardUsecase) getThanosUrl(organizationId string) (out string, err // tks-endpoint-secret 이 있다면 그 secret 내의 endpoint 를 사용한다. secrets, err := clientset_admin.CoreV1().Secrets(organization.PrimaryClusterId).Get(context.TODO(), "tks-endpoint-secret", metav1.GetOptions{}) if err != nil { - log.Info("cannot found tks-endpoint-secret. so use LoadBalancer...") + log.Info(ctx, "cannot found tks-endpoint-secret. so use LoadBalancer...") - clientset_user, err := kubernetes.GetClientFromClusterId(organization.PrimaryClusterId) + clientset_user, err := kubernetes.GetClientFromClusterId(ctx, organization.PrimaryClusterId) if err != nil { return out, errors.Wrap(err, "Failed to get client set for user cluster") } @@ -455,7 +523,7 @@ func (u *DashboardUsecase) getThanosUrl(organizationId string) (out string, err } } else { out = "http://" + string(secrets.Data["thanos"]) - log.Info("thanosUrl : ", out) + log.Info(ctx, "thanosUrl : ", out) u.cache.Set(prefix+organizationId, out, gcache.DefaultExpiration) return out, nil } @@ -529,14 +597,14 @@ func (u *DashboardUsecase) getStackCpu(result []thanos.MetricDataResult, cluster return } -func (u *DashboardUsecase) getClusterNameFromId(clusterId string) (clusterName string, err error) { +func (u *DashboardUsecase) getClusterNameFromId(ctx context.Context, clusterId string) (clusterName string, err error) { const prefix = "CACHE_KEY_CLUSTER_NAME_FROM_ID" value, found := u.cache.Get(prefix + clusterId) if found { return value.(string), nil } - cluster, err := u.clusterRepo.Get(domain.ClusterId(clusterId)) + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) if err != nil { return clusterName, errors.Wrap(err, "Failed to get cluster") } @@ -546,6 +614,559 @@ func (u *DashboardUsecase) getClusterNameFromId(clusterId string) (clusterName s return } +func (u *DashboardUsecase) GetPolicyUpdate(ctx context.Context, policyTemplates []policytemplate.TKSPolicyTemplate, + policies []policytemplate.TKSPolicy) (domain.DashboardPolicyUpdate, error) { + + var outdatedTemplateIds []string + for _, tpt := range policyTemplates { + templateId := tpt.Labels[policytemplate.TemplateIDLabel] + id, err := uuid.Parse(templateId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + continue + } + version, err := u.policyTemplateRepo.GetLatestTemplateVersion(ctx, id) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + continue + } + + if version != tpt.Spec.Version { + outdatedTemplateIds = append(outdatedTemplateIds, templateId) + } + } + + outdatedTemplateCount := len(outdatedTemplateIds) + outdatedPolicyCount := 0 + + for _, policy := range policies { + templateId := policy.Labels[policytemplate.TemplateIDLabel] + + if slices.Contains(outdatedTemplateIds, templateId) { + outdatedPolicyCount++ + } + } + + dpu := domain.DashboardPolicyUpdate{ + PolicyTemplate: outdatedTemplateCount, + Policy: outdatedPolicyCount, + } + + return dpu, nil +} + +func (u *DashboardUsecase) GetPolicyEnforcement(ctx context.Context, organizationId string, primaryClusterId string) (*domain.BarChartData, error) { + type DashboardPolicyTemplate struct { + ClusterId string + PolicyTemplate map[string]map[string]int + } + dashboardPolicyTemplates := make([]DashboardPolicyTemplate, 0) + + // get clusters from db + dbClusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, uuid.Nil, nil) + if err != nil { + return nil, err + } + filteredClusters := funk.Filter(dbClusters, func(x model.Cluster) bool { + return x.Status == domain.ClusterStatus_RUNNING + }) + if filteredClusters != nil { + dbPolicyTemplates, err := u.policyTemplateRepo.GetPolicyTemplateByOrganizationIdOrTKS(ctx, organizationId) + if err != nil { + return nil, err + } + for _, cluster := range filteredClusters.([]model.Cluster) { + policyTemplates := make(map[string]map[string]int) + // get policytemplates by cluster + // dbPolicyTemplates = {"K8sAllowedRepos": {"": 0}} + for _, dpt := range dbPolicyTemplates { + if _, ok := policyTemplates[dpt.TemplateName]; !ok { + policyTemplates[dpt.TemplateName] = map[string]int{"": 0} + } + } + dashboardPolicyTemplates = append(dashboardPolicyTemplates, + DashboardPolicyTemplate{ClusterId: cluster.Name, PolicyTemplate: policyTemplates}) + } + } + + // get clusters from cr + clusters, err := policytemplate.GetTksClusterCRs(ctx, primaryClusterId) + if err != nil { + log.Error(ctx, "Failed to retrieve policytemplate list ", err) + return nil, err + } + + for _, cluster := range clusters { + // policyTemplates = {"K8sAllowedRepos": {"members": 1}} + policyTemplates := make(map[string]map[string]int) + + // If the cluster does not have a policytemplate, skip ahead + // cluster.Status.Templates = {"K8sAllowedRepos": ["members"]} + if cluster.Status.Templates == nil { + continue + } + + for templateName, policies := range cluster.Status.Templates { + for _, policy := range policies { + policyTemplates[templateName] = make(map[string]int) + policyTemplates[templateName][policy] = 1 + } + } + + dashboardPolicyTemplates = append(dashboardPolicyTemplates, + DashboardPolicyTemplate{ClusterId: cluster.Name, PolicyTemplate: policyTemplates}) + } + + // fetch policies from db + dbPolicies, err := u.policyRepo.Fetch(ctx, organizationId, nil) + if err != nil { + return nil, err + } + + type TotalPolicyCount struct { + PolicyName string + OptionalPolicyCount int + RequiredPolicyCount int + } + + // totalTemplate = {"template name": TotalPolicyCount} + totalTemplate := make(map[string]*TotalPolicyCount) + for _, dpt := range dashboardPolicyTemplates { + for templateName, policies := range dpt.PolicyTemplate { + if _, ok := totalTemplate[templateName]; !ok { + totalTemplate[templateName] = &TotalPolicyCount{"", 0, 0} + } + // check if policy is required or optional + for policy, count := range policies { + for _, dbPolicy := range *dbPolicies { + if policy == dbPolicy.PolicyResourceName { + temp := totalTemplate[templateName] + temp.PolicyName = policy + if dbPolicy.Mandatory { + temp.RequiredPolicyCount += count + } else { + temp.OptionalPolicyCount += count + } + } + } + } + } + } + + // desc sorting by value + type ChartData struct { + Name string + OptionalCount int + RequiredCount int + } + chartData := make([]ChartData, 0) + for key, val := range totalTemplate { + data := ChartData{ + Name: key, + OptionalCount: val.OptionalPolicyCount, + RequiredCount: val.RequiredPolicyCount, + } + chartData = append(chartData, data) + } + sort.Slice(chartData, func(i, j int) bool { + return chartData[i].OptionalCount+chartData[i].RequiredCount > + chartData[j].OptionalCount+chartData[j].RequiredCount + }) + + // X축 + var xAxis *domain.Axis + xData := make([]string, 0) + + // Y축 + var series []domain.UnitNumber + yOptionalData := make([]int, 0) + yRequiredData := make([]int, 0) + + for _, v := range chartData { + xData = append(xData, v.Name) + yOptionalData = append(yOptionalData, v.OptionalCount) + yRequiredData = append(yRequiredData, v.RequiredCount) + } + + xAxis = &domain.Axis{ + Data: xData, + } + + optionalUnit := domain.UnitNumber{ + Name: "선택", + Data: yOptionalData, + } + series = append(series, optionalUnit) + + requiredUnit := domain.UnitNumber{ + Name: "필수", + Data: yRequiredData, + } + series = append(series, requiredUnit) + + bcd := &domain.BarChartData{ + XAxis: xAxis, + Series: series, + } + + return bcd, nil +} + +func (u *DashboardUsecase) GetPolicyViolation(ctx context.Context, organizationId string, duration string, interval string) (*domain.BarChartData, error) { + thanosClient, err := u.GetThanosClient(ctx, organizationId) + if err != nil { + return nil, errors.Wrap(err, "failed to create thanos client") + } + + durationSec, intervalSec := getDurationAndIntervalSec(duration, interval) + + clusterIdStr, err := u.GetFlatClusterIds(ctx, organizationId) + if err != nil { + return nil, err + } + query := fmt.Sprintf("sum by(kind,name,violation_enforcement)(opa_scorecard_constraint_violations{taco_cluster=~\"%s\"})", clusterIdStr) + + now := time.Now() + pm, err := thanosClient.FetchPolicyRange(ctx, query, int(now.Unix())-durationSec, int(now.Unix()), intervalSec) + if err != nil { + return nil, err + } + + // totalViolation: {"K8sRequiredLabels": {"violation_enforcement": 2}} + totalViolation := make(map[string]map[string]int) + + dbPolicyTemplates, err := u.policyTemplateRepo.GetPolicyTemplateByOrganizationIdOrTKS(ctx, organizationId) + if err != nil { + return nil, err + } + for _, dpt := range dbPolicyTemplates { + if _, ok := totalViolation[dpt.TemplateName]; !ok { + totalViolation[dpt.TemplateName] = map[string]int{"": 0} + } + } + + for _, res := range pm.Data.Result { + policyTemplate := res.Metric.Kind + if len(res.Metric.Violation) == 0 { + continue + } + count, err := strconv.Atoi(res.Value[1].(string)) + if err != nil { + count = 0 + } + violation := res.Metric.Violation + if val, ok := totalViolation[policyTemplate][violation]; !ok { + totalViolation[policyTemplate] = make(map[string]int) + totalViolation[policyTemplate][violation] = count + } else { + totalViolation[policyTemplate][violation] = val + count + } + } + + // desc sorting by value + type ChartData struct { + Name string + DenyCount int + WarnCount int + DryRunCount int + } + chartData := make([]ChartData, 0) + for pt, violations := range totalViolation { + data := ChartData{} + data.Name = pt + if val, ok := violations["deny"]; ok { + data.DenyCount = val + } + if val, ok := violations["warn"]; ok { + data.WarnCount = val + } + if val, ok := violations["dryrun"]; ok { + data.DryRunCount = val + } + chartData = append(chartData, data) + } + sort.Slice(chartData, func(i, j int) bool { + return chartData[i].DenyCount+chartData[i].WarnCount+chartData[i].DryRunCount > + chartData[j].DenyCount+chartData[j].WarnCount+chartData[j].DryRunCount + }) + + // X축 + var xAxis *domain.Axis + xData := make([]string, 0) + + // Y축 + var series []domain.UnitNumber + yDenyData := make([]int, 0) + yWarnData := make([]int, 0) + yDryrunData := make([]int, 0) + + for _, v := range chartData { + xData = append(xData, v.Name) + yDenyData = append(yDenyData, v.DenyCount) + yWarnData = append(yWarnData, v.WarnCount) + yDryrunData = append(yDryrunData, v.DryRunCount) + } + + xAxis = &domain.Axis{ + Data: xData, + } + + denyUnit := domain.UnitNumber{ + Name: "거부", + Data: yDenyData, + } + series = append(series, denyUnit) + + warnUnit := domain.UnitNumber{ + Name: "경고", + Data: yWarnData, + } + series = append(series, warnUnit) + + dryrunUnit := domain.UnitNumber{ + Name: "감사", + Data: yDryrunData, + } + series = append(series, dryrunUnit) + + bcd := &domain.BarChartData{ + XAxis: xAxis, + Series: series, + } + + return bcd, nil +} + +func (u *DashboardUsecase) GetPolicyViolationLog(ctx context.Context, organizationId string) (*domain.GetDashboardPolicyViolationLogResponse, error) { + // TODO Implement me + return nil, nil +} + +func (u *DashboardUsecase) GetWorkload(ctx context.Context, organizationId string) (*domain.GetDashboardWorkloadResponse, error) { + thanosClient, err := u.GetThanosClient(ctx, organizationId) + if err != nil { + return nil, errors.Wrap(err, "failed to create thanos client") + } + + clusterIdStr, err := u.GetFlatClusterIds(ctx, organizationId) + if err != nil { + return nil, err + } + + dwr := &domain.GetDashboardWorkloadResponse{Title: "자원별 Pod 배포 현황"} + + // Deployment pod count + count := 0 + query := fmt.Sprintf("sum (kube_deployment_status_replicas_available{taco_cluster=~'%s'} )", clusterIdStr) + wm, err := thanosClient.GetWorkload(ctx, query) + if err != nil { + return nil, err + } + if len(wm.Data.Result) > 0 && len(wm.Data.Result[0].Value) > 1 { + count, _ = strconv.Atoi(wm.Data.Result[0].Value[1].(string)) + } + + dwr.Data = append(dwr.Data, domain.WorkloadData{Name: "Deployments", Value: count}) + + // StatefulSet pod count + count = 0 + query = fmt.Sprintf("sum (kube_statefulset_status_replicas_available{taco_cluster=~'%s'} )", clusterIdStr) + wm, err = thanosClient.GetWorkload(ctx, query) + if err != nil { + return nil, err + } + if len(wm.Data.Result) > 0 && len(wm.Data.Result[0].Value) > 1 { + count, _ = strconv.Atoi(wm.Data.Result[0].Value[1].(string)) + } + dwr.Data = append(dwr.Data, domain.WorkloadData{Name: "StatefulSets", Value: count}) + + // DaemonSet pod count + count = 0 + query = fmt.Sprintf("sum (kube_daemonset_status_number_available{taco_cluster=~'%s'} )", clusterIdStr) + wm, err = thanosClient.GetWorkload(ctx, query) + if err != nil { + return nil, err + } + if len(wm.Data.Result) > 0 && len(wm.Data.Result[0].Value) > 1 { + count, _ = strconv.Atoi(wm.Data.Result[0].Value[1].(string)) + } + dwr.Data = append(dwr.Data, domain.WorkloadData{Name: "DaemonSets", Value: count}) + + // CronJob pod count + count = 0 + query = fmt.Sprintf("sum (kube_cronjob_status_active{taco_cluster=~'%s'} )", clusterIdStr) + wm, err = thanosClient.GetWorkload(ctx, query) + if err != nil { + return nil, err + } + if len(wm.Data.Result) > 0 && len(wm.Data.Result[0].Value) > 1 { + count, _ = strconv.Atoi(wm.Data.Result[0].Value[1].(string)) + } + dwr.Data = append(dwr.Data, domain.WorkloadData{Name: "CronJobs", Value: count}) + + // Job pod count + count = 0 + query = fmt.Sprintf("sum (kube_job_status_active{taco_cluster=~'%s'} )", clusterIdStr) + wm, err = thanosClient.GetWorkload(ctx, query) + if err != nil { + return nil, err + } + if len(wm.Data.Result) > 0 && len(wm.Data.Result[0].Value) > 1 { + count, _ = strconv.Atoi(wm.Data.Result[0].Value[1].(string)) + } + dwr.Data = append(dwr.Data, domain.WorkloadData{Name: "Jobs", Value: count}) + + return dwr, nil +} + +func (u *DashboardUsecase) GetPolicyViolationTop5(ctx context.Context, organizationId string, duration string, interval string) (*domain.BarChartData, error) { + thanosClient, err := u.GetThanosClient(ctx, organizationId) + if err != nil { + return nil, errors.Wrap(err, "failed to create thanos client") + } + + durationSec, intervalSec := getDurationAndIntervalSec(duration, interval) + + clusterIdStr, err := u.GetFlatClusterIds(ctx, organizationId) + if err != nil { + return nil, err + } + + now := time.Now() + query := fmt.Sprintf("topk (5, sum by (kind) (opa_scorecard_constraint_violations{taco_cluster=~'%s'}))", clusterIdStr) + ptm, err := thanosClient.FetchPolicyTemplateRange(ctx, query, int(now.Unix())-durationSec, int(now.Unix()), intervalSec) + if err != nil { + return nil, err + } + + templateNames := make([]string, 0) + for _, result := range ptm.Data.Result { + templateNames = append(templateNames, result.Metric.Kind) + } + + // desc sorting by value + type ChartData struct { + Name string + DenyCount int + WarnCount int + DryRunCount int + } + chartData := make([]ChartData, 0) + for _, templateName := range templateNames { + //xData = append(xData, templateName) + data := ChartData{} + data.Name = templateName + + query = fmt.Sprintf("sum by (violation_enforcement) "+ + "(opa_scorecard_constraint_violations{taco_cluster='%s', kind='%s'})", clusterIdStr, templateName) + pvcm, err := thanosClient.FetchPolicyViolationCountRange(ctx, query, int(now.Unix())-durationSec, int(now.Unix()), intervalSec) + if err != nil { + return nil, err + } + + denyCount := 0 + warnCount := 0 + dryrunCount := 0 + for _, result := range pvcm.Data.Result { + switch policy := result.Metric.ViolationEnforcement; policy { + case "": + denyCount, _ = strconv.Atoi(result.Value[1].(string)) + case "warn": + warnCount, _ = strconv.Atoi(result.Value[1].(string)) + case "dryrun": + dryrunCount, _ = strconv.Atoi(result.Value[1].(string)) + } + } + data.DenyCount = denyCount + data.WarnCount = warnCount + data.DryRunCount = dryrunCount + chartData = append(chartData, data) + } + sort.Slice(chartData, func(i, j int) bool { + return chartData[i].DenyCount+chartData[i].WarnCount+chartData[i].DryRunCount > + chartData[j].DenyCount+chartData[j].WarnCount+chartData[j].DryRunCount + }) + + // X축 + var xAxis *domain.Axis + xData := make([]string, 0) + + // Y축 + var series []domain.UnitNumber + yDenyData := make([]int, 0) + yWarnData := make([]int, 0) + yDryrunData := make([]int, 0) + + for _, v := range chartData { + xData = append(xData, v.Name) + yDenyData = append(yDenyData, v.DenyCount) + yWarnData = append(yWarnData, v.WarnCount) + yDryrunData = append(yDryrunData, v.DryRunCount) + } + + xAxis = &domain.Axis{ + Data: xData, + } + + denyUnit := domain.UnitNumber{ + Name: "거부", + Data: yDenyData, + } + series = append(series, denyUnit) + + warnUnit := domain.UnitNumber{ + Name: "경고", + Data: yWarnData, + } + series = append(series, warnUnit) + + dryrunUnit := domain.UnitNumber{ + Name: "감사", + Data: yDryrunData, + } + series = append(series, dryrunUnit) + + bcd := &domain.BarChartData{ + XAxis: xAxis, + Series: series, + } + + return bcd, nil +} + +func (u *DashboardUsecase) GetThanosClient(ctx context.Context, organizationId string) (thanos.ThanosClient, error) { + thanosUrl, err := u.getThanosUrl(ctx, organizationId) + if err != nil { + log.Error(ctx, err) + return nil, httpErrors.NewInternalServerError(err, "D_INVALID_PRIMARY_STACK", "") + } + address, port := helper.SplitAddress(ctx, thanosUrl) + + // [TEST] + //address = "http://a93c60de70c794ef39b495976588c989-d7cd29ca75def693.elb.ap-northeast-2.amazonaws.com" + //port = 9090 + + client, err := thanos.New(address, port, false, "") + if err != nil { + return nil, errors.Wrap(err, "failed to create thanos client") + } + return client, nil +} + +func (u *DashboardUsecase) GetFlatClusterIds(ctx context.Context, organizationId string) (string, error) { + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, uuid.Nil, nil) + if err != nil { + log.Error(ctx, err) + return "", err + } + var clusterIds []string + for _, cluster := range clusters { + clusterIds = append(clusterIds, cluster.ID.String()) + } + clusterIdStr := strings.Join(clusterIds, "|") + return clusterIdStr, nil +} + func rangeDate(start, end time.Time) func() time.Time { y, m, d := start.Date() start = time.Date(y, m, d, 0, 0, 0, 0, time.UTC) @@ -561,3 +1182,27 @@ func rangeDate(start, end time.Time) func() time.Time { return date } } + +func getDurationAndIntervalSec(duration string, interval string) (int, int) { + durationSec := 60 * 60 * 24 + switch duration { + case "1h": + durationSec = 60 * 60 + case "1d": + durationSec = 60 * 60 * 24 + case "7d": + durationSec = 60 * 60 * 24 * 7 + case "30d": + durationSec = 60 * 60 * 24 * 30 + } + + intervalSec := 60 * 60 // default 1h + switch interval { + case "1h": + intervalSec = 60 * 60 + case "1d": + intervalSec = 60 * 60 * 24 + } + + return durationSec, intervalSec +} diff --git a/internal/usecase/organization.go b/internal/usecase/organization.go index 3e4cc97d..7e5c3d07 100644 --- a/internal/usecase/organization.go +++ b/internal/usecase/organization.go @@ -8,6 +8,8 @@ import ( "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" argowf "github.com/openinfradev/tks-api/pkg/argo-client" @@ -19,47 +21,63 @@ import ( ) type IOrganizationUsecase interface { - Create(context.Context, *domain.Organization) (organizationId string, err error) - Fetch(pg *pagination.Pagination) (*[]domain.Organization, error) - Get(organizationId string) (domain.Organization, error) - Update(organizationId string, in domain.UpdateOrganizationRequest) (domain.Organization, error) - UpdatePrimaryClusterId(organizationId string, clusterId string) (err error) - Delete(organizationId string, accessToken string) error + Create(context.Context, *model.Organization) (organizationId string, err error) + Fetch(ctx context.Context, pg *pagination.Pagination) (*[]model.Organization, error) + Get(ctx context.Context, organizationId string) (model.Organization, error) + Update(ctx context.Context, organizationId string, dto model.Organization) (model.Organization, error) + UpdatePrimaryClusterId(ctx context.Context, organizationId string, clusterId string) (err error) + ChangeAdminId(ctx context.Context, organizationId string, adminId uuid.UUID) error + Delete(ctx context.Context, organizationId string, accessToken string) error } type OrganizationUsecase struct { - repo repository.IOrganizationRepository - argo argowf.ArgoClient - kc keycloak.IKeycloak + repo repository.IOrganizationRepository + userRepo repository.IUserRepository + roleRepo repository.IRoleRepository + clusterRepo repository.IClusterRepository + stackTemplateRepo repository.IStackTemplateRepository + systemNotificationRuleRepo repository.ISystemNotificationRuleRepository + systemNotificationTemplateRepo repository.ISystemNotificationTemplateRepository + argo argowf.ArgoClient + kc keycloak.IKeycloak } func NewOrganizationUsecase(r repository.Repository, argoClient argowf.ArgoClient, kc keycloak.IKeycloak) IOrganizationUsecase { return &OrganizationUsecase{ - repo: r.Organization, - argo: argoClient, - kc: kc, + repo: r.Organization, + userRepo: r.User, + roleRepo: r.Role, + clusterRepo: r.Cluster, + stackTemplateRepo: r.StackTemplate, + systemNotificationRuleRepo: r.SystemNotificationRule, + systemNotificationTemplateRepo: r.SystemNotificationTemplate, + argo: argoClient, + kc: kc, } } -func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organization) (organizationId string, err error) { - creator := uuid.Nil - if in.Creator != "" { - creator, err = uuid.Parse(in.Creator) - if err != nil { - return "", err - } +func (u *OrganizationUsecase) Create(ctx context.Context, in *model.Organization) (organizationId string, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } + userId := user.GetUserId() + in.CreatorId = &userId // Create realm in keycloak - if organizationId, err = u.kc.CreateRealm(helper.GenerateOrganizationId()); err != nil { + if organizationId, err = u.kc.CreateRealm(ctx, helper.GenerateOrganizationId()); err != nil { return "", err } + in.ID = organizationId - _, err = u.repo.Create(organizationId, in.Name, creator, in.Phone, in.Description) + // Create organization in DB + _, err = u.repo.Create(ctx, in) if err != nil { return "", err } + workflowId, err := u.argo.SumbitWorkflowFromWftpl( + ctx, "tks-create-contract-repo", argowf.SubmitOptions{ Parameters: []string{ @@ -69,46 +87,89 @@ func (u *OrganizationUsecase) Create(ctx context.Context, in *domain.Organizatio }, }) if err != nil { - log.ErrorWithContext(ctx, "failed to submit argo workflow template. err : ", err) + log.Error(ctx, "failed to submit argo workflow template. err : ", err) return "", errors.Wrap(err, "Failed to call argo workflow") } - log.InfoWithContext(ctx, "submited workflow :", workflowId) + log.Info(ctx, "submited workflow :", workflowId) - if err := u.repo.InitWorkflow(organizationId, workflowId, domain.OrganizationStatus_CREATING); err != nil { + if err := u.repo.InitWorkflow(ctx, organizationId, workflowId, domain.OrganizationStatus_CREATING); err != nil { return "", errors.Wrap(err, "Failed to init workflow") } return organizationId, nil } -func (u *OrganizationUsecase) Fetch(pg *pagination.Pagination) (out *[]domain.Organization, err error) { - organizations, err := u.repo.Fetch(pg) +func (u *OrganizationUsecase) Fetch(ctx context.Context, pg *pagination.Pagination) (out *[]model.Organization, err error) { + organizations, err := u.repo.Fetch(ctx, pg) if err != nil { return nil, err } + + // Make Admin object + users, err := u.userRepo.List(ctx) + if err == nil { + for i, organization := range *organizations { + if organization.AdminId == nil { + continue + } + for _, user := range *users { + user := user + if user.ID == *organization.AdminId { + (*organizations)[i].Admin = &user + } + } + } + } + return organizations, nil } -func (u *OrganizationUsecase) Get(organizationId string) (res domain.Organization, err error) { - res, err = u.repo.Get(organizationId) +func (u *OrganizationUsecase) Get(ctx context.Context, organizationId string) (out model.Organization, err error) { + out, err = u.repo.Get(ctx, organizationId) if err != nil { - return domain.Organization{}, httpErrors.NewNotFoundError(err, "", "") + return model.Organization{}, httpErrors.NewNotFoundError(err, "", "") } - return res, nil + + // Make Admin object + if out.AdminId != nil { + admin, err := u.userRepo.GetByUuid(ctx, *out.AdminId) + if err == nil { + out.Admin = &admin + } + } + + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, uuid.Nil, nil) + if err != nil { + log.Info(ctx, err) + out.ClusterCount = 0 + } + out.ClusterCount = len(clusters) + return out, nil + } -func (u *OrganizationUsecase) Delete(organizationId string, accessToken string) (err error) { - _, err = u.Get(organizationId) +func (u *OrganizationUsecase) Delete(ctx context.Context, organizationId string, accessToken string) (err error) { + _, err = u.Get(ctx, organizationId) if err != nil { return err } // Delete realm in keycloak - if err := u.kc.DeleteRealm(organizationId); err != nil { + if err := u.kc.DeleteRealm(ctx, organizationId); err != nil { return err } - // [TODO] validation - // cluster 나 appgroup 등이 삭제 되었는지 확인 - err = u.repo.Delete(organizationId) + // delete roles in DB + roles, err := u.roleRepo.ListTksRoles(ctx, organizationId, nil) + if err != nil { + return err + } + for _, role := range roles { + if err := u.roleRepo.Delete(ctx, role.ID); err != nil { + return err + } + } + + // delete organization in DB + err = u.repo.Delete(ctx, organizationId) if err != nil { return err } @@ -116,33 +177,47 @@ func (u *OrganizationUsecase) Delete(organizationId string, accessToken string) return nil } -func (u *OrganizationUsecase) Update(organizationId string, in domain.UpdateOrganizationRequest) (domain.Organization, error) { - _, err := u.Get(organizationId) +func (u *OrganizationUsecase) Update(ctx context.Context, organizationId string, in model.Organization) (model.Organization, error) { + _, err := u.Get(ctx, organizationId) if err != nil { - return domain.Organization{}, httpErrors.NewNotFoundError(err, "", "") + return model.Organization{}, httpErrors.NewNotFoundError(err, "", "") } - res, err := u.repo.Update(organizationId, in) + res, err := u.repo.Update(ctx, organizationId, in) if err != nil { - return domain.Organization{}, err + return model.Organization{}, err } return res, nil } -func (u *OrganizationUsecase) UpdatePrimaryClusterId(organizationId string, clusterId string) (err error) { +func (u *OrganizationUsecase) UpdatePrimaryClusterId(ctx context.Context, organizationId string, clusterId string) (err error) { if clusterId != "" && !helper.ValidateClusterId(clusterId) { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterId"), "", "") } - _, err = u.Get(organizationId) + _, err = u.Get(ctx, organizationId) if err != nil { return httpErrors.NewNotFoundError(err, "", "") } - err = u.repo.UpdatePrimaryClusterId(organizationId, clusterId) + err = u.repo.UpdatePrimaryClusterId(ctx, organizationId, clusterId) if err != nil { return err } return nil } + +func (u *OrganizationUsecase) ChangeAdminId(ctx context.Context, organizationId string, adminId uuid.UUID) error { + _, err := u.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewNotFoundError(err, "", "") + } + + err = u.repo.UpdateAdminId(ctx, organizationId, adminId) + if err != nil { + return err + } + + return nil +} diff --git a/internal/usecase/permission.go b/internal/usecase/permission.go new file mode 100644 index 00000000..532e723c --- /dev/null +++ b/internal/usecase/permission.go @@ -0,0 +1,194 @@ +package usecase + +import ( + "context" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/repository" + "sort" +) + +type IPermissionUsecase interface { + CreatePermissionSet(ctx context.Context, permissionSet *model.PermissionSet) error + GetPermissionSetByRoleId(ctx context.Context, roleId string) (*model.PermissionSet, error) + ListPermissions(ctx context.Context, roleId string) ([]*model.Permission, error) + SetRoleIdToPermissionSet(ctx context.Context, roleId string, permissionSet *model.PermissionSet) + GetAllowedPermissionSet(ctx context.Context) *model.PermissionSet + GetUserPermissionSet(ctx context.Context) *model.PermissionSet + UpdatePermission(ctx context.Context, permission *model.Permission) error + MergePermissionWithOrOperator(ctx context.Context, permissionSet ...*model.PermissionSet) *model.PermissionSet +} + +type PermissionUsecase struct { + repo repository.IPermissionRepository +} + +func NewPermissionUsecase(repo repository.Repository) *PermissionUsecase { + return &PermissionUsecase{ + repo: repo.Permission, + } +} + +func (p PermissionUsecase) CreatePermissionSet(ctx context.Context, permissionSet *model.PermissionSet) error { + var err error + if err = p.repo.Create(ctx, permissionSet.Dashboard); err != nil { + return err + } + if err = p.repo.Create(ctx, permissionSet.Stack); err != nil { + return err + } + if err = p.repo.Create(ctx, permissionSet.Policy); err != nil { + return err + } + if err = p.repo.Create(ctx, permissionSet.ProjectManagement); err != nil { + return err + } + if err = p.repo.Create(ctx, permissionSet.Notification); err != nil { + return err + } + if err = p.repo.Create(ctx, permissionSet.Configuration); err != nil { + return err + } + + return nil +} +func (p PermissionUsecase) GetPermissionSetByRoleId(ctx context.Context, roleId string) (*model.PermissionSet, error) { + permissionSet := &model.PermissionSet{ + Dashboard: nil, + Stack: nil, + Policy: nil, + ProjectManagement: nil, + Notification: nil, + Configuration: nil, + } + + permissionList, err := p.repo.List(ctx, roleId) + if err != nil { + return nil, err + } + for _, permission := range permissionList { + switch permission.Name { + case string(model.DashBoardPermission): + permissionSet.Dashboard = permission + case string(model.StackPermission): + permissionSet.Stack = permission + case string(model.PolicyPermission): + permissionSet.Policy = permission + case string(model.ProjectPermission): + permissionSet.ProjectManagement = permission + case string(model.NotificationPermission): + permissionSet.Notification = permission + case string(model.ConfigurationPermission): + permissionSet.Configuration = permission + } + + p.sortPermissionRecursive(permission) + } + + return permissionSet, nil +} + +func (p PermissionUsecase) ListPermissions(ctx context.Context, roleId string) ([]*model.Permission, error) { + permissions, err := p.repo.List(ctx, roleId) + if err != nil { + return nil, err + } + for _, permission := range permissions { + p.sortPermissionRecursive(permission) + } + + return permissions, nil +} + +func (p PermissionUsecase) GetPermission(ctx context.Context, id uuid.UUID) (*model.Permission, error) { + permission, err := p.repo.Get(ctx, id) + if err != nil { + return nil, err + } + p.sortPermissionRecursive(permission) + return permission, nil +} + +func (p PermissionUsecase) DeletePermission(ctx context.Context, id uuid.UUID) error { + return p.repo.Delete(ctx, id) +} + +func (p PermissionUsecase) UpdatePermission(ctx context.Context, permission *model.Permission) error { + return p.repo.Update(ctx, permission) +} + +func (p PermissionUsecase) SetRoleIdToPermissionSet(ctx context.Context, roleId string, permissionSet *model.PermissionSet) { + permissionSet.SetRoleId(roleId) +} + +func (p PermissionUsecase) GetAllowedPermissionSet(ctx context.Context) *model.PermissionSet { + permissionSet := model.NewDefaultPermissionSet() + permissionSet.SetAllowedPermissionSet() + return permissionSet +} + +func (p PermissionUsecase) GetUserPermissionSet(ctx context.Context) *model.PermissionSet { + permissionSet := model.NewDefaultPermissionSet() + permissionSet.SetUserPermissionSet() + return permissionSet +} + +func (p PermissionUsecase) MergePermissionWithOrOperator(ctx context.Context, permissionSet ...*model.PermissionSet) *model.PermissionSet { + var out *model.PermissionSet + for i, ps := range permissionSet { + if i == 0 { + out = ps + continue + } + + out.Dashboard = p.mergePermission(ctx, out.Dashboard, ps.Dashboard) + out.Stack = p.mergePermission(ctx, out.Stack, ps.Stack) + out.Policy = p.mergePermission(ctx, out.Policy, ps.Policy) + out.ProjectManagement = p.mergePermission(ctx, out.ProjectManagement, ps.ProjectManagement) + out.Notification = p.mergePermission(ctx, out.Notification, ps.Notification) + out.Configuration = p.mergePermission(ctx, out.Configuration, ps.Configuration) + } + + return out +} + +func (p PermissionUsecase) mergePermission(ctx context.Context, mergedPermission, permission *model.Permission) *model.Permission { + var mergedEdgePermissions []*model.Permission + mergedEdgePermissions = model.GetEdgePermission(mergedPermission, mergedEdgePermissions, nil) + + var rightEdgePermissions []*model.Permission + rightEdgePermissions = model.GetEdgePermission(permission, rightEdgePermissions, nil) + + for i, rightEdgePermission := range rightEdgePermissions { + *(mergedEdgePermissions[i].IsAllowed) = *(mergedEdgePermissions[i].IsAllowed) || *(rightEdgePermission.IsAllowed) + } + + return mergedPermission +} + +func (p PermissionUsecase) sortPermissionRecursive(permission *model.Permission) { + if len(permission.Children) > 0 { + if permission.Children[0].IsAllowed != nil { + sort.Slice(permission.Children, func(i, j int) bool { + key1 := permission.Children[i].Key + key2 := permission.Children[j].Key + + order1, exists1 := model.SortOrder[key1] + order2, exists2 := model.SortOrder[key2] + + if exists1 && exists2 { + return order1 < order2 + } else if exists1 { + return true + } else if exists2 { + return false + } + + return key1 < key2 + }) + } + for _, child := range permission.Children { + p.sortPermissionRecursive(child) + } + } +} diff --git a/internal/usecase/policy-template.go b/internal/usecase/policy-template.go new file mode 100644 index 00000000..3fb66ac6 --- /dev/null +++ b/internal/usecase/policy-template.go @@ -0,0 +1,814 @@ +package usecase + +import ( + "context" + "fmt" + "strings" + + "github.com/openinfradev/tks-api/pkg/log" + "k8s.io/apimachinery/pkg/api/errors" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + policytemplate "github.com/openinfradev/tks-api/internal/policy-template" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" +) + +type IPolicyTemplateUsecase interface { + Create(ctx context.Context, policyTemplate model.PolicyTemplate) (policyTemplateId uuid.UUID, err error) + Fetch(ctx context.Context, organizationId *string, pg *pagination.Pagination) (policyTemplates []model.PolicyTemplate, err error) + Update(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, templateName *string, description *string, + severity *string, deorecated *bool, permittedOrganizationIds *[]string) (err error) + Get(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (policyTemplates *model.PolicyTemplate, err error) + Delete(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (err error) + IsPolicyTemplateNameExist(ctx context.Context, organizationId *string, policyTemplateName string) (bool, error) + IsPolicyTemplateKindExist(ctx context.Context, organizationId *string, policyTemplateKind string) (bool, error) + GetPolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string) (policyTemplateVersionsReponse *model.PolicyTemplate, err error) + ListPolicyTemplateVersions(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (policyTemplateVersionsReponse *domain.ListPolicyTemplateVersionsResponse, err error) + DeletePolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string) (err error) + CreatePolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, newVersion string, schema []*domain.ParameterDef, rego string, libs []string, + syncKinds *[]string, syncJson *string) (version string, err error) + + RegoCompile(request *domain.RegoCompileRequest, parseParameter bool) (response *domain.RegoCompileResponse, err error) + + // FillPermittedOrganizations(ctx context.Context, + // policyTemplate *model.PolicyTemplate, out *admin_domain.PolicyTemplateResponse) error + // FillPermittedOrganizationsForList(ctx context.Context, + // policyTemplates *[]model.PolicyTemplate, outs *[]admin_domain.PolicyTemplateResponse) error + + ListPolicyTemplateStatistics(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (statistics []model.UsageCount, err error) + GetPolicyTemplateDeploy(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (deployInfo domain.GetPolicyTemplateDeployResponse, err error) + + ExtractPolicyParameters(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string, rego string, libs []string) (response *domain.RegoCompileResponse, err error) + + AddPermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) + UpdatePermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) + DeletePermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) +} + +type PolicyTemplateUsecase struct { + organizationRepo repository.IOrganizationRepository + clusterRepo repository.IClusterRepository + policyRepo repository.IPolicyRepository + repo repository.IPolicyTemplateRepository +} + +func NewPolicyTemplateUsecase(r repository.Repository) IPolicyTemplateUsecase { + return &PolicyTemplateUsecase{ + repo: r.PolicyTemplate, + policyRepo: r.Policy, + organizationRepo: r.Organization, + clusterRepo: r.Cluster, + } +} + +func (u *PolicyTemplateUsecase) Create(ctx context.Context, dto model.PolicyTemplate) (policyTemplateId uuid.UUID, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewUnauthorizedError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + if dto.SyncKinds != nil && dto.SyncJson != nil { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "both sync kinds and json specified") + } + + if dto.SyncKinds != nil { + if _, err := policytemplate.CheckAndConvertToSyncData(*dto.SyncKinds); err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "invalid sync kind") + } + } + + if dto.SyncJson != nil { + result, err := policytemplate.ParseAndCheckSyncData(*dto.SyncJson) + + if err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "invalid sync json") + } + + formatted, err := policytemplate.MarshalSyncData(result) + + if err == nil { + dto.SyncJson = &formatted + } + } + + if dto.IsTksTemplate() { + exists, err := u.repo.ExistByName(ctx, dto.TemplateName) + if err == nil && exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_NAME", "policy template name already exists") + } + + exists, err = u.repo.ExistByKind(ctx, dto.Kind) + if err == nil && exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_KIND", "policy template kind already exists") + } + } else { + exists, err := u.repo.ExistByNameInOrganization(ctx, *dto.OrganizationId, dto.TemplateName) + if err == nil && exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_NAME", "policy template name already exists") + } + + exists, err = u.repo.ExistByKindInOrganization(ctx, *dto.OrganizationId, dto.Kind) + if err == nil && exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_KIND", "policy template kind already exists") + } + } + + if err := policytemplate.ValidateParamDefs(dto.ParametersSchema); err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(err, "PT_INVALID_PARAMETER_SCHEMA", "") + } + + if dto.IsTksTemplate() { + // TKS 템블릿이면 + dto.Mandatory = false + dto.OrganizationId = nil + dto.PermittedOrganizations = make([]model.Organization, len(dto.PermittedOrganizationIds)) + + for i, organizationId := range dto.PermittedOrganizationIds { + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + dto.PermittedOrganizations[i] = organization + } + } else { + dto.PermittedOrganizationIds = make([]string, 0) + dto.PermittedOrganizations = make([]model.Organization, 0) + } + + userId := user.GetUserId() + dto.CreatorId = &userId + + dto.Rego = policytemplate.FormatRegoCode(dto.Rego) + dto.Libs = policytemplate.FormatLibCode(dto.Libs) + + id, err := u.repo.Create(ctx, dto) + + if err != nil { + return uuid.Nil, err + } + + return id, nil +} + +func (u *PolicyTemplateUsecase) Fetch(ctx context.Context, organizationId *string, pg *pagination.Pagination) (policyTemplates []model.PolicyTemplate, err error) { + if organizationId == nil { + policyTemplates, err = u.repo.Fetch(ctx, pg) + } else { + policyTemplates, err = u.repo.FetchForOrganization(ctx, *organizationId, pg) + } + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + + organizations, err := u.organizationRepo.Fetch(ctx, nil) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + + var primaryClusterId string + + if organizationId != nil { + org, err := u.organizationRepo.Get(ctx, *organizationId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } else { + primaryClusterId = org.PrimaryClusterId + } + } + + for i := range policyTemplates { + // 단순히 참조하면 업데이트가 안되므로 pointer derefrencing + policyTemplate := &policyTemplates[i] + if policyTemplate.IsTksTemplate() && len(policyTemplate.PermittedOrganizations) == 0 { + if organizations != nil { + (*policyTemplate).PermittedOrganizations = *organizations + } + } + + if organizationId != nil { + (*policyTemplate).LatestVersion = policyTemplate.Version + + // 에러 없이 primaryClusterId를 가져왔을 때만 처리 + if len(primaryClusterId) > 0 { + + templateCR, err := policytemplate.GetTksPolicyTemplateCR(ctx, primaryClusterId, policyTemplate.ResoureName()) + + if err == nil && templateCR != nil { + (*policyTemplate).CurrentVersion = templateCR.Spec.Version + } else if errors.IsNotFound(err) || templateCR == nil { + // 템플릿이 존재하지 않으면 최신 버전으로 배포되므로 + (*policyTemplate).CurrentVersion = policyTemplate.LatestVersion + } else { + // 통신 실패 등 기타 에러, 버전을 세팅하지 않아 에러임을 알수 있도록 로그를 남김 + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + } + } + } + + return policyTemplates, err +} + +// func (u *PolicyTemplateUsecase) FillPermittedOrganizations(ctx context.Context, +// policyTemplate *model.PolicyTemplate, out *admin_domain.PolicyTemplateResponse) error { +// organizations, err := u.organizationRepo.Fetch(ctx, nil) + +// if err != nil { +// return err +// } + +// u.fillPermittedOrganizations(ctx, organizations, policyTemplate, out) + +// return nil +// } + +// func (u *PolicyTemplateUsecase) FillPermittedOrganizationsForList(ctx context.Context, +// policyTemplates *[]model.PolicyTemplate, outs *[]admin_domain.PolicyTemplateResponse) error { + +// organizations, err := u.organizationRepo.Fetch(ctx, nil) + +// if err != nil { +// return err +// } + +// results := *outs + +// for i, policyTemplate := range *policyTemplates { +// u.fillPermittedOrganizations(ctx, organizations, &policyTemplate, &results[i]) +// } + +// return nil +// } + +// // 모든 조직 목록에 대해 허용 여부 업데이트 +// func (u *PolicyTemplateUsecase) fillPermittedOrganizations(_ context.Context, organizations *[]model.Organization, policyTemplate *model.PolicyTemplate, out *admin_domain.PolicyTemplateResponse) { +// if policyTemplate == nil || organizations == nil || out == nil { +// return +// } + +// if policyTemplate.IsOrganizationTemplate() { +// return +// } + +// // 정책 템플릿에서 허용된 조직 목록이 없다는 것은 모든 조직이 사용할 수 있음을 의미함 +// allPermitted := len(policyTemplate.PermittedOrganizationIds) == 0 + +// // 허용된 조직 포함 여부를 효율적으로 처리하기 위해 ID 리스트를 셋으로 변환 +// permittedOrganizationIdSet := mapset.NewSet(policyTemplate.PermittedOrganizationIds...) + +// out.PermittedOrganizations = make([]admin_domain.PermittedOrganization, len(*organizations)) + +// for i, organization := range *organizations { +// permitted := allPermitted || permittedOrganizationIdSet.ContainsOne(organization.ID) + +// out.PermittedOrganizations[i] = admin_domain.PermittedOrganization{ +// OrganizationId: organization.ID, +// OrganizationName: organization.Name, +// Permitted: permitted, +// } +// } +// } + +func (u *PolicyTemplateUsecase) Get(ctx context.Context, organizationId *string, policyTemplateID uuid.UUID) (policyTemplates *model.PolicyTemplate, err error) { + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateID) + + if err != nil { + return nil, err + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + return nil, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + if policyTemplate.IsTksTemplate() && len(policyTemplate.PermittedOrganizations) == 0 { + organizations, err := u.organizationRepo.Fetch(ctx, nil) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } else if organizations != nil { + policyTemplate.PermittedOrganizations = *organizations + } + } + + return policyTemplate, nil +} + +func (u *PolicyTemplateUsecase) Update(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, templateName *string, description *string, severity *string, deprecated *bool, permittedOrganizationIds *[]string) (err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateId) + if err != nil { + return httpErrors.NewNotFoundError(err, "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if policyTemplate == nil { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + // 다른 Organization의 템플릿을 조작하려고 함, 보안을 위해서 해당 식별자 존재 자체를 알려주면 안되므로 not found + if *organizationId != *policyTemplate.OrganizationId { + return httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + return httpErrors.NewForbiddenError(fmt.Errorf( + "cannot update policy template"), + "PT_NOT_PERMITTED_ON_TKS_POLICY_TEMPLATE", "") + } + + updateMap := make(map[string]interface{}) + + // 기존 이름과 같은 경우면 체크 필요없으므로 다른 경우에만 처리하도록 추가 + if templateName != nil && *templateName != policyTemplate.TemplateName { + if policyTemplate.IsTksTemplate() { + exists, err := u.repo.ExistByName(ctx, *templateName) + if err == nil && exists { + return httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_TEMPLATE_NAME", "policy template name already exists") + } + } else { + exists, err := u.repo.ExistByNameInOrganization(ctx, *organizationId, *templateName) + if err == nil && exists { + return httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_TEMPLATE_NAME", "policy template name already exists") + } + } + + updateMap["template_name"] = templateName + } + + if description != nil { + updateMap["description"] = description + } + + if deprecated != nil { + updateMap["deprecated"] = deprecated + } + + if severity != nil { + updateMap["severity"] = severity + } + + var newPermittedOrganizations *[]model.Organization = nil + + if permittedOrganizationIds != nil { + permittedOrganizations := make([]model.Organization, len(*permittedOrganizationIds)) + + for i, organizationId := range *permittedOrganizationIds { + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + permittedOrganizations[i] = organization + } + newPermittedOrganizations = &permittedOrganizations + } else if len(updateMap) == 0 { + // 허용된 조직도 필드 속성도 업데이트되지 않았으므로 아무것도 업데이트할 것이 없음 + return nil + } + + updatorId := user.GetUserId() + updateMap["updator_id"] = updatorId + + err = u.repo.Update(ctx, policyTemplateId, updateMap, newPermittedOrganizations) + if err != nil { + return err + } + + return nil +} + +func (u *PolicyTemplateUsecase) Delete(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (err error) { + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil { + return err + } + + if policyTemplate == nil { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + // 다른 Organization의 템플릿을 조작하려고 함, 보안을 위해서 해당 식별자 존재 자체를 알려주면 안되므로 not found + if *organizationId != *policyTemplate.OrganizationId { + return httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + return httpErrors.NewForbiddenError(fmt.Errorf( + "cannot delete tks policy template"), + "PT_NOT_PERMITTED_ON_TKS_POLICY_TEMPLATE", "") + } + + return u.repo.Delete(ctx, policyTemplateId) +} + +func (u *PolicyTemplateUsecase) IsPolicyTemplateNameExist(ctx context.Context, organizationId *string, policyTemplateName string) (bool, error) { + if organizationId == nil { + return u.repo.ExistByName(ctx, policyTemplateName) + } + + return u.repo.ExistByNameInOrganization(ctx, *organizationId, policyTemplateName) +} + +func (u *PolicyTemplateUsecase) IsPolicyTemplateKindExist(ctx context.Context, organizationId *string, policyTemplateKind string) (bool, error) { + if organizationId == nil { + return u.repo.ExistByKind(ctx, policyTemplateKind) + } + + return u.repo.ExistByKindInOrganization(ctx, *organizationId, policyTemplateKind) +} + +func (u *PolicyTemplateUsecase) GetPolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string) (policyTemplateVersionsReponse *model.PolicyTemplate, err error) { + policyTemplate, err := u.repo.GetPolicyTemplateVersion(ctx, policyTemplateId, version) + + if err != nil { + return nil, err + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + return nil, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + if policyTemplate.IsTksTemplate() && len(policyTemplate.PermittedOrganizations) == 0 { + organizations, err := u.organizationRepo.Fetch(ctx, nil) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } else if organizations != nil { + policyTemplate.PermittedOrganizations = *organizations + } + } + + return policyTemplate, nil +} + +func (u *PolicyTemplateUsecase) ListPolicyTemplateVersions(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (policyTemplateVersionsReponse *domain.ListPolicyTemplateVersionsResponse, err error) { + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil { + return nil, err + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + return nil, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + return u.repo.ListPolicyTemplateVersions(ctx, policyTemplateId) +} + +func (u *PolicyTemplateUsecase) DeletePolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string) (err error) { + policyTemplate, err := u.repo.GetPolicyTemplateVersion(ctx, policyTemplateId, version) + + if err != nil { + return err + } + + if policyTemplate == nil { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + // 다른 Organization의 템플릿을 조작하려고 함, 보안을 위해서 해당 식별자 존재 자체를 알려주면 안되므로 not found + if *organizationId != *policyTemplate.OrganizationId { + return httpErrors.NewNotFoundError(fmt.Errorf( + "policy template version not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + return httpErrors.NewForbiddenError(fmt.Errorf( + "cannot delete tks policy template version"), + "PT_NOT_PERMITTED_ON_TKS_POLICY_TEMPLATE", "") + } + + return u.repo.DeletePolicyTemplateVersion(ctx, policyTemplateId, version) +} + +func (u *PolicyTemplateUsecase) CreatePolicyTemplateVersion(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, newVersion string, schema []*domain.ParameterDef, rego string, libs []string, + syncKinds *[]string, syncJson *string) (version string, err error) { + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil { + return "", err + } + + if syncKinds != nil && syncJson != nil { + return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "both sync kinds and json specified") + } + + if syncKinds != nil { + if _, err := policytemplate.CheckAndConvertToSyncData(*syncKinds); err != nil { + return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "invalid sync kind") + } + } + + if syncJson != nil { + result, err := policytemplate.ParseAndCheckSyncData(*syncJson) + if err != nil { + return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_INVALID_SYNC", "invalid sync json") + } + + formatted, err := policytemplate.MarshalSyncData(result) + + if err == nil { + syncJson = &formatted + } + } + + if policyTemplate == nil { + return "", httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + // 다른 Organization의 템플릿을 조작하려고 함, 보안을 위해서 해당 식별자 존재 자체를 알려주면 안되므로 not found + if *organizationId != *policyTemplate.OrganizationId { + return "", httpErrors.NewNotFoundError(fmt.Errorf( + "policy template version not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + return "", httpErrors.NewForbiddenError(fmt.Errorf( + "cannot crate tks policy template version"), + "PT_NOT_PERMITTED_ON_TKS_POLICY_TEMPLATE", "") + } + + if err := policytemplate.ValidateParamDefs(policyTemplate.ParametersSchema); err != nil { + return "", httpErrors.NewBadRequestError(err, "PT_INVALID_PARAMETER_SCHEMA", "") + } + + rego = policytemplate.FormatRegoCode(rego) + libs = policytemplate.FormatLibCode(libs) + + return u.repo.CreatePolicyTemplateVersion(ctx, policyTemplateId, newVersion, schema, rego, libs, syncKinds, syncJson) +} + +func (u *PolicyTemplateUsecase) RegoCompile(request *domain.RegoCompileRequest, parseParameter bool) (response *domain.RegoCompileResponse, err error) { + response = &domain.RegoCompileResponse{} + response.Errors = []domain.RegoCompieError{} + + compiler, err := policytemplate.CompileRegoWithLibs(request.Rego, request.Libs) + + if err != nil { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_FAILED_TO_LOAD_REGO_MODULE", + Message: "failed to load rego module", + Text: err.Error(), + }) + } else if compiler.Failed() { + for _, compileError := range compiler.Errors { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_INVALID_REGO_SYNTAX", + Message: "invalid rego syntax", + Text: fmt.Sprintf("[%d:%d] %s", + compileError.Location.Row, compileError.Location.Col, + compileError.Message), + }) + } + } + + if len(response.Errors) > 0 { + return response, nil + } + + if parseParameter { + // 효율적인 파라미터 추출을 위한 머지 + modules, err := policytemplate.MergeAndCompileRegoWithLibs(request.Rego, request.Libs) + if err != nil { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_FAILED_TO_LOAD_REGO_MODULE", + Message: "failed to load merged rego module", + Text: err.Error(), + }) + } else { + response.ParametersSchema = policytemplate.ExtractParameter(modules) + } + } + + return response, nil +} + +func (u *PolicyTemplateUsecase) ListPolicyTemplateStatistics(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (statistics []model.UsageCount, err error) { + return u.policyRepo.GetUsageCountByTemplateId(ctx, organizationId, policyTemplateId) +} + +func (u *PolicyTemplateUsecase) GetPolicyTemplateDeploy(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID) (deployVersions domain.GetPolicyTemplateDeployResponse, err error) { + policyTemplate, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil { + return deployVersions, err + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + return deployVersions, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + if organizationId == nil { + organizations, err := u.organizationRepo.Fetch(ctx, nil) + if err != nil { + return deployVersions, err + } + + deployVersions.DeployVersion = map[string]string{} + for _, organization := range *organizations { + tksPolicyTemplate, err := policytemplate.GetTksPolicyTemplateCR(ctx, organization.PrimaryClusterId, strings.ToLower(policyTemplate.Kind)) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + continue + } + + for clusterId, status := range tksPolicyTemplate.Status.TemplateStatus { + deployVersions.DeployVersion[clusterId] = status.Version + } + } + + return deployVersions, nil + } + + organization, err := u.organizationRepo.Get(ctx, *organizationId) + if err != nil { + return deployVersions, err + } + + tksPolicyTemplate, err := policytemplate.GetTksPolicyTemplateCR(ctx, organization.PrimaryClusterId, strings.ToLower(policyTemplate.Kind)) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return deployVersions, httpErrors.NewInternalServerError(err, "P_FAILED_TO_CALL_KUBERNETES", "") + } + + for clusterId, status := range tksPolicyTemplate.Status.TemplateStatus { + deployVersions.DeployVersion[clusterId] = status.Version + } + + return deployVersions, nil +} + +func (u *PolicyTemplateUsecase) ExtractPolicyParameters(ctx context.Context, organizationId *string, policyTemplateId uuid.UUID, version string, rego string, libs []string) (response *domain.RegoCompileResponse, err error) { + policyTemplate, err := u.repo.GetPolicyTemplateVersion(ctx, policyTemplateId, version) + + if err != nil { + return nil, err + } + + if policyTemplate == nil { + return nil, httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if !policyTemplate.IsPermittedToOrganization(organizationId) { + // 다른 Organization의 템플릿을 조작하려고 함, 보안을 위해서 해당 식별자 존재 자체를 알려주면 안되므로 not found + if *organizationId != *policyTemplate.OrganizationId { + return nil, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template version not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + } + + response = &domain.RegoCompileResponse{} + response.Errors = []domain.RegoCompieError{} + + compiler, err := policytemplate.CompileRegoWithLibs(rego, libs) + + if err != nil { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_FAILED_TO_LOAD_REGO_MODULE", + Message: "failed to load rego module", + Text: err.Error(), + }) + return response, nil + } else if compiler.Failed() { + for _, compileError := range compiler.Errors { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_INVALID_REGO_SYNTAX", + Message: "invalid rego syntax", + Text: fmt.Sprintf("[%d:%d] %s", + compileError.Location.Row, compileError.Location.Col, + compileError.Message), + }) + } + return response, nil + } + + if len(response.Errors) > 0 { + return response, nil + } + + modules, err := policytemplate.MergeAndCompileRegoWithLibs(rego, libs) + if err != nil { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_FAILED_TO_LOAD_REGO_MODULE", + Message: "failed to load merged rego module", + Text: err.Error(), + }) + } + + extractedParamDefs := policytemplate.ExtractParameter(modules) + paramdefs := policyTemplate.ParametersSchema + + newParams, err := policytemplate.GetNewExtractedParamDefs(paramdefs, extractedParamDefs) + + response.ParametersSchema = []*domain.ParameterDef{} + + if err != nil { + response.Errors = append(response.Errors, domain.RegoCompieError{ + Status: 400, + Code: "PT_NOT_COMPATIBLE_PARAMS", + Message: "new parameters are not compatible to current version", + Text: err.Error(), + }) + } else { + response.ParametersSchema = append(response.ParametersSchema, paramdefs...) + response.ParametersSchema = append(response.ParametersSchema, newParams...) + } + + return response, nil +} + +func (u *PolicyTemplateUsecase) AddPermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) { + policyTemplates := make([]model.PolicyTemplate, len(policyTemplateIds)) + + for i, policyTemplateId := range policyTemplateIds { + result, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil || result == nil { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if result.IsOrganizationTemplate() { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to permit organization to organization policy template"), + "PT_FAILED_TO_PERMIT_ORG_TEMPLATE", "") + } + + policyTemplates[i] = *result + } + + return u.organizationRepo.AddPermittedPolicyTemplatesByID(ctx, organizationId, policyTemplates) +} + +func (u *PolicyTemplateUsecase) UpdatePermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) { + policyTemplates := make([]model.PolicyTemplate, len(policyTemplateIds)) + + for i, policyTemplateId := range policyTemplateIds { + result, err := u.repo.GetByID(ctx, policyTemplateId) + + if err != nil || result == nil { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to fetch policy template"), + "PT_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + if result.IsOrganizationTemplate() { + return httpErrors.NewBadRequestError(fmt.Errorf( + "failed to permit organization to organization policy template"), + "PT_FAILED_TO_PERMIT_ORG_TEMPLATE", "") + } + + policyTemplates[i] = *result + } + + return u.organizationRepo.UpdatePermittedPolicyTemplatesByID(ctx, organizationId, policyTemplates) +} + +func (u *PolicyTemplateUsecase) DeletePermittedPolicyTemplatesForOrganization(ctx context.Context, organizationId string, policyTemplateIds []uuid.UUID) (err error) { + return u.organizationRepo.DeletePermittedPolicyTemplatesByID(ctx, organizationId, policyTemplateIds) +} diff --git a/internal/usecase/policy.go b/internal/usecase/policy.go new file mode 100644 index 00000000..c5d6f6ba --- /dev/null +++ b/internal/usecase/policy.go @@ -0,0 +1,1158 @@ +package usecase + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + mapset "github.com/deckarep/golang-set/v2" + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + policytemplate "github.com/openinfradev/tks-api/internal/policy-template" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/internal/serializer" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/strings/slices" +) + +type IPolicyUsecase interface { + Create(ctx context.Context, organizationId string, dto model.Policy) (policyId uuid.UUID, err error) + Update(ctx context.Context, organizationId string, policyId uuid.UUID, + mandatory *bool, policyName *string, description *string, templateId *uuid.UUID, enforcementAction *string, + parameters *string, match *domain.Match, matchYaml *string, targetClusterIds *[]string) (err error) + Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) + Get(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) + GetForEdit(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination, filledParameter bool) (*[]model.Policy, error) + IsPolicyIdExist(ctx context.Context, organizationId string, policyId uuid.UUID) (exists bool, err error) + IsPolicyNameExist(ctx context.Context, organizationId string, policyName string) (exists bool, err error) + IsPolicyResourceNameExist(ctx context.Context, organizationId string, policyResourceName string) (exists bool, err error) + UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusterIds []string) (err error) + SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) + GetMandatoryPolicies(ctx context.Context, organizationId string) (response *domain.GetMandatoryPoliciesResponse, err error) + ListStackPolicyStatus(ctx context.Context, clusterId string, pg *pagination.Pagination) (policyStatuses []domain.StackPolicyStatusResponse, err error) + GetStackPolicyTemplateStatus(ctx context.Context, clusterId string, policyTemplateId uuid.UUID) (clusterPolicyTemplateStatusResponse *domain.GetStackPolicyTemplateStatusResponse, err error) + UpdateStackPolicyTemplateStatus(ctx context.Context, clusterId string, policyTemplateId uuid.UUID, + currentVersion string, targetVerson string) (err error) + GetPolicyStatistics(ctx context.Context, organizationId string) (response *domain.PolicyStatisticsResponse, err error) + AddPoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) + UpdatePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) + DeletePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) + GetStackPolicyStatistics(ctx context.Context, organizationId string, clusterId domain.ClusterId) (statistics *domain.StackPolicyStatistics, err error) + GetPolicyIDsByClusterID(ctx context.Context, clusterId domain.ClusterId) (out *[]uuid.UUID, err error) +} + +type PolicyUsecase struct { + organizationRepo repository.IOrganizationRepository + clusterRepo repository.IClusterRepository + templateRepo repository.IPolicyTemplateRepository + repo repository.IPolicyRepository +} + +func NewPolicyUsecase(r repository.Repository) IPolicyUsecase { + return &PolicyUsecase{ + repo: r.Policy, + templateRepo: r.PolicyTemplate, + organizationRepo: r.Organization, + clusterRepo: r.Cluster, + } +} + +func randomResouceName(kind string) string { + uuid := uuid.New().String() + idStr := strings.Split(uuid, "-") + return strings.ToLower(kind) + "-" + idStr[len(idStr)-1] +} + +func (u *PolicyUsecase) Create(ctx context.Context, organizationId string, dto model.Policy) (policyId uuid.UUID, err error) { + dto.OrganizationId = organizationId + + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewUnauthorizedError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + exists, err := u.repo.ExistByName(ctx, dto.OrganizationId, dto.PolicyName) + if err != nil { + return uuid.Nil, err + } + + if exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_CREATE_ALREADY_EXISTED_NAME", "policy name already exists") + } + + policyTemplate, err := u.templateRepo.GetByID(ctx, dto.TemplateId) + if err != nil { + return uuid.Nil, err + } + + if policyTemplate == nil { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_POlICY_TEMPLATE_NOT_FOUND", "policy template not found") + } + + if len(dto.PolicyResourceName) == 0 { + dto.PolicyResourceName = randomResouceName(policyTemplate.Kind) + } + + exists, err = u.repo.ExistByResourceName(ctx, dto.OrganizationId, dto.PolicyResourceName) + if err != nil { + return uuid.Nil, err + } + + if exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_RESOURCE_NAME", "policy resource name already exists") + } + + dto.TargetClusters = make([]model.Cluster, len(dto.TargetClusterIds)) + for i, clusterId := range dto.TargetClusterIds { + + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return uuid.Nil, httpErrors.NewBadRequestError(err, "P_FAILED_FETCH_CLUSTER", "") + } + dto.TargetClusters[i] = cluster + } + + if !policyTemplate.IsPermittedToOrganization(&organizationId) { + return uuid.Nil, httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + userId := user.GetUserId() + dto.CreatorId = &userId + dto.TemplateId = policyTemplate.ID + + organization, err := u.organizationRepo.Get(ctx, organizationId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + if err := policytemplate.ValidateJSONusingParamdefs(policyTemplate.ParametersSchema, dto.Parameters); err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return uuid.Nil, httpErrors.NewBadRequestError(err, "P_INVALID_POLICY_PARAMETER", "") + } + + primaryClusterId := organization.PrimaryClusterId + + // k8s에 label로 policyID를 기록해 주기 위해 DB 컬럼 생성 시 ID를 생성하지 않고 미리 생성 + dto.ID = uuid.New() + + err = createOrUpdatPolicy(ctx, &dto, policyTemplate, primaryClusterId) + if err != nil { + return uuid.Nil, err + } + + id, err := u.repo.Create(ctx, dto) + + if err != nil { + return uuid.Nil, err + } + + return id, nil +} + +func createOrUpdatPolicy(ctx context.Context, dto *model.Policy, policyTemplate *model.PolicyTemplate, primaryClusterId string) error { + policyCR := policytemplate.PolicyToTksPolicyCR(dto) + + // DB 생성 전 policy DTO에 PolicyTemplate 필드를 넣어주면 탬플릿이 생성될 수 있으므로, + // dto에 세팅하지 않고 변환 후 필요한 템플릿 리소스 이름을 따로 넣어 줌 + if len(policyCR.Spec.Template) == 0 { + policyCR.Spec.Template = policyTemplate.Kind + } + + policyTemplateCR := policytemplate.PolicyTemplateToTksPolicyTemplateCR(policyTemplate) + + exists, err := policytemplate.ExistsTksPolicyTemplateCR(ctx, primaryClusterId, policyTemplateCR.Name) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewInternalServerError(err, "P_FAILED_TO_CALL_KUBERNETES", "") + } + + if !exists { + err = policytemplate.ApplyTksPolicyTemplateCR(ctx, primaryClusterId, policyTemplateCR) + + if err != nil { + errYaml := "" + if policyCR != nil { + errYaml, _ = policyTemplateCR.YAML() + } + + log.Errorf(ctx, "error is :%s(%T), policyTemplateCR='%+v'", err.Error(), err, errYaml) + + return httpErrors.NewInternalServerError(err, "P_FAILED_TO_APPLY_KUBERNETES", "") + } + } + + err = policytemplate.ApplyTksPolicyCR(ctx, primaryClusterId, policyCR) + + // policyYaml, _ := policyCR.YAML() + // policyTemplateYaml, _ := policyTemplateCR.YAML() + + // fmt.Println("-------------------- Policy --------------------") + // fmt.Printf("%+v\n", policyYaml) + // fmt.Println("------------------------------------------------") + // fmt.Println("--------------- Policy Template ----------------") + // fmt.Printf("%+v\n", policyTemplateYaml) + // fmt.Println("------------------------------------------------") + // fmt.Printf("%+v\n", policyTemplateCR.Spec.Targets[0].Rego) + // fmt.Println("------------------------------------------------") + + if err != nil { + errYaml := "" + if policyCR != nil { + errYaml, _ = policyCR.YAML() + } + + log.Errorf(ctx, "error is :%s(%T), policyCR='%+v'", err.Error(), err, errYaml) + + return httpErrors.NewInternalServerError(err, "P_FAILED_TO_APPLY_KUBERNETES", "") + } + + return nil +} + +func (u *PolicyUsecase) Update(ctx context.Context, organizationId string, policyId uuid.UUID, + mandatory *bool, policyName *string, description *string, templateId *uuid.UUID, enforcementAction *string, + parameters *string, match *domain.Match, matchYaml *string, targetClusterIds *[]string) (err error) { + + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return httpErrors.NewNotFoundError(err, "P_FAILED_FETCH_POLICY", "") + } + + updateMap := make(map[string]interface{}) + + if mandatory != nil { + updateMap["mandatory"] = mandatory + } + + // 정책명을 업데이트하기로 설정하였고 + if policyName != nil && policy.PolicyName != *policyName { + exists, err := u.repo.ExistByName(ctx, organizationId, *policyName) + + if err != nil && !errors.IsNotFound(err) { + return err + } + + // 이름이 같은 정책이 존재하지만 아이디가 서로 다른 경우, 즉 다른 정책이 해당 이름 사용 중임 + if exists { + return httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY_NAME", "policy name already exists") + } + + // 해당 이름 사용중인 정책이 없으면 업데이트, 있으면 동일 정책이므로 업데이트 안 함 + updateMap["policy_name"] = policyName + } + + if description != nil { + updateMap["description"] = description + } + + schema := policy.PolicyTemplate.ParametersSchema + + if templateId != nil { + policyTemplate, err := u.templateRepo.GetByID(ctx, *templateId) + schema = policyTemplate.ParametersSchema + + if err != nil { + return err + } + + if !policyTemplate.IsPermittedToOrganization(&organizationId) { + return httpErrors.NewNotFoundError(fmt.Errorf( + "policy template not found"), + "PT_NOT_FOUND_POLICY_TEMPLATE", "") + } + + updateMap["template_id"] = templateId + } + + if enforcementAction != nil { + updateMap["enforcement_action"] = enforcementAction + } + + if parameters != nil { + updateMap["parameters"] = parameters + + if err := policytemplate.ValidateJSONusingParamdefs(schema, *parameters); err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewBadRequestError(err, "P_INVALID_POLICY_PARAMETER", "") + } + } + + if matchYaml != nil { + updateMap["match_yaml"] = matchYaml + updateMap["policy_match"] = nil + } else if match != nil { + updateMap["policy_match"] = match.JSON() + updateMap["match_yaml"] = nil + } + + var newTargetClusters *[]model.Cluster = nil + + if targetClusterIds != nil { + targetClusters := make([]model.Cluster, len(*targetClusterIds)) + + for i, clusterId := range *targetClusterIds { + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), "C_INVALID_CLUSTER_ID", "") + } + + targetClusters[i] = cluster + } + newTargetClusters = &targetClusters + } else if len(updateMap) == 0 { + // 허용된 조직도 필드 속성도 업데이트되지 않았으므로 아무것도 업데이트할 것이 없음 + return nil + } + + updatorId := user.GetUserId() + updateMap["updator_id"] = updatorId + + err = u.repo.Update(ctx, organizationId, policyId, updateMap, newTargetClusters) + if err != nil { + return err + } + + if templateId != nil || enforcementAction != nil || + parameters != nil || match != nil || targetClusterIds != nil { + + organization, err := u.organizationRepo.Get(ctx, organizationId) + + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "") + } + + policyCR := policytemplate.PolicyToTksPolicyCR(policy) + + err = policytemplate.ApplyTksPolicyCR(ctx, organization.PrimaryClusterId, policyCR) + + if err != nil { + log.Errorf(ctx, "failed to apply TksPolicyCR: %v", err) + return err + } + + return err + } + + return nil +} + +func (u *PolicyUsecase) Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) { + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return err + } + + organization, err := u.organizationRepo.Get(ctx, organizationId) + + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + exists, err := policytemplate.ExistsTksPolicyCR(ctx, organization.PrimaryClusterId, policy.PolicyResourceName) + if err != nil { + log.Errorf(ctx, "failed to check TksPolicyCR: %v", err) + return httpErrors.NewInternalServerError(err, "P_FAILED_TO_APPLY_KUBERNETES", "") + } + + if exists { + err = policytemplate.DeleteTksPolicyCR(ctx, organization.PrimaryClusterId, policy.PolicyResourceName) + + if err != nil { + log.Errorf(ctx, "failed to delete TksPolicyCR: %v", err) + return httpErrors.NewInternalServerError(err, "P_FAILED_TO_APPLY_KUBERNETES", "") + } + } + + return u.repo.Delete(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) Get(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) { + return u.repo.GetByID(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) GetForEdit(ctx context.Context, organizationId string, policyId uuid.UUID) (policy *model.Policy, err error) { + policy, err = u.repo.GetByID(ctx, organizationId, policyId) + + if err != nil { + return nil, err + } + + policyTemplate, err := u.templateRepo.GetByID(ctx, policy.TemplateId) + + if policyTemplate != nil { + policy.PolicyTemplate = *policyTemplate + } + + return policy, err +} + +func (u *PolicyUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination, filledParameter bool) (*[]model.Policy, error) { + policies, err := u.repo.Fetch(ctx, organizationId, pg) + + if err != nil { + return nil, err + } + + // 단순 Fetch인 경우에는 Policy에 해당하는 Template만 Join해 줌 + // PolicyTemplate의 최신 버전을 조회해서 파라미터 스키마 등을 조회해서 넣어줘야 함 + if filledParameter { + for i, policy := range *policies { + policyTemplate, err := u.templateRepo.GetByID(ctx, policy.TemplateId) + + if err == nil && policyTemplate != nil { + (*policies)[i].PolicyTemplate = *policyTemplate + } + } + } + + return policies, err +} + +func (u *PolicyUsecase) IsPolicyNameExist(ctx context.Context, organizationId string, policyName string) (exists bool, err error) { + return u.repo.ExistByName(ctx, organizationId, policyName) +} + +func (u *PolicyUsecase) IsPolicyResourceNameExist(ctx context.Context, organizationId string, policyResoName string) (exists bool, err error) { + return u.repo.ExistByResourceName(ctx, organizationId, policyResoName) +} + +func (u *PolicyUsecase) IsPolicyIdExist(ctx context.Context, organizationId string, policyId uuid.UUID) (exists bool, err error) { + return u.repo.ExistByID(ctx, organizationId, policyId) +} + +func (u *PolicyUsecase) UpdatePolicyTargetClusters(ctx context.Context, organizationId string, policyId uuid.UUID, currentClusterIds []string, targetClusterIds []string) (err error) { + targetClusters := make([]model.Cluster, len(targetClusterIds)) + + for i, clusterId := range targetClusterIds { + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid clusterId"), "C_INVALID_CLUSTER_ID", "") + } + + targetClusters[i] = cluster + } + + organization, err := u.organizationRepo.Get(ctx, organizationId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + err = u.repo.UpdatePolicyTargetClusters(ctx, organizationId, policyId, currentClusterIds, targetClusters) + if err != nil { + return err + } + + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid policyId"), "C_INVALID_POLICY_ID", "") + } + + policyCR := policytemplate.PolicyToTksPolicyCR(policy) + + err = policytemplate.ApplyTksPolicyCR(ctx, organization.PrimaryClusterId, policyCR) + + if err != nil { + log.Errorf(ctx, "failed to apply TksPolicyCR: %v", err) + return err + } + + return nil +} + +func (u *PolicyUsecase) SetMandatoryPolicies(ctx context.Context, organizationId string, mandatoryPolicyIds []uuid.UUID, nonMandatoryPolicyIds []uuid.UUID) (err error) { + return u.repo.SetMandatoryPolicies(ctx, organizationId, mandatoryPolicyIds, nonMandatoryPolicyIds) +} + +func (u *PolicyUsecase) GetMandatoryPolicies(ctx context.Context, organizationId string) (response *domain.GetMandatoryPoliciesResponse, err error) { + + var out domain.GetMandatoryPoliciesResponse + + policyTemplates, err := u.templateRepo.Fetch(ctx, nil) + + if err != nil { + return nil, err + } + + templateMaps := map[string]*domain.MandatoryTemplateInfo{} + + for _, policyTemplate := range policyTemplates { + templateId := policyTemplate.ID.String() + + if len(policyTemplate.PermittedOrganizationIds) == 0 || + mapset.NewSet(policyTemplate.PermittedOrganizationIds...).Contains(organizationId) { + templateMaps[templateId] = &domain.MandatoryTemplateInfo{ + TemplateName: policyTemplate.TemplateName, + TemplateId: templateId, + Description: policyTemplate.Description, + Policies: []domain.MandatoryPolicyInfo{}, + } + } + } + + policies, err := u.repo.Fetch(ctx, organizationId, nil) + + if err != nil { + return nil, err + } + + for _, policy := range *policies { + template, ok := templateMaps[policy.TemplateId.String()] + + if ok { + template.Policies = append(template.Policies, domain.MandatoryPolicyInfo{ + PolicyName: policy.PolicyName, + PolicyId: policy.ID.String(), + Description: policy.Description, + Mandatory: policy.Mandatory, + }) + + if policy.Mandatory { + template.Mandatory = true + } + } + } + + for _, template := range templateMaps { + out.Templates = append(out.Templates, *template) + } + + return &out, nil +} + +func (u *PolicyUsecase) ListStackPolicyStatus(ctx context.Context, clusterId string, pg *pagination.Pagination) (policyStatuses []domain.StackPolicyStatusResponse, err error) { + policies, err := u.repo.FetchByClusterId(ctx, clusterId, pg) + + if err != nil { + return nil, err + } + + // policies가 빈 목록일 수도 있으므로 policy의 organization 정보는 못 가져올 수도 있음 + // 따라서 cluster의 Organization에서 primaryClusterId를 가져옴 + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return nil, err + } + + primaryClusterId := cluster.Organization.PrimaryClusterId + + // tksClusterCR, err := policytemplate.GetTksClusterCR(ctx, primaryClusterId, clusterId) + // if err != nil { + // return nil, err + // } + + result := make([]domain.StackPolicyStatusResponse, len(*policies)) + + for i, policy := range *policies { + if err := serializer.Map(ctx, policy, &result[i]); err != nil { + continue + } + + result[i].PolicyId = policy.ID.String() + result[i].PolicyDescription = policy.Description + result[i].PolicyMandatory = policy.Mandatory + latestVersion, _ := u.templateRepo.GetLatestTemplateVersion(ctx, policy.TemplateId) + + tksPolicyTemplateCR, err := policytemplate.GetTksPolicyTemplateCR(ctx, primaryClusterId, policy.PolicyTemplate.ResoureName()) + if err != nil { + return nil, err + } + + result[i].TemplateCurrentVersion = tksPolicyTemplateCR.Spec.Version + + // version, ok := tksClusterCR.Status.Templates[policy.PolicyTemplate.Kind] + + // if ok { + // result[i].TemplateCurrentVersion = version + // } + + result[i].TemplateLatestVersion = latestVersion + result[i].TemplateDescription = policy.PolicyTemplate.Description + } + + return result, nil +} + +func (u *PolicyUsecase) UpdateStackPolicyTemplateStatus(ctx context.Context, clusterId string, policyTemplateId uuid.UUID, + currentVersion string, targetVerson string) (err error) { + if currentVersion == targetVerson { + // 버전 동일, 할일 없음 + return nil + } + + latestTemplate, err := u.templateRepo.GetByID(ctx, policyTemplateId) + if err != nil { + return err + } + + if targetVerson != latestTemplate.Version { + return fmt.Errorf("targetVersion is not a latest version") + } + + currentTemplate, err := u.templateRepo.GetPolicyTemplateVersion(ctx, policyTemplateId, currentVersion) + if err != nil { + return err + } + + // 파라미터 호환성 검증, 파라미터 스키마가 동일하거나 추가된 필드만 있어야 하고 기존 필드는 이름, 타입이 유지되어야 함 + _, err = extractNewTemplateParameter(currentTemplate.ParametersSchema, latestTemplate.ParametersSchema) + if err != nil { + return err + } + + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return err + } + + primaryClusterId := cluster.Organization.PrimaryClusterId + + resourceName := strings.ToLower(latestTemplate.Kind) + + tksPolicyTemplate, err := policytemplate.GetTksPolicyTemplateCR(ctx, primaryClusterId, resourceName) + + if err != nil { + return err + } + + if tksPolicyTemplate.Spec.ToLatest == nil { + tksPolicyTemplate.Spec.ToLatest = []string{} + } + + latestTemplateCR := policytemplate.PolicyTemplateToTksPolicyTemplateCR(latestTemplate) + tksPolicyTemplate.Spec = latestTemplateCR.Spec + + if !slices.Contains(tksPolicyTemplate.Spec.ToLatest, clusterId) { + tksPolicyTemplate.Spec.ToLatest = append(tksPolicyTemplate.Spec.ToLatest, clusterId) + } + + return policytemplate.UpdateTksPolicyTemplateCR(ctx, primaryClusterId, tksPolicyTemplate) +} + +func (u *PolicyUsecase) GetStackPolicyTemplateStatus(ctx context.Context, clusterId string, policyTemplateId uuid.UUID) (stackPolicyTemplateStatusResponse *domain.GetStackPolicyTemplateStatusResponse, err error) { + policies, err := u.repo.FetchByClusterIdAndTemplaeId(ctx, clusterId, policyTemplateId) + + if err != nil { + return nil, err + } + + latestTemplate, err := u.templateRepo.GetByID(ctx, policyTemplateId) + if err != nil { + return nil, err + } + + if latestTemplate == nil { + return nil, httpErrors.NewBadRequestError(err, "P_FAILED_FETCH_TEMPLATE", "") + } + + // policies가 빈 목록일 수도 있으므로 policy의 organization 정보는 못 가져올 수도 있음 + // 따라서 cluster의 Organization에서 primaryClusterId를 가져옴 + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(clusterId)) + if err != nil { + return nil, err + } + + primaryClusterId := cluster.Organization.PrimaryClusterId + + // tksClusterCR, err := policytemplate.GetTksClusterCR(ctx, primaryClusterId, clusterId) + // if err != nil { + // return nil, err + // } + + // version, ok := tksClusterCR.Status.Templates[latestTemplate.Kind] + // + // if !ok { + // return nil, fmt.Errorf("version not found in CR") + // } + + var version string + + tksPolicyTemplateCR, err := policytemplate.GetTksPolicyTemplateCR(ctx, primaryClusterId, latestTemplate.ResoureName()) + if err != nil { + if errors.IsNotFound(err) { + // CR이 배포되지 않았으므로 최신 버전이 현재 버전임(앞으로 설치될 모든 정책은 이 버전으로 설치되므로) + version = latestTemplate.Version + } else { + return nil, err + } + } else { + version = tksPolicyTemplateCR.Spec.Version + } + + currentTemplate, err := u.templateRepo.GetPolicyTemplateVersion(ctx, policyTemplateId, version) + if err != nil { + return nil, err + } + + updatedPolicyParameters, err := extractNewTemplateParameter(currentTemplate.ParametersSchema, latestTemplate.ParametersSchema) + + if err != nil { + return nil, err + } + + affectedPolicies := make([]domain.PolicyStatus, len(*policies)) + + for i, policy := range *policies { + affectedPolicies[i].PolicyId = policy.ID.String() + affectedPolicies[i].PolicyName = policy.PolicyName + + parsed, err := parseParameter(currentTemplate.ParametersSchema, latestTemplate.ParametersSchema, policy.Parameters) + + if err != nil { + return nil, err + } + affectedPolicies[i].PolicyParameters = parsed + } + + result := domain.GetStackPolicyTemplateStatusResponse{ + TemplateName: currentTemplate.TemplateName, + TemplateId: policyTemplateId.String(), + TemplateDescription: currentTemplate.Description, + TemplateLatestVersion: latestTemplate.Version, + TemplateCurrentVersion: currentTemplate.Version, + TemplateLatestVersionReleaseDate: latestTemplate.CreatedAt, + AffectedPolicies: affectedPolicies, + UpdatedPolicyParameters: updatedPolicyParameters, + } + + return &result, nil +} + +func (u *PolicyUsecase) GetPolicyStatistics(ctx context.Context, organizationId string) (response *domain.PolicyStatisticsResponse, err error) { + result := domain.PolicyStatisticsResponse{} + + orgTemplateCount, err := u.templateRepo.CountOrganizationTemplate(ctx, organizationId) + if err != nil { + return nil, err + } + + tksTemplateCount, err := u.templateRepo.CountTksTemplateByOrganization(ctx, organizationId) + if err != nil { + return nil, err + } + + policyStatistics, err := u.repo.CountPolicyByEnforcementAction(ctx, organizationId) + if err != nil { + return nil, err + } + + var policyTotal int64 + var deny int64 + var dryrun int64 + var warn int64 + + for _, stat := range policyStatistics { + switch stat.EnforcementAction { + case "deny": + deny = stat.Count + policyTotal += deny + case "dryrun": + dryrun = stat.Count + policyTotal += dryrun + case "warn": + warn = stat.Count + policyTotal += warn + } + } + + result.Template = domain.TemplateCount{ + TksTemplate: tksTemplateCount, + OrganizationTemplate: orgTemplateCount, + Total: tksTemplateCount + orgTemplateCount, + } + + policyFromOrgTemplates, err := u.templateRepo.CountPolicyFromOrganizationTemplate(ctx, organizationId) + if err != nil { + return nil, err + } + + result.Policy = domain.PolicyCount{ + Deny: deny, + Warn: warn, + Dryrun: dryrun, + FromOrgTemplate: policyFromOrgTemplates, + FromTksTemplate: policyTotal - policyFromOrgTemplates, + Total: policyTotal, + } + + return &result, nil +} + +func (u *PolicyUsecase) AddPoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) { + organization, err := u.organizationRepo.Get(ctx, organizationId) + + primaryClusterId := organization.PrimaryClusterId + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + tkpolicies, err := policytemplate.GetTksPolicyCRs(ctx, primaryClusterId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return err + } + + ids := make([]string, len(policyIds)) + for i, policyId := range policyIds { + ids[i] = policyId.String() + } + + // mapset.NewSet([]string{}) + + for _, tkspolicy := range tkpolicies { + policyId := tkspolicy.GetPolicyID() + + // 추가 대상 + if slices.Contains(ids, policyId) { + // 현재 클러스터가 안 추가되어 있으면 + if !slices.Contains(tkspolicy.Spec.Clusters, string(clusterId)) { + tkspolicy.Spec.Clusters = append(tkspolicy.Spec.Clusters, string(clusterId)) + + err := policytemplate.ApplyTksPolicyCR(ctx, primaryClusterId, &tkspolicy) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + } + + } + } + + policies := []model.Policy{} + + for _, policyId := range policyIds { + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + continue + } + + policies = append(policies, *policy) + } + + return u.repo.AddPoliciesForClusterID(ctx, organizationId, clusterId, policies) +} + +func (u *PolicyUsecase) UpdatePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) { + organization, err := u.organizationRepo.Get(ctx, organizationId) + + primaryClusterId := organization.PrimaryClusterId + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + tkpolicies, err := policytemplate.GetTksPolicyCRs(ctx, primaryClusterId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return err + } + + ids := make([]string, len(policyIds)) + for i, policyId := range policyIds { + ids[i] = policyId.String() + } + + // mapset.NewSet([]string{}) + + for _, tkspolicy := range tkpolicies { + policyId := tkspolicy.GetPolicyID() + + // 세팅 대상 + if slices.Contains(ids, policyId) { + // 현재 클러스터가 안 추가되어 있으면 + if !slices.Contains(tkspolicy.Spec.Clusters, string(clusterId)) { + tkspolicy.Spec.Clusters = append(tkspolicy.Spec.Clusters, string(clusterId)) + + err := policytemplate.ApplyTksPolicyCR(ctx, primaryClusterId, &tkspolicy) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + } + + } else { // 클리어 대상 + // 현재 클러스터가 추가되어 있으면 + if slices.Contains(tkspolicy.Spec.Clusters, string(clusterId)) { + newClusters := []string{} + + tkspolicy.Spec.Clusters = slices.Filter(newClusters, tkspolicy.Spec.Clusters, + func(s string) bool { return s != string(clusterId) }) + + err := policytemplate.ApplyTksPolicyCR(ctx, primaryClusterId, &tkspolicy) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + } + } + } + + policies := []model.Policy{} + + for _, policyId := range policyIds { + policy, err := u.repo.GetByID(ctx, organizationId, policyId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + continue + } + + policies = append(policies, *policy) + } + + return u.repo.UpdatePoliciesForClusterID(ctx, organizationId, clusterId, policies) +} + +func (u *PolicyUsecase) DeletePoliciesForClusterID(ctx context.Context, organizationId string, clusterId domain.ClusterId, policyIds []uuid.UUID) (err error) { + organization, err := u.organizationRepo.Get(ctx, organizationId) + + primaryClusterId := organization.PrimaryClusterId + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + tkpolicies, err := policytemplate.GetTksPolicyCRs(ctx, primaryClusterId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return err + } + + ids := make([]string, len(policyIds)) + for i, policyId := range policyIds { + ids[i] = policyId.String() + } + + for _, tkspolicy := range tkpolicies { + policyId := tkspolicy.GetPolicyID() + + if slices.Contains(ids, policyId) { + // 현재 클러스터가 추가되어 있으면 제거 + if slices.Contains(tkspolicy.Spec.Clusters, string(clusterId)) { + newClusters := []string{} + + tkspolicy.Spec.Clusters = slices.Filter(newClusters, tkspolicy.Spec.Clusters, + func(s string) bool { return s != string(clusterId) }) + err := policytemplate.ApplyTksPolicyCR(ctx, primaryClusterId, &tkspolicy) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + } + } + } + } + + return u.repo.DeletePoliciesForClusterID(ctx, organizationId, clusterId, policyIds) +} + +func (u *PolicyUsecase) GetStackPolicyStatistics(ctx context.Context, organizationId string, clusterId domain.ClusterId) (statistics *domain.StackPolicyStatistics, err error) { + organization, err := u.organizationRepo.Get(ctx, organizationId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + + primaryClusterId := organization.PrimaryClusterId + + templateList, err := policytemplate.GetTksPolicyTemplateCRs(ctx, primaryClusterId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return nil, httpErrors.NewInternalServerError(fmt.Errorf("fail to get template list from kubernetes"), "P_FAILED_TO_CALL_KUBERNETES", "") + } + + totalTemplateCount := len(templateList) + outdatedTemplateIds := []string{} + + for _, template := range templateList { + templateId := template.GetId() + + id, err := uuid.Parse(templateId) + + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + continue + } + + version, err := u.templateRepo.GetLatestTemplateVersion(ctx, id) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + continue + } + + if version != template.Spec.Version { + outdatedTemplateIds = append(outdatedTemplateIds, templateId) + } + } + + outdatedTemplateCount := len(outdatedTemplateIds) + uptodateTemplateCount := totalTemplateCount - outdatedTemplateCount + + policyList, err := policytemplate.GetTksPolicyCRs(ctx, primaryClusterId) + if err != nil { + log.Errorf(ctx, "error is :%s(%T)", err.Error(), err) + + return nil, httpErrors.NewInternalServerError(fmt.Errorf("fail to get policy list from kubernetes"), "P_FAILED_TO_CALL_KUBERNETES", "") + } + + outdatedPolicyCount := 0 + + for _, policy := range policyList { + templateId := policy.GetTemplateID() + + if slices.Contains(outdatedTemplateIds, templateId) { + outdatedPolicyCount++ + } + } + + tototalPolicyCount := len(policyList) + + uptodatePolicyCount := tototalPolicyCount - outdatedPolicyCount + + result := domain.StackPolicyStatistics{ + TotalTemplateCount: totalTemplateCount, + UptodateTemplateCount: uptodateTemplateCount, + OutofdateTemplateCount: outdatedTemplateCount, + TotalPolicyCount: tototalPolicyCount, + UptodatePolicyCount: uptodatePolicyCount, + OutofdatePolicyCount: outdatedPolicyCount, + } + + return &result, nil +} + +func (u *PolicyUsecase) GetPolicyIDsByClusterID(ctx context.Context, clusterId domain.ClusterId) (out *[]uuid.UUID, err error) { + return u.repo.GetPolicyIDsByClusterID(ctx, clusterId) +} + +func extractNewTemplateParameter(paramdefs []*domain.ParameterDef, newParamDefs []*domain.ParameterDef) (policyParameters []domain.UpdatedPolicyTemplateParameter, err error) { + diffParamDef, err := policytemplate.GetNewParamDefs(paramdefs, newParamDefs) + + if err != nil { + return nil, err + } + + results := []domain.UpdatedPolicyTemplateParameter{} + + // 새버전에 추가된 파라미터 + for _, paramdef := range diffParamDef { + result := domain.UpdatedPolicyTemplateParameter{ + Name: paramdef.Key, + Type: paramdef.Type, + Value: paramdef.DefaultValue, + } + + results = append(results, result) + } + + return results, nil +} + +func parseParameter(paramdefs []*domain.ParameterDef, newParamDefs []*domain.ParameterDef, parameter string) (policyParameters []domain.PolicyParameter, err error) { + diffParamDef, err := policytemplate.GetNewParamDefs(paramdefs, newParamDefs) + + if err != nil { + return nil, err + } + + paramMap, err := parameterToValueMap(parameter) + if err != nil { + return nil, err + } + + // 기존 파라미터 + results := []domain.PolicyParameter{} + for _, paramdef := range paramdefs { + result := domain.PolicyParameter{ + Name: paramdef.Key, + Type: paramdef.Type, + Value: paramdef.DefaultValue, + Updatable: false, + } + + if val, ok := paramMap[paramdef.Key]; ok { + result.Value = val + } + + results = append(results, result) + } + + // 새버전에 추가된 파라미터 + for _, paramdef := range diffParamDef { + result := domain.PolicyParameter{ + Name: paramdef.Key, + Type: paramdef.Type, + Value: paramdef.DefaultValue, + Updatable: true, + } + + results = append(results, result) + } + + return results, nil +} + +func parameterToValueMap(parameters string) (parameterMap map[string]string, err error) { + var obj map[string]interface{} + result := map[string]string{} + + err = json.Unmarshal([]byte(parameters), &obj) + + for k, v := range obj { + value, err := json.Marshal(v) + + if err != nil { + return nil, err + } + + result[k] = string(value) + } + + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/internal/usecase/project.go b/internal/usecase/project.go new file mode 100644 index 00000000..2ad792a4 --- /dev/null +++ b/internal/usecase/project.go @@ -0,0 +1,965 @@ +package usecase + +import ( + "context" + "fmt" + "strconv" + "time" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/keycloak" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/internal/serializer" + argowf "github.com/openinfradev/tks-api/pkg/argo-client" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/kubernetes" + "github.com/openinfradev/tks-api/pkg/log" + thanos "github.com/openinfradev/tks-api/pkg/thanos-client" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ProjectAll int = iota + ProjectLeader + ProjectMember + ProjectViewer +) + +type IProjectUsecase interface { + CreateProject(ctx context.Context, p *model.Project) (string, error) + GetProjects(ctx context.Context, organizationId string, userId string, onlyMyProject bool, projectName string, pg *pagination.Pagination) ([]domain.ProjectResponse, error) + GetProject(ctx context.Context, organizationId string, projectId string) (*model.Project, error) + GetProjectWithLeader(ctx context.Context, organizationId string, projectId string) (*model.Project, error) + IsProjectNameExist(ctx context.Context, organizationId string, projectName string) (bool, error) + UpdateProject(ctx context.Context, p *model.Project, newLeaderId string) error + GetProjectRole(ctx context.Context, id string) (*model.ProjectRole, error) + GetProjectRoles(ctx context.Context, query int) ([]model.ProjectRole, error) + AddProjectMember(ctx context.Context, organizationId string, pm *model.ProjectMember) (string, error) + GetProjectUser(ctx context.Context, projectUserId string) (*model.ProjectUser, error) + GetProjectMember(ctx context.Context, projectMemberId string) (*model.ProjectMember, error) + GetProjectMemberByUserId(ctx context.Context, projectId string, userId string) (*model.ProjectMember, error) + GetProjectMembers(ctx context.Context, projectId string, query int, pg *pagination.Pagination) ([]model.ProjectMember, error) + GetProjectMemberCount(ctx context.Context, projectMemberId string) (*domain.GetProjectMemberCountResponse, error) + RemoveProjectMember(ctx context.Context, organizationId string, projectMemberId string) error + UpdateProjectMemberRole(ctx context.Context, pm *model.ProjectMember) error + CreateProjectNamespace(ctx context.Context, organizationId string, pn *model.ProjectNamespace) error + IsProjectNamespaceExist(ctx context.Context, organizationId string, projectId string, stackId string, projectNamespace string) (bool, error) + GetProjectNamespaces(ctx context.Context, organizationId string, projectId string, pg *pagination.Pagination) ([]model.ProjectNamespace, error) + GetProjectNamespace(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) (*model.ProjectNamespace, error) + UpdateProjectNamespace(ctx context.Context, pn *model.ProjectNamespace) error + DeleteProjectNamespace(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) error + GetAppCount(ctx context.Context, organizationId string, projectId string, namespace string) (appCount int, err error) + EnsureNamespaceForCluster(ctx context.Context, organizationId string, stackId string, namespaceName string) error + EnsureRequiredSetupForCluster(ctx context.Context, organizationId string, projectId string, stackId string) error + MayRemoveRequiredSetupForCluster(ctx context.Context, organizationId string, projectId string, stackId string) error + CreateK8SNSRoleBinding(ctx context.Context, organizationId string, projectId string, stackId string, namespace string) error + DeleteK8SNSRoleBinding(ctx context.Context, organizationId string, projectId string, stackId string, namespace string) error + GetProjectNamespaceKubeconfig(ctx context.Context, organizationId string, projectId string, namespace string, stackId domain.StackId) (string, error) + GetProjectKubeconfig(ctx context.Context, organizationId string, projectId string) (string, error) + GetK8sResources(ctx context.Context, organizationId string, projectId string, namespace string, stackId domain.StackId) (out domain.ProjectNamespaceK8sResources, err error) + GetResourcesUsage(ctx context.Context, thanosClient thanos.ThanosClient, organizationId string, projectId string, namespace string, stackId domain.StackId) (out domain.ProjectNamespaceResourcesUsage, err error) + AssignKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, projectMemberId string) error + UnassignKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, projectMemberId string) error +} + +type ProjectUsecase struct { + projectRepo repository.IProjectRepository + userRepository repository.IUserRepository + authRepository repository.IAuthRepository + clusterRepository repository.IClusterRepository + appgroupRepository repository.IAppGroupRepository + organizationRepository repository.IOrganizationRepository + argo argowf.ArgoClient + kc keycloak.IKeycloak +} + +func NewProjectUsecase(r repository.Repository, kc keycloak.IKeycloak, argoClient argowf.ArgoClient) IProjectUsecase { + return &ProjectUsecase{ + projectRepo: r.Project, + userRepository: r.User, + authRepository: r.Auth, + clusterRepository: r.Cluster, + appgroupRepository: r.AppGroup, + organizationRepository: r.Organization, + argo: argoClient, + kc: kc, + } +} + +func (u *ProjectUsecase) CreateProject(ctx context.Context, p *model.Project) (string, error) { + projectId, err := u.projectRepo.CreateProject(ctx, p) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to create project.") + } + + prs, err := u.GetProjectRoles(ctx, ProjectAll) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to retrieve project roles.") + } + log.Debugf(ctx, "Project roles: %v", prs) + for _, pr := range prs { + log.Debugf(ctx, "Start to create Project role: %s. orgId: %s, projectId: %s", pr.Name, p.OrganizationId, projectId) + err = u.kc.EnsureClientRoleWithClientName(ctx, p.OrganizationId, keycloak.DefaultClientID, pr.Name+"@"+projectId) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to create project setting on keycloak.") + } + } + + return projectId, nil +} + +func (u *ProjectUsecase) GetProjects(ctx context.Context, organizationId string, userId string, onlyMyProject bool, projectName string, pg *pagination.Pagination) (pr []domain.ProjectResponse, err error) { + if userId == "" { + if pr, err = u.projectRepo.GetAllProjects(ctx, organizationId, projectName, pg); err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get projects.") + } + } else { + userUuid, err := uuid.Parse(userId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to parse uuid to string") + } + if !onlyMyProject { + pr, err = u.projectRepo.GetProjects(ctx, organizationId, userUuid, projectName, pg) + } else { + pr, err = u.projectRepo.GetProjectsByUserId(ctx, organizationId, userUuid, projectName, pg) + } + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get projects.") + } + } + + return pr, err +} + +func (u *ProjectUsecase) GetProject(ctx context.Context, organizationId string, projectId string) (*model.Project, error) { + p, err := u.projectRepo.GetProjectById(ctx, organizationId, projectId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get projects.") + } + return p, err +} + +func (u *ProjectUsecase) GetProjectWithLeader(ctx context.Context, organizationId string, projectId string) (*model.Project, error) { + p, err := u.projectRepo.GetProjectByIdAndLeader(ctx, organizationId, projectId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get projects.") + } + return p, err +} + +func (u *ProjectUsecase) IsProjectNameExist(ctx context.Context, organizationId string, projectName string) (bool, error) { + exist := true + p, err := u.projectRepo.GetProjectByName(ctx, organizationId, projectName) + if err != nil { + log.Error(ctx, err) + exist = false + return exist, errors.Wrap(err, "Failed to retrieve project name.") + } + if p == nil { + exist = false + } + return exist, nil +} + +func (u *ProjectUsecase) UpdateProject(ctx context.Context, p *model.Project, newLeaderId string) error { + //TODO: [donggyu] have to implementation about un/assigning client roles + + var currentMemberId, currentLeaderId, projectRoleId string + for _, pm := range p.ProjectMembers { + currentMemberId = pm.ID + currentLeaderId = pm.ProjectUser.ID.String() + projectRoleId = pm.ProjectRole.ID + } + p.ProjectNamespaces = nil + p.ProjectMembers = nil + + // only project table update + if err := u.projectRepo.UpdateProject(ctx, p); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to update project.") + } + + if newLeaderId != "" && currentLeaderId != newLeaderId { + pu, err := u.GetProjectUser(ctx, newLeaderId) + if err != nil { + return err + } + if pu == nil { + return errors.Wrap(err, "The user doesn't exist.") + } + + // If project leader exists, remove leader + if currentMemberId != "" { + if err := u.RemoveProjectMember(ctx, p.OrganizationId, currentMemberId); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove project member.") + } + } + + if projectRoleId == "" { + pr, err := u.projectRepo.GetProjectRoleByName(ctx, "project-leader") + if err != nil { + return err + } + projectRoleId = pr.ID + } + + // If the Member does not exist in the Project, insert it, but if it does, update it. + pm, err := u.projectRepo.GetProjectMemberByUserId(ctx, p.ID, newLeaderId) + if err != nil { + return err + } + if pm == nil { + newPm := &model.ProjectMember{ + ProjectId: p.ID, + ProjectUserId: pu.ID, + ProjectUser: nil, + ProjectRoleId: projectRoleId, + ProjectRole: nil, + IsProjectLeader: true, + CreatedAt: *p.UpdatedAt, + } + res, err := u.AddProjectMember(ctx, p.OrganizationId, newPm) + if err != nil { + return err + } + log.Infof(ctx, "Added project member: %s", res) + } else { + pm.ProjectUserId = pu.ID + pm.ProjectRoleId = projectRoleId + pm.IsProjectLeader = true + pm.UpdatedAt = p.UpdatedAt + pm.ProjectUser = nil + pm.ProjectRole = nil + err := u.UpdateProjectMemberRole(ctx, pm) + if err != nil { + return err + } + } + } + + return nil +} + +func (u *ProjectUsecase) GetProjectRole(ctx context.Context, id string) (*model.ProjectRole, error) { + pr, err := u.projectRepo.GetProjectRoleById(ctx, id) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get project roles.") + } + + return pr, nil +} + +func (u *ProjectUsecase) GetProjectRoles(ctx context.Context, query int) (prs []model.ProjectRole, err error) { + var pr *model.ProjectRole + + if query == ProjectLeader { + pr, err = u.projectRepo.GetProjectRoleByName(ctx, "project-leader") + } else if query == ProjectMember { + pr, err = u.projectRepo.GetProjectRoleByName(ctx, "project-member") + } else if query == ProjectViewer { + pr, err = u.projectRepo.GetProjectRoleByName(ctx, "project-viewer") + } else { + prs, err = u.projectRepo.GetAllProjectRoles(ctx) + } + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get project roles.") + } + + if pr != nil { + prs = append(prs, *pr) + } + + return prs, nil +} + +func (u *ProjectUsecase) AddProjectMember(ctx context.Context, organizationId string, pm *model.ProjectMember) (string, error) { + projectMemberId, err := u.projectRepo.AddProjectMember(ctx, pm) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to add project member to project.") + } + + return projectMemberId, nil +} + +func (u *ProjectUsecase) GetProjectUser(ctx context.Context, projectUserId string) (*model.ProjectUser, error) { + var uid uuid.UUID + uid, err := uuid.Parse(projectUserId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to parse uuid to string") + } + + user, err := u.userRepository.GetByUuid(ctx, uid) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to retrieve user by id") + } + var pu model.ProjectUser + if err = serializer.Map(ctx, user, &pu); err != nil { + log.Error(ctx, err) + return nil, err + } + return &pu, nil +} + +func (u *ProjectUsecase) GetProjectMember(ctx context.Context, projectMemberId string) (pm *model.ProjectMember, err error) { + pm, err = u.projectRepo.GetProjectMemberById(ctx, projectMemberId) + if err != nil { + log.Error(ctx, err) + return pm, errors.Wrap(err, "Failed to get project member.") + } + + return pm, nil +} + +func (u *ProjectUsecase) GetProjectMemberByUserId(ctx context.Context, projectId string, userId string) (pm *model.ProjectMember, err error) { + pm, err = u.projectRepo.GetProjectMemberByUserId(ctx, projectId, userId) + if err != nil { + log.Error(ctx, err) + return pm, errors.Wrap(err, "Failed to get project member.") + } + + return pm, nil +} + +func (u *ProjectUsecase) GetProjectMembers(ctx context.Context, projectId string, query int, pg *pagination.Pagination) (pms []model.ProjectMember, err error) { + if query == ProjectLeader { + pms, err = u.projectRepo.GetProjectMembersByProjectIdAndRoleName(ctx, projectId, "project-leader", pg) + } else if query == ProjectMember { + pms, err = u.projectRepo.GetProjectMembersByProjectIdAndRoleName(ctx, projectId, "project-member", pg) + } else if query == ProjectViewer { + pms, err = u.projectRepo.GetProjectMembersByProjectIdAndRoleName(ctx, projectId, "project-viewer", pg) + } else { + pms, err = u.projectRepo.GetProjectMembersByProjectId(ctx, projectId, pg) + } + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get project members.") + } + + return pms, nil +} + +func (u *ProjectUsecase) GetProjectMemberCount(ctx context.Context, projectMemberId string) (pmcr *domain.GetProjectMemberCountResponse, err error) { + pmcr, err = u.projectRepo.GetProjectMemberCountByProjectId(ctx, projectMemberId) + if err != nil { + log.Error(ctx, err) + return pmcr, errors.Wrap(err, "Failed to get project member count.") + } + + return pmcr, nil +} + +func (u *ProjectUsecase) RemoveProjectMember(ctx context.Context, organizationId string, projectMemberId string) error { + pm, err := u.GetProjectMember(ctx, projectMemberId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get project member to project.") + } + // unassign keycloak client role to member + err = u.kc.UnassignClientRoleToUser(ctx, organizationId, pm.ProjectUserId.String(), keycloak.DefaultClientID, pm.ProjectRole.Name+"@"+pm.ProjectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove project member to project.") + } + + if err := u.projectRepo.RemoveProjectMember(ctx, projectMemberId); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove project member to project.") + } + + return nil +} + +func (u *ProjectUsecase) UpdateProjectMemberRole(ctx context.Context, pm *model.ProjectMember) error { + + if err := u.projectRepo.UpdateProjectMemberRole(ctx, pm); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove project member to project.") + } + return nil +} + +func (u *ProjectUsecase) CreateProjectNamespace(ctx context.Context, organizationId string, pn *model.ProjectNamespace) error { + if err := u.projectRepo.CreateProjectNamespace(ctx, organizationId, pn); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to create project namespace.") + } + return nil +} + +func (u *ProjectUsecase) IsProjectNamespaceExist(ctx context.Context, organizationId string, projectId string, stackId string, projectNamespace string) (bool, error) { + exist := true + pn, err := u.projectRepo.GetProjectNamespaceByName(ctx, organizationId, projectId, stackId, projectNamespace) + if err != nil { + log.Error(ctx, err) + exist = false + return exist, errors.Wrap(err, "Failed to retrieve project namespace.") + } + if pn == nil { + exist = false + } + return exist, nil +} + +func (u *ProjectUsecase) GetProjectNamespaces(ctx context.Context, organizationId string, projectId string, pg *pagination.Pagination) ([]model.ProjectNamespace, error) { + pns, err := u.projectRepo.GetProjectNamespaces(ctx, organizationId, projectId, pg) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to retrieve project namespaces.") + } + + for _, pn := range pns { + o, err := u.organizationRepository.Get(ctx, organizationId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get organization.") + } + + appGroupsInPrimaryCluster, err := u.appgroupRepository.Fetch(ctx, domain.ClusterId(o.PrimaryClusterId), nil) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get app groups.") + } + + for i, appGroup := range appGroupsInPrimaryCluster { + if appGroup.AppGroupType == domain.AppGroupType_LMA { + applications, err := u.appgroupRepository.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get applications.") + } + if len(applications) > 0 { + pns[i].GrafanaUrl = applications[0].Endpoint + "/d/tks_namespace_dashboard/tks-kubernetes-view-namespaces?orgId=" + organizationId + "&var-datasource=Prometheus&var-taco_cluster=" + pn.StackId + "&var-namespace=" + pn.Namespace + } + } + } + } + + return pns, nil +} + +func (u *ProjectUsecase) GetProjectNamespace(ctx context.Context, organizationId string, projectId string, projectNamespace string, stackId string) (*model.ProjectNamespace, error) { + pn, err := u.projectRepo.GetProjectNamespaceByPrimaryKey(ctx, organizationId, projectId, projectNamespace, stackId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to retrieve project namespace.") + } + o, err := u.organizationRepository.Get(ctx, organizationId) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get organization.") + } + + appGroupsInPrimaryCluster, err := u.appgroupRepository.Fetch(ctx, domain.ClusterId(o.PrimaryClusterId), nil) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get app groups.") + } + + for _, appGroup := range appGroupsInPrimaryCluster { + if appGroup.AppGroupType == domain.AppGroupType_LMA { + applications, err := u.appgroupRepository.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) + if err != nil { + log.Error(ctx, err) + return nil, errors.Wrap(err, "Failed to get applications.") + } + if len(applications) > 0 { + pn.GrafanaUrl = applications[0].Endpoint + "/d/tks_namespace_dashboard/tks-kubernetes-view-namespaces?orgId=" + organizationId + "&var-datasource=Prometheus&var-taco_cluster=" + stackId + "&var-namespace=" + projectNamespace + } + } + } + return pn, nil +} + +func (u *ProjectUsecase) UpdateProjectNamespace(ctx context.Context, pn *model.ProjectNamespace) error { + if err := u.projectRepo.UpdateProjectNamespace(ctx, pn); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to update project namespace") + } + return nil +} + +func (u *ProjectUsecase) DeleteProjectNamespace(ctx context.Context, organizationId string, projectId string, + stackId string, projectNamespace string) error { + if err := u.projectRepo.DeleteProjectNamespace(ctx, organizationId, projectId, projectNamespace, stackId); err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to delete project namespace.") + } + return nil +} + +func (u *ProjectUsecase) GetAppCount(ctx context.Context, organizationId string, projectId string, namespace string) (appCount int, err error) { + if namespace == "" { + appCount, err = u.projectRepo.GetAppCountByProjectId(ctx, organizationId, projectId) + } else { + appCount, err = u.projectRepo.GetAppCountByNamespace(ctx, organizationId, projectId, namespace) + } + if err != nil { + log.Error(ctx, err) + return 0, errors.Wrap(err, "Failed to retrieve app count.") + } + + return appCount, nil +} + +func (u *ProjectUsecase) EnsureRequiredSetupForCluster(ctx context.Context, organizationId string, projectId string, stackId string) error { + _, err := u.projectRepo.GetProjectNamespaces(ctx, organizationId, projectId, nil) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get project namespace in database.") + } + + if err := u.createK8SInitialResource(ctx, organizationId, projectId, stackId); err != nil { + log.Error(ctx, err) + return err + } + + if err := u.createKeycloakClientRoles(ctx, organizationId, projectId, stackId+"-k8s-api"); err != nil { + log.Error(ctx, err) + return err + } + + projectMembers, err := u.GetProjectMembers(ctx, projectId, ProjectAll, nil) + if err != nil { + log.Error(ctx, err) + return err + } + for _, pm := range projectMembers { + err = u.assignEachKeycloakClientRoleToMember(ctx, organizationId, projectId, stackId+"-k8s-api", pm.ProjectUserId.String(), pm.ProjectRole.Name) + if err != nil { + log.Error(ctx, err) + return err + } + } + + return nil +} +func (u *ProjectUsecase) MayRemoveRequiredSetupForCluster(ctx context.Context, organizationId string, projectId string, stackId string) error { + pns, err := u.projectRepo.GetProjectNamespaces(ctx, organizationId, projectId, nil) + if err != nil { + return err + } + var nsCount int + for _, pn := range pns { + if pn.StackId == stackId { + nsCount++ + } + } + + // if there are more than one namespace, it means that required setup is needed on the other namespace + if nsCount > 1 { + return nil + } + + if err := u.deleteK8SInitialResource(ctx, organizationId, projectId, stackId); err != nil { + return err + } + + projectMembers, err := u.GetProjectMembers(ctx, projectId, ProjectAll, nil) + if err != nil { + return err + } + for _, pm := range projectMembers { + err = u.unassignKeycloakClientRoleToMember(ctx, organizationId, projectId, stackId+"-k8s-api", pm.ProjectUserId.String(), pm.ProjectRole.Name) + if err != nil { + return err + } + } + + if err := u.deleteKeycloakClientRoles(ctx, organizationId, projectId, stackId+"-k8s-api"); err != nil { + return err + } + + return nil +} +func (u *ProjectUsecase) createK8SInitialResource(ctx context.Context, organizationId string, projectId string, stackId string) error { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId, kubernetes.KubeconfigForAdmin) + if err != nil { + return errors.Wrap(err, "Failed to get kubeconfig.") + } + + err = kubernetes.EnsureClusterRole(ctx, kubeconfig, projectId) + if err != nil { + return err + } + + err = kubernetes.EnsureCommonClusterRole(ctx, kubeconfig, projectId) + if err != nil { + return err + } + + err = kubernetes.EnsureCommonClusterRoleBinding(ctx, kubeconfig, projectId) + if err != nil { + return err + } + + return nil +} +func (u *ProjectUsecase) deleteK8SInitialResource(ctx context.Context, organizationId string, projectId string, stackId string) error { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId, kubernetes.KubeconfigForAdmin) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get kubeconfig.") + } + + err = kubernetes.RemoveClusterRole(ctx, kubeconfig, projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove cluster roles.") + } + + err = kubernetes.RemoveCommonClusterRole(ctx, kubeconfig, projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove common cluster roles.") + } + + err = kubernetes.RemoveCommonClusterRoleBinding(ctx, kubeconfig, projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to remove common cluster role bindings.") + } + + return nil +} +func (u *ProjectUsecase) createKeycloakClientRoles(ctx context.Context, organizationId string, projectId string, clientId string) error { + // create Roles in keycloak + roles := []string{"project-leader", "project-member", "project-viewer"} + for _, role := range roles { + err := u.kc.EnsureClientRoleWithClientName(ctx, organizationId, clientId, role+"@"+projectId) + if err != nil { + return errors.Wrap(err, "Failed to create keycloak client role.") + } + + } + return nil +} +func (u *ProjectUsecase) deleteKeycloakClientRoles(ctx context.Context, organizationId string, projectId string, clientId string) error { + // first check whether the stac + + // delete Roles in keycloak + for _, role := range []string{strconv.Itoa(ProjectLeader), strconv.Itoa(ProjectMember), strconv.Itoa(ProjectViewer)} { + err := u.kc.DeleteClientRoleWithClientName(ctx, organizationId, clientId, role+"@"+projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to delete keycloak client roles.") + } + } + return nil +} +func (u *ProjectUsecase) CreateK8SNSRoleBinding(ctx context.Context, organizationId string, projectId string, stackId string, namespace string) error { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId, kubernetes.KubeconfigForAdmin) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get kubeconfig.") + } + + err = kubernetes.EnsureRoleBinding(ctx, kubeconfig, projectId, namespace) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to create K8s role binding.") + } + + return nil +} +func (u *ProjectUsecase) DeleteK8SNSRoleBinding(ctx context.Context, organizationId string, projectId string, stackId string, namespace string) error { + //TODO implement me + return nil +} + +func (u *ProjectUsecase) AssignKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, projectMemberId string) error { + pm, err := u.GetProjectMember(ctx, projectMemberId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get project member.") + } + err = u.assignEachKeycloakClientRoleToMember(ctx, organizationId, projectId, clientId, pm.ProjectUserId.String(), pm.ProjectRole.Name) + if err != nil { + log.Error(ctx, err) + return err + } + return nil +} + +func (u *ProjectUsecase) assignEachKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, userId string, roleName string) error { + err := u.kc.AssignClientRoleToUser(ctx, organizationId, userId, clientId, roleName+"@"+projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to assign each KeycloakClientRole to member.") + } + return nil +} + +func (u *ProjectUsecase) UnassignKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, projectMemberId string) error { + pm, err := u.GetProjectMember(ctx, projectMemberId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get project member.") + } + err = u.unassignKeycloakClientRoleToMember(ctx, organizationId, projectId, clientId, pm.ProjectUser.ID.String(), pm.ProjectRole.Name) + if err != nil { + log.Error(ctx, err) + return err + } + return nil +} + +func (u *ProjectUsecase) unassignKeycloakClientRoleToMember(ctx context.Context, organizationId string, projectId string, clientId string, userId string, roleName string) error { + err := u.kc.UnassignClientRoleToUser(ctx, organizationId, userId, clientId, roleName+"@"+projectId) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to un-assign each KeycloakClientRole to member.") + } + return nil +} + +func (u *ProjectUsecase) GetProjectNamespaceKubeconfig(ctx context.Context, organizationId string, projectId string, namespace string, stackId domain.StackId) (string, error) { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId.String(), kubernetes.KubeconfigForUser) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to get kubeconfig.") + } + + type kubeConfigType struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Clusters []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + } `yaml:"clusters"` + Contexts []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + } `yaml:"contexts"` + + Users []interface{} `yaml:"users,omitempty"` + } + + var config kubeConfigType + err = yaml.Unmarshal(kubeconfig, &config) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to unmarshal kubeconfig.") + } + config.Contexts[0].Context.Namespace = namespace + + kubeconfig, err = yaml.Marshal(config) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to marshal kubeconfig.") + } + + return string(kubeconfig[:]), nil + +} + +func (u *ProjectUsecase) GetProjectKubeconfig(ctx context.Context, organizationId string, projectId string) (string, error) { + projectNamespaces, err := u.projectRepo.GetProjectNamespaces(ctx, organizationId, projectId, nil) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to retrieve project namespaces.") + } + + type kubeConfigType struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Clusters []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + } `yaml:"clusters"` + Contexts []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + } `yaml:"contexts"` + + Users []interface{} `yaml:"users,omitempty"` + } + + kubeconfigs := make([]string, 0) + for _, pn := range projectNamespaces { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, pn.StackId, kubernetes.KubeconfigForUser) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to retrieve kubeconfig.") + } + + var config kubeConfigType + err = yaml.Unmarshal(kubeconfig, &config) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to unmarshal kubeconfig.") + } + config.Contexts[0].Context.Namespace = pn.Namespace + + kubeconfig, err = yaml.Marshal(config) + if err != nil { + log.Error(ctx, err) + return "", errors.Wrap(err, "Failed to marshal kubeconfig.") + } + + kubeconfigs = append(kubeconfigs, string(kubeconfig[:])) + } + + return kubernetes.MergeKubeconfigsWithSingleUser(kubeconfigs) +} + +func (u *ProjectUsecase) GetK8sResources(ctx context.Context, organizationId string, projectId string, namespace string, stackId domain.StackId) (out domain.ProjectNamespaceK8sResources, err error) { + _, err = u.clusterRepository.Get(ctx, domain.ClusterId(stackId)) + if err != nil { + return out, errors.Wrap(err, fmt.Sprintf("Failed to get cluster : stackId %s", stackId)) + } + + clientset_user, err := kubernetes.GetClientFromClusterId(ctx, stackId.String()) + if err != nil { + return out, errors.Wrap(err, fmt.Sprintf("Failed to get clientset : stackId %s", stackId)) + } + + out.UpdatedAt = time.Now() + + pods, err := clientset_user.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Pods = len(pods.Items) + } else { + log.Error(ctx, "Failed to get pods. err : ", err) + } + + pvcs, err := clientset_user.CoreV1().PersistentVolumeClaims(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.PVCs = len(pvcs.Items) + } else { + log.Error(ctx, "Failed to get pvcs. err : ", err) + } + + services, err := clientset_user.CoreV1().Services(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Services = len(services.Items) + } else { + log.Error(ctx, "Failed to get services. err : ", err) + } + + ingresses, err := clientset_user.NetworkingV1().Ingresses(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Ingresses = len(ingresses.Items) + } else { + log.Error(ctx, "Failed to get ingresses. err : ", err) + } + + deployments, err := clientset_user.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Deployments = len(deployments.Items) + } else { + log.Error(ctx, "Failed to get deployments. err : ", err) + } + + statefulsets, err := clientset_user.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Statefulsets = len(statefulsets.Items) + } else { + log.Error(ctx, "Failed to get statefulsets. err : ", err) + } + + daemonsets, err := clientset_user.AppsV1().DaemonSets(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Daemonsets = len(daemonsets.Items) + } else { + log.Error(ctx, "Failed to get daemonsets. err : ", err) + } + + jobs, err := clientset_user.BatchV1().Jobs(namespace).List(ctx, metav1.ListOptions{}) + if err == nil { + out.Jobs = len(jobs.Items) + } else { + log.Error(ctx, "Failed to get jobs. err : ", err) + } + + return +} + +func (u *ProjectUsecase) GetResourcesUsage(ctx context.Context, thanosClient thanos.ThanosClient, organizationId string, projectId string, namespace string, stackId domain.StackId) (out domain.ProjectNamespaceResourcesUsage, err error) { + _, err = u.clusterRepository.Get(ctx, domain.ClusterId(stackId)) + if err != nil { + return out, errors.Wrap(err, fmt.Sprintf("Failed to get cluster : stackId %s", stackId)) + } + + // sum(rate(container_cpu_usage_seconds_total{taco_cluster=\"$taco_cluster\",image!=\"\"}[$__rate_interval])) by (namespace) + query := fmt.Sprintf("sum(rate(container_cpu_usage_seconds_total{image!=\"\", namespace=\"%s\"}[10m]) ) by (taco_cluster, namespace)", namespace) + result, err := thanosClient.Get(ctx, query) + if err != nil { + return out, err + } + for _, val := range result.Data.Result { + if val.Metric.TacoCluster == stackId.String() { + if val.Metric.Namespace == namespace { + if s, err := strconv.ParseFloat(val.Value[1].(string), 32); err == nil { + out.Cpu = fmt.Sprintf("%0.2f %%", s*100) + } + } + } + } + + // sum(container_memory_working_set_bytes{taco_cluster=\"$taco_cluster\",image!=\"\"}) by (namespace) + query = fmt.Sprintf("sum(container_memory_working_set_bytes{image!=\"\", namespace=\"%s\"}) by (taco_cluster, namespace)", namespace) + result, err = thanosClient.Get(ctx, query) + if err != nil { + return out, err + } + for _, val := range result.Data.Result { + if val.Metric.TacoCluster == stackId.String() { + if val.Metric.Namespace == namespace { + memory, _ := strconv.Atoi(val.Value[1].(string)) + out.Memory = fmt.Sprintf("%d MiB", memory/1024/1024) + } + } + } + + out.Storage = "" + + return +} + +func (u *ProjectUsecase) EnsureNamespaceForCluster(ctx context.Context, organizationId string, stackId string, namespaceName string) error { + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId, kubernetes.KubeconfigForAdmin) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to get kubeconfig.") + } + + err = kubernetes.EnsureNamespace(ctx, kubeconfig, namespaceName) + if err != nil { + log.Error(ctx, err) + return errors.Wrap(err, "Failed to create K8s namespace resource.") + } + + return nil +} diff --git a/internal/usecase/role.go b/internal/usecase/role.go new file mode 100644 index 00000000..7b181c93 --- /dev/null +++ b/internal/usecase/role.go @@ -0,0 +1,99 @@ +package usecase + +import ( + "context" + "github.com/openinfradev/tks-api/internal/keycloak" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" +) + +type IRoleUsecase interface { + CreateTksRole(ctx context.Context, role *model.Role) (string, error) + ListTksRoles(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]*model.Role, error) + GetTksRole(ctx context.Context, orgainzationId string, id string) (*model.Role, error) + DeleteTksRole(ctx context.Context, organizationId string, id string) error + UpdateTksRole(ctx context.Context, role *model.Role) error + IsRoleNameExisted(ctx context.Context, organizationId string, roleName string) (bool, error) +} + +type RoleUsecase struct { + repo repository.IRoleRepository + kc keycloak.IKeycloak +} + +func NewRoleUsecase(repo repository.Repository, kc keycloak.IKeycloak) *RoleUsecase { + return &RoleUsecase{ + repo: repo.Role, + kc: kc, + } +} + +func (r RoleUsecase) CreateTksRole(ctx context.Context, role *model.Role) (string, error) { + roleId, err := r.kc.CreateGroup(ctx, role.OrganizationID, role.Name) + if err != nil { + return "", err + } + role.ID = roleId + return r.repo.Create(ctx, role) +} + +func (r RoleUsecase) ListTksRoles(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]*model.Role, error) { + roles, err := r.repo.ListTksRoles(ctx, organizationId, pg) + if err != nil { + return nil, err + } + + return roles, nil +} + +func (r RoleUsecase) GetTksRole(ctx context.Context, organizationId string, id string) (*model.Role, error) { + role, err := r.repo.GetTksRole(ctx, organizationId, id) + if err != nil { + return nil, err + } + + return role, nil +} + +func (r RoleUsecase) DeleteTksRole(ctx context.Context, organizationId string, id string) error { + role, err := r.repo.GetTksRole(ctx, organizationId, id) + if err != nil { + return err + } + err = r.kc.DeleteGroup(ctx, organizationId, role.Name+"@"+organizationId) + if err != nil { + return err + } + return r.repo.Delete(ctx, id) +} + +func (r RoleUsecase) UpdateTksRole(ctx context.Context, newRole *model.Role) error { + role, err := r.repo.GetTksRole(ctx, newRole.OrganizationID, newRole.ID) + if err != nil { + return err + } + err = r.kc.UpdateGroup(ctx, role.OrganizationID, role.Name, newRole.Name) + if err != nil { + return err + } + err = r.repo.Update(ctx, newRole) + if err != nil { + return err + } + + return nil +} + +func (r RoleUsecase) IsRoleNameExisted(ctx context.Context, organizationId string, roleName string) (bool, error) { + role, err := r.repo.GetTksRoleByRoleName(ctx, organizationId, roleName) + if err != nil { + return false, err + } + + if role != nil { + return true, nil + } + + return false, nil +} diff --git a/internal/usecase/stack-template.go b/internal/usecase/stack-template.go index 4e2731ac..41c95a46 100644 --- a/internal/usecase/stack-template.go +++ b/internal/usecase/stack-template.go @@ -2,55 +2,295 @@ package usecase import ( "context" + "fmt" + "net/http" + "strings" + "github.com/PuerkitoBio/goquery" "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" - "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type IStackTemplateUsecase interface { - Get(ctx context.Context, stackTemplate uuid.UUID) (domain.StackTemplate, error) - Fetch(ctx context.Context, pg *pagination.Pagination) ([]domain.StackTemplate, error) - Create(ctx context.Context, dto domain.StackTemplate) (stackTemplate uuid.UUID, err error) - Update(ctx context.Context, dto domain.StackTemplate) error - Delete(ctx context.Context, dto domain.StackTemplate) error + Get(ctx context.Context, stackTemplateId uuid.UUID) (model.StackTemplate, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.StackTemplate, error) + FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.StackTemplate, error) + Create(ctx context.Context, dto model.StackTemplate) (stackTemplate uuid.UUID, err error) + Update(ctx context.Context, dto model.StackTemplate) error + Delete(ctx context.Context, stackTemplateId uuid.UUID) error + UpdateOrganizations(ctx context.Context, dto model.StackTemplate) error + GetByName(ctx context.Context, name string) (model.StackTemplate, error) + AddOrganizationStackTemplates(ctx context.Context, organizationId string, stackTemplateIds []string) error + RemoveOrganizationStackTemplates(ctx context.Context, organizationId string, stackTemplateIds []string) error + GetTemplateIds(ctx context.Context) ([]string, error) } type StackTemplateUsecase struct { - repo repository.IStackTemplateRepository + repo repository.IStackTemplateRepository + organizationRepo repository.IOrganizationRepository + clusterRepo repository.IClusterRepository } func NewStackTemplateUsecase(r repository.Repository) IStackTemplateUsecase { return &StackTemplateUsecase{ - repo: r.StackTemplate, + repo: r.StackTemplate, + organizationRepo: r.Organization, + clusterRepo: r.Cluster, } } -func (u *StackTemplateUsecase) Create(ctx context.Context, dto domain.StackTemplate) (stackTemplate uuid.UUID, err error) { - return uuid.Nil, nil +func (u *StackTemplateUsecase) Create(ctx context.Context, dto model.StackTemplate) (stackTemplateId uuid.UUID, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + dto.CreatorId = &userId + dto.UpdatorId = &userId + + if _, err = u.GetByName(ctx, dto.Name); err == nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("duplicate stackTemplate name"), "ST_CREATE_ALREADY_EXISTED_NAME", "") + } + + dto.Services = servicesFromIds(dto.ServiceIds) + stackTemplateId, err = u.repo.Create(ctx, dto) + if err != nil { + return uuid.Nil, httpErrors.NewInternalServerError(err, "", "") + } + log.Info(ctx, "newly created StackTemplate ID:", stackTemplateId) + + dto.ID = stackTemplateId + err = u.UpdateOrganizations(ctx, dto) + if err != nil { + return uuid.Nil, err + } + + return stackTemplateId, nil } -func (u *StackTemplateUsecase) Update(ctx context.Context, dto domain.StackTemplate) error { +func (u *StackTemplateUsecase) Update(ctx context.Context, dto model.StackTemplate) error { + _, err := u.repo.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_NOT_EXISTED_STACK_TEMPLATE", "") + } + + dto.Services = servicesFromIds(dto.ServiceIds) + err = u.repo.Update(ctx, dto) + if err != nil { + return err + } + + err = u.UpdateOrganizations(ctx, dto) + if err != nil { + return err + } + return nil } -func (u *StackTemplateUsecase) Get(ctx context.Context, stackTemplate uuid.UUID) (res domain.StackTemplate, err error) { - res, err = u.repo.Get(stackTemplate) +func (u *StackTemplateUsecase) Get(ctx context.Context, stackTemplateId uuid.UUID) (res model.StackTemplate, err error) { + res, err = u.repo.Get(ctx, stackTemplateId) if err != nil { - return domain.StackTemplate{}, err + return res, err } return } -func (u *StackTemplateUsecase) Fetch(ctx context.Context, pg *pagination.Pagination) (res []domain.StackTemplate, err error) { - res, err = u.repo.Fetch(pg) +func (u *StackTemplateUsecase) GetByName(ctx context.Context, name string) (out model.StackTemplate, err error) { + out, err = u.repo.GetByName(ctx, name) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return out, httpErrors.NewNotFoundError(err, "ST_FAILED_FETCH_STACK_TEMPLATE", "") + } + return out, err + } + + return +} + +func (u *StackTemplateUsecase) Fetch(ctx context.Context, pg *pagination.Pagination) (res []model.StackTemplate, err error) { + res, err = u.repo.Fetch(ctx, pg) if err != nil { return nil, err } return res, nil } -func (u *StackTemplateUsecase) Delete(ctx context.Context, dto domain.StackTemplate) (err error) { +func (u *StackTemplateUsecase) FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (res []model.StackTemplate, err error) { + res, err = u.repo.FetchWithOrganization(ctx, organizationId, pg) + if err != nil { + return nil, err + } + return res, nil +} + +func (u *StackTemplateUsecase) Delete(ctx context.Context, stackTemplateId uuid.UUID) (err error) { + stackTemplate, err := u.repo.Get(ctx, stackTemplateId) + if err != nil { + return err + } + + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + stackTemplate.UpdatorId = &userId + + // check if used + pg := pagination.NewPaginationWithFilter("stack_template_id", "", "$eq", []string{stackTemplateId.String()}) + res, err := u.clusterRepo.Fetch(ctx, pg) + if err != nil { + return err + } + if len(res) > 0 { + return httpErrors.NewBadRequestError(fmt.Errorf("Failed to delete stackTemplate %s", stackTemplateId.String()), "ST_FAILED_DELETE_EXIST_CLUSTERS", "") + } + + err = u.repo.Delete(ctx, stackTemplate) + if err != nil { + return err + } + + return nil +} + +func (u *StackTemplateUsecase) UpdateOrganizations(ctx context.Context, dto model.StackTemplate) error { + _, err := u.repo.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_NOT_EXISTED_STACK_TEMPLATE", "") + } + + organizations := make([]model.Organization, 0) + for _, organizationId := range dto.OrganizationIds { + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err == nil { + organizations = append(organizations, organization) + } + } + + err = u.repo.UpdateOrganizations(ctx, dto.ID, organizations) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_UPDATE_ORGANIZATION", "") + } + + return nil +} + +func (u *StackTemplateUsecase) AddOrganizationStackTemplates(ctx context.Context, organizationId string, stackTemplateIds []string) error { + _, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewBadRequestError(err, "O_NOT_EXISTED_NAME", "") + } + + stackTemplates := make([]model.StackTemplate, 0) + for _, strId := range stackTemplateIds { + stackTemplateId, _ := uuid.Parse(strId) + stackTemplate, err := u.repo.Get(ctx, stackTemplateId) + if err == nil { + stackTemplates = append(stackTemplates, stackTemplate) + } + } + + err = u.organizationRepo.AddStackTemplates(ctx, organizationId, stackTemplates) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_ADD_ORGANIZATION_STACK_TEMPLATE", "") + } + return nil } + +func (u *StackTemplateUsecase) RemoveOrganizationStackTemplates(ctx context.Context, organizationId string, stackTemplateIds []string) error { + _, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewBadRequestError(err, "O_NOT_EXISTED_NAME", "") + } + + stackTemplates := make([]model.StackTemplate, 0) + for _, strId := range stackTemplateIds { + stackTemplateId, _ := uuid.Parse(strId) + stackTemplate, err := u.repo.Get(ctx, stackTemplateId) + if err == nil { + stackTemplates = append(stackTemplates, stackTemplate) + } + } + + err = u.organizationRepo.RemoveStackTemplates(ctx, organizationId, stackTemplates) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_REMOVE_ORGANIZATION_STACK_TEMPLATE", "") + } + + return nil +} + +func (u *StackTemplateUsecase) GetTemplateIds(ctx context.Context) (out []string, err error) { + clientset_admin, err := kubernetes.GetClientAdminCluster(ctx) + if err != nil { + return out, errors.Wrap(err, "Failed to get client set for admin cluster") + } + + secrets, err := clientset_admin.CoreV1().Secrets("argo").Get(context.TODO(), "git-svc-token", metav1.GetOptions{}) + if err != nil { + log.Error(ctx, "cannot found git-svc-token. so use default hard-corded values") + return out, err + } + + gitSvcUrl := string(secrets.Data["GIT_SVC_URL"]) + username := string(secrets.Data["USERNAME"]) + branch := string(secrets.Data["GIT_BASE_BRANCH"]) + url := fmt.Sprintf("%s/%s/decapod-site/src/branch/%s", gitSvcUrl, username, branch) + log.Info(ctx, "git url : ", url) + + rsp, err := http.Get(url) + if err != nil { + return out, err + } + defer rsp.Body.Close() + + html, err := goquery.NewDocumentFromReader(rsp.Body) + if err != nil { + return out, err + } + + wrapper := html.Find("#repo-files-table > tbody") + items := wrapper.Find("a.muted") + items.Each(func(idx int, sel *goquery.Selection) { + href, _ := sel.Attr("href") + if strings.Contains(href, "reference") { + arr := strings.Split(href, "/") + log.Info(ctx, arr[len(arr)-1]) + + out = append(out, arr[len(arr)-1]) + } + + }) + + return +} + +func servicesFromIds(serviceIds []string) []byte { + services := "[" + for i, serviceId := range serviceIds { + if i > 0 { + services = services + "," + } + switch serviceId { + case "LMA": + services = services + internal.SERVICE_LMA + case "SERVICE_MESH": + services = services + internal.SERVICE_SERVICE_MESH + } + } + services = services + "]" + return []byte(services) +} diff --git a/internal/usecase/stack.go b/internal/usecase/stack.go index 3bb6f907..e2f576cf 100644 --- a/internal/usecase/stack.go +++ b/internal/usecase/stack.go @@ -9,14 +9,15 @@ import ( "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" - "github.com/openinfradev/tks-api/internal/kubernetes" "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" "github.com/openinfradev/tks-api/internal/serializer" argowf "github.com/openinfradev/tks-api/pkg/argo-client" "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/kubernetes" "github.com/openinfradev/tks-api/pkg/log" "github.com/pkg/errors" "github.com/spf13/viper" @@ -24,13 +25,13 @@ import ( ) type IStackUsecase interface { - Get(ctx context.Context, stackId domain.StackId) (domain.Stack, error) - GetByName(ctx context.Context, organizationId string, name string) (domain.Stack, error) - Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]domain.Stack, error) - Create(ctx context.Context, dto domain.Stack) (stackId domain.StackId, err error) + Get(ctx context.Context, stackId domain.StackId) (model.Stack, error) + GetByName(ctx context.Context, organizationId string, name string) (model.Stack, error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.Stack, error) + Create(ctx context.Context, dto model.Stack) (stackId domain.StackId, err error) Install(ctx context.Context, stackId domain.StackId) (err error) - Update(ctx context.Context, dto domain.Stack) error - Delete(ctx context.Context, dto domain.Stack) error + Update(ctx context.Context, dto model.Stack) error + Delete(ctx context.Context, dto model.Stack) error GetKubeConfig(ctx context.Context, stackId domain.StackId) (kubeConfig string, err error) GetStepStatus(ctx context.Context, stackId domain.StackId) (out []domain.StackStepStatus, stackStatus string, err error) SetFavorite(ctx context.Context, stackId domain.StackId) error @@ -61,7 +62,7 @@ func NewStackUsecase(r repository.Repository, argoClient argowf.ArgoClient, dash } } -func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId domain.StackId, err error) { +func (u *StackUsecase) Create(ctx context.Context, dto model.Stack) (stackId domain.StackId, err error) { user, ok := request.UserFrom(ctx) if !ok { return "", httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") @@ -72,12 +73,12 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do return "", httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "S_CREATE_ALREADY_EXISTED_NAME", "") } - stackTemplate, err := u.stackTemplateRepo.Get(dto.StackTemplateId) + stackTemplate, err := u.stackTemplateRepo.Get(ctx, dto.StackTemplateId) if err != nil { return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid stackTemplateId"), "S_INVALID_STACK_TEMPLATE", "") } - clusters, err := u.clusterRepo.FetchByOrganizationId(dto.OrganizationId, user.GetUserId(), nil) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, dto.OrganizationId, user.GetUserId(), nil) if err != nil { return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Failed to get clusters"), "S_FAILED_GET_CLUSTERS", "") } @@ -85,7 +86,7 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do if len(clusters) == 0 { isPrimary = true } - log.DebugWithContext(ctx, "isPrimary ", isPrimary) + log.Debug(ctx, "isPrimary ", isPrimary) if dto.CloudService == domain.CloudService_BYOH { if dto.ClusterEndpoint == "" { @@ -96,32 +97,38 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do return "", httpErrors.NewBadRequestError(fmt.Errorf("Invalid clusterEndpoint"), "S_INVALID_ADMINCLUSTER_URL", "") } } else { - if _, err = u.cloudAccountRepo.Get(dto.CloudAccountId); err != nil { + if _, err = u.cloudAccountRepo.Get(ctx, dto.CloudAccountId); err != nil { return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid cloudAccountId"), "S_INVALID_CLOUD_ACCOUNT", "") } } // Make stack nodes - var stackConf domain.StackConfResponse - if err = domain.Map(dto.Conf, &stackConf); err != nil { - log.InfoWithContext(ctx, err) - } + // [TODO] to be advanced feature + dto.Conf.TksCpNodeMax = dto.Conf.TksCpNode + dto.Conf.TksInfraNodeMax = dto.Conf.TksInfraNode + dto.Conf.TksUserNodeMax = dto.Conf.TksUserNode if stackTemplate.CloudService == "AWS" && stackTemplate.KubeType == "AWS" { - if stackConf.TksCpNode == 0 { - stackConf.TksCpNode = 3 - stackConf.TksCpNodeMax = 3 - stackConf.TksInfraNode = 3 - stackConf.TksInfraNodeMax = 3 + if dto.Conf.TksCpNode == 0 { + dto.Conf.TksCpNode = 3 + dto.Conf.TksCpNodeMax = 3 + dto.Conf.TksInfraNode = 3 + dto.Conf.TksInfraNodeMax = 3 } // user 노드는 MAX_AZ_NUM의 배수로 요청한다. - if stackConf.TksUserNode%domain.MAX_AZ_NUM != 0 { + if dto.Conf.TksUserNode%domain.MAX_AZ_NUM != 0 { return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid node count"), "", "") } } + var conf domain.StackConfResponse + if err := serializer.Map(ctx, dto.Conf, &conf); err != nil { + log.Error(ctx, err) + return "", httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid node conf"), "", "") + } + workflow := "tks-stack-create" - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), "cluster_name=" + dto.Name, @@ -131,32 +138,33 @@ func (u *StackUsecase) Create(ctx context.Context, dto domain.Stack) (stackId do "stack_template_id=" + dto.StackTemplateId.String(), "creator=" + user.GetUserId().String(), "base_repo_branch=" + viper.GetString("revision"), - "infra_conf=" + strings.Replace(helper.ModelToJson(stackConf), "\"", "\\\"", -1), + "infra_conf=" + strings.Replace(helper.ModelToJson(conf), "\"", "\\\"", -1), "cloud_service=" + dto.CloudService, "cluster_endpoint=" + dto.ClusterEndpoint, + "policy_ids=" + strings.Join(dto.PolicyIds, ","), }, }) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return "", httpErrors.NewInternalServerError(err, "S_FAILED_TO_CALL_WORKFLOW", "") } - log.DebugWithContext(ctx, "Submitted workflow: ", workflowId) + log.Debug(ctx, "Submitted workflow: ", workflowId) // wait & get clusterId ( max 1min ) dto.ID = domain.StackId("") for i := 0; i < 60; i++ { time.Sleep(time.Second * 5) - workflow, err := u.argo.GetWorkflow("argo", workflowId) + workflow, err := u.argo.GetWorkflow(ctx, "argo", workflowId) if err != nil { return "", err } - log.DebugWithContext(ctx, "workflow ", workflow) + log.Debug(ctx, "workflow ", workflow) if workflow.Status.Phase != "" && workflow.Status.Phase != "Running" { return "", fmt.Errorf("Invalid workflow status [%s]", workflow.Status.Phase) } - cluster, err := u.clusterRepo.GetByName(dto.OrganizationId, dto.Name) + cluster, err := u.clusterRepo.GetByName(ctx, dto.OrganizationId, dto.Name) if err != nil { continue } @@ -175,12 +183,12 @@ func (u *StackUsecase) Install(ctx context.Context, stackId domain.StackId) (err return httpErrors.NewBadRequestError(fmt.Errorf("Invalid stackId"), "S_INVALID_STACK_ID", "") } - _, err = u.stackTemplateRepo.Get(cluster.StackTemplateId) + _, err = u.stackTemplateRepo.Get(ctx, cluster.StackTemplateId) if err != nil { return httpErrors.NewInternalServerError(errors.Wrap(err, "Invalid stackTemplateId"), "S_INVALID_STACK_TEMPLATE", "") } - clusters, err := u.clusterRepo.FetchByOrganizationId(cluster.OrganizationId, uuid.Nil, nil) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, cluster.OrganizationId, uuid.Nil, nil) if err != nil { return httpErrors.NewInternalServerError(errors.Wrap(err, "Failed to get clusters"), "S_FAILED_GET_CLUSTERS", "") } @@ -188,7 +196,7 @@ func (u *StackUsecase) Install(ctx context.Context, stackId domain.StackId) (err if len(clusters) == 0 { isPrimary = true } - log.DebugWithContext(ctx, "isPrimary ", isPrimary) + log.Debug(ctx, "isPrimary ", isPrimary) if cluster.CloudService != domain.CloudService_BYOH { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid cloud service"), "S_INVALID_CLOUD_SERVICE", "") @@ -196,12 +204,12 @@ func (u *StackUsecase) Install(ctx context.Context, stackId domain.StackId) (err // Make stack nodes var stackConf domain.StackConfResponse - if err = domain.Map(cluster.Conf, &stackConf); err != nil { - log.InfoWithContext(ctx, err) + if err = serializer.Map(ctx, cluster, &stackConf); err != nil { + log.Info(ctx, err) } workflow := "tks-stack-install" - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), "cluster_id=" + cluster.ID.String(), @@ -213,16 +221,16 @@ func (u *StackUsecase) Install(ctx context.Context, stackId domain.StackId) (err }, }) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return httpErrors.NewInternalServerError(err, "S_FAILED_TO_CALL_WORKFLOW", "") } - log.DebugWithContext(ctx, "Submitted workflow: ", workflowId) + log.Debug(ctx, "Submitted workflow: ", workflowId) return nil } -func (u *StackUsecase) Get(ctx context.Context, stackId domain.StackId) (out domain.Stack, err error) { - cluster, err := u.clusterRepo.Get(domain.ClusterId(stackId)) +func (u *StackUsecase) Get(ctx context.Context, stackId domain.StackId) (out model.Stack, err error) { + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(stackId)) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return out, httpErrors.NewNotFoundError(err, "S_FAILED_FETCH_CLUSTER", "") @@ -230,39 +238,41 @@ func (u *StackUsecase) Get(ctx context.Context, stackId domain.StackId) (out dom return out, err } - organization, err := u.organizationRepo.Get(cluster.OrganizationId) + organization, err := u.organizationRepo.Get(ctx, cluster.OrganizationId) if err != nil { return out, httpErrors.NewInternalServerError(errors.Wrap(err, fmt.Sprintf("Failed to get organization for clusterId %s", domain.ClusterId(stackId))), "S_FAILED_FETCH_ORGANIZATION", "") } - appGroups, err := u.appGroupRepo.Fetch(domain.ClusterId(stackId), nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, domain.ClusterId(stackId), nil) if err != nil { return out, err } - stackResources, _ := u.dashbordUsecase.GetStacks(ctx, cluster.OrganizationId) - out = reflectClusterToStack(cluster, appGroups) + out = reflectClusterToStack(ctx, cluster, appGroups) if organization.PrimaryClusterId == cluster.ID.String() { out.PrimaryCluster = true } + // Resources + stackResources, _ := u.dashbordUsecase.GetStacks(ctx, cluster.OrganizationId) for _, resource := range stackResources { if resource.ID == domain.StackId(cluster.ID) { - if err := serializer.Map(resource, &out.Resource); err != nil { - log.Error(err) + if err := serializer.Map(ctx, resource, &out.Resource); err != nil { + log.Error(ctx, err) } } } - appGroupsInPrimaryCluster, err := u.appGroupRepo.Fetch(domain.ClusterId(organization.PrimaryClusterId), nil) + appGroupsInPrimaryCluster, err := u.appGroupRepo.Fetch(ctx, domain.ClusterId(organization.PrimaryClusterId), nil) if err != nil { return out, err } + // Grafana URL for _, appGroup := range appGroupsInPrimaryCluster { if appGroup.AppGroupType == domain.AppGroupType_LMA { - applications, err := u.appGroupRepo.GetApplications(appGroup.ID, domain.ApplicationType_GRAFANA) + applications, err := u.appGroupRepo.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) if err != nil { return out, err } @@ -272,11 +282,25 @@ func (u *StackUsecase) Get(ctx context.Context, stackId domain.StackId) (out dom } } + // Favorited + if cluster.Favorites != nil && len(*cluster.Favorites) > 0 { + out.Favorited = true + } else { + out.Favorited = false + } + + // AppServeApps + appServeAppCnt, err := u.appServeAppRepo.GetNumOfAppsOnStack(ctx, cluster.OrganizationId, cluster.ID.String()) + if err != nil { + log.Error(ctx, err) + } + out.AppServeAppCnt = int(appServeAppCnt) + return } -func (u *StackUsecase) GetByName(ctx context.Context, organizationId string, name string) (out domain.Stack, err error) { - cluster, err := u.clusterRepo.GetByName(organizationId, name) +func (u *StackUsecase) GetByName(ctx context.Context, organizationId string, name string) (out model.Stack, err error) { + cluster, err := u.clusterRepo.GetByName(ctx, organizationId, name) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return out, httpErrors.NewNotFoundError(err, "S_FAILED_FETCH_CLUSTER", "") @@ -284,27 +308,27 @@ func (u *StackUsecase) GetByName(ctx context.Context, organizationId string, nam return out, err } - appGroups, err := u.appGroupRepo.Fetch(cluster.ID, nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, cluster.ID, nil) if err != nil { return out, err } - out = reflectClusterToStack(cluster, appGroups) + out = reflectClusterToStack(ctx, cluster, appGroups) return } -func (u *StackUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []domain.Stack, err error) { +func (u *StackUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (out []model.Stack, err error) { user, ok := request.UserFrom(ctx) if !ok { return out, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") } - organization, err := u.organizationRepo.Get(organizationId) + organization, err := u.organizationRepo.Get(ctx, organizationId) if err != nil { return out, httpErrors.NewInternalServerError(errors.Wrap(err, fmt.Sprintf("Failed to get organization for clusterId %s", organizationId)), "S_FAILED_FETCH_ORGANIZATION", "") } - clusters, err := u.clusterRepo.FetchByOrganizationId(organizationId, user.GetUserId(), pg) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organizationId, user.GetUserId(), pg) if err != nil { return out, err } @@ -312,19 +336,19 @@ func (u *StackUsecase) Fetch(ctx context.Context, organizationId string, pg *pag stackResources, _ := u.dashbordUsecase.GetStacks(ctx, organizationId) for _, cluster := range clusters { - appGroups, err := u.appGroupRepo.Fetch(cluster.ID, nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, cluster.ID, nil) if err != nil { return nil, err } - outStack := reflectClusterToStack(cluster, appGroups) + outStack := reflectClusterToStack(ctx, cluster, appGroups) if organization.PrimaryClusterId == cluster.ID.String() { outStack.PrimaryCluster = true } for _, appGroup := range appGroups { if appGroup.AppGroupType == domain.AppGroupType_LMA { - applications, err := u.appGroupRepo.GetApplications(appGroup.ID, domain.ApplicationType_GRAFANA) + applications, err := u.appGroupRepo.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) if err != nil { return nil, err } @@ -336,12 +360,18 @@ func (u *StackUsecase) Fetch(ctx context.Context, organizationId string, pg *pag for _, resource := range stackResources { if resource.ID == domain.StackId(cluster.ID) { - if err := serializer.Map(resource, &outStack.Resource); err != nil { - log.Error(err) + if err := serializer.Map(ctx, resource, &outStack.Resource); err != nil { + log.Error(ctx, err) } } } + if cluster.Favorites != nil && len(*cluster.Favorites) > 0 { + outStack.Favorited = true + } else { + outStack.Favorited = false + } + out = append(out, outStack) } @@ -352,25 +382,25 @@ func (u *StackUsecase) Fetch(ctx context.Context, organizationId string, pg *pag return } -func (u *StackUsecase) Update(ctx context.Context, dto domain.Stack) (err error) { +func (u *StackUsecase) Update(ctx context.Context, dto model.Stack) (err error) { user, ok := request.UserFrom(ctx) if !ok { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - _, err = u.clusterRepo.Get(domain.ClusterId(dto.ID)) + _, err = u.clusterRepo.Get(ctx, domain.ClusterId(dto.ID)) if err != nil { return httpErrors.NewNotFoundError(err, "S_FAILED_FETCH_CLUSTER", "") } updatorId := user.GetUserId() - dtoCluster := domain.Cluster{ + dtoCluster := model.Cluster{ ID: domain.ClusterId(dto.ID), Description: dto.Description, UpdatorId: &updatorId, } - err = u.clusterRepo.Update(dtoCluster) + err = u.clusterRepo.Update(ctx, dtoCluster) if err != nil { return err } @@ -378,19 +408,27 @@ func (u *StackUsecase) Update(ctx context.Context, dto domain.Stack) (err error) return nil } -func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) { +func (u *StackUsecase) Delete(ctx context.Context, dto model.Stack) (err error) { user, ok := request.UserFrom(ctx) if !ok { return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") } - cluster, err := u.clusterRepo.Get(domain.ClusterId(dto.ID)) + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(dto.ID)) if err != nil { return httpErrors.NewBadRequestError(errors.Wrap(err, "Failed to get cluster"), "S_FAILED_FETCH_CLUSTER", "") } + // cluster 의 상태가 비정상 상태라면, DB 데이터만 삭제 + if cluster.Status != domain.ClusterStatus_RUNNING && + cluster.Status != domain.ClusterStatus_BOOTSTRAPPING && + cluster.Status != domain.ClusterStatus_INSTALLING && + cluster.Status != domain.ClusterStatus_DELETING { + return u.clusterRepo.Delete(ctx, domain.ClusterId(dto.ID)) + } + // 지우려고 하는 stack 이 primary cluster 라면, organization 내에 cluster 가 자기 자신만 남아있을 경우이다. - organizations, err := u.organizationRepo.Fetch(nil) + organizations, err := u.organizationRepo.Fetch(ctx, nil) if err != nil { return errors.Wrap(err, "Failed to get organizations") } @@ -398,7 +436,7 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) for _, organization := range *organizations { if organization.PrimaryClusterId == cluster.ID.String() { - clusters, err := u.clusterRepo.FetchByOrganizationId(organization.ID, user.GetUserId(), nil) + clusters, err := u.clusterRepo.FetchByOrganizationId(ctx, organization.ID, user.GetUserId(), nil) if err != nil { return errors.Wrap(err, "Failed to get organizations") } @@ -413,7 +451,7 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) break } } - appGroups, err := u.appGroupRepo.Fetch(domain.ClusterId(dto.ID), nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, domain.ClusterId(dto.ID), nil) if err != nil { return errors.Wrap(err, "Failed to get appGroups") } @@ -425,7 +463,8 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) } } - appsCnt, err := u.appServeAppRepo.GetNumOfAppsOnStack(dto.OrganizationId, dto.ID.String()) + // Check AppServing + appsCnt, err := u.appServeAppRepo.GetNumOfAppsOnStack(ctx, dto.OrganizationId, dto.ID.String()) if err != nil { return errors.Wrap(err, "Failed to get numOfAppsOnStack") } @@ -433,10 +472,8 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) return httpErrors.NewBadRequestError(fmt.Errorf("existed appServeApps in %s", dto.OrganizationId), "S_FAILED_DELETE_EXISTED_ASA", "") } - // [TODO] BYOH 삭제는 어떻게 처리하는게 좋은가? - workflow := "tks-stack-delete" - workflowId, err := u.argo.SumbitWorkflowFromWftpl(workflow, argowf.SubmitOptions{ + workflowId, err := u.argo.SumbitWorkflowFromWftpl(ctx, workflow, argowf.SubmitOptions{ Parameters: []string{ fmt.Sprintf("tks_api_url=%s", viper.GetString("external-address")), "organization_id=" + dto.OrganizationId, @@ -446,23 +483,23 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) }, }) if err != nil { - log.ErrorWithContext(ctx, err) + log.Error(ctx, err) return err } - log.DebugWithContext(ctx, "Submitted workflow: ", workflowId) + log.Debug(ctx, "Submitted workflow: ", workflowId) // Remove Cluster & AppGroup status description - if err := u.appGroupRepo.InitWorkflowDescription(cluster.ID); err != nil { - log.ErrorWithContext(ctx, err) + if err := u.appGroupRepo.InitWorkflowDescription(ctx, cluster.ID); err != nil { + log.Error(ctx, err) } - if err := u.clusterRepo.InitWorkflowDescription(cluster.ID); err != nil { - log.ErrorWithContext(ctx, err) + if err := u.clusterRepo.InitWorkflowDescription(ctx, cluster.ID); err != nil { + log.Error(ctx, err) } // wait & get clusterId ( max 1min ) for i := 0; i < 60; i++ { time.Sleep(time.Second * 2) - workflow, err := u.argo.GetWorkflow("argo", workflowId) + workflow, err := u.argo.GetWorkflow(ctx, "argo", workflowId) if err != nil { return err } @@ -481,7 +518,7 @@ func (u *StackUsecase) Delete(ctx context.Context, dto domain.Stack) (err error) } func (u *StackUsecase) GetKubeConfig(ctx context.Context, stackId domain.StackId) (kubeConfig string, err error) { - kubeconfig, err := kubernetes.GetKubeConfig(stackId.String()) + kubeconfig, err := kubernetes.GetKubeConfig(ctx, stackId.String(), kubernetes.KubeconfigForUser) //kubeconfig, err := kubernetes.GetKubeConfig("cmsai5k5l") if err != nil { return "", err @@ -492,12 +529,12 @@ func (u *StackUsecase) GetKubeConfig(ctx context.Context, stackId domain.StackId // [TODO] need more pretty... func (u *StackUsecase) GetStepStatus(ctx context.Context, stackId domain.StackId) (out []domain.StackStepStatus, stackStatus string, err error) { - cluster, err := u.clusterRepo.Get(domain.ClusterId(stackId)) + cluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(stackId)) if err != nil { return out, "", err } - organization, err := u.organizationRepo.Get(cluster.OrganizationId) + organization, err := u.organizationRepo.Get(ctx, cluster.OrganizationId) if err != nil { return out, "", err } @@ -540,7 +577,7 @@ func (u *StackUsecase) GetStepStatus(ctx context.Context, stackId domain.StackId }) } - appGroups, err := u.appGroupRepo.Fetch(domain.ClusterId(stackId), nil) + appGroups, err := u.appGroupRepo.Fetch(ctx, domain.ClusterId(stackId), nil) for _, appGroup := range appGroups { for i, step := range out { if step.Stage == appGroup.AppGroupType.String() { @@ -595,7 +632,7 @@ func (u *StackUsecase) SetFavorite(ctx context.Context, stackId domain.StackId) return httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") } - err := u.clusterRepo.SetFavorite(domain.ClusterId(stackId), user.GetUserId()) + err := u.clusterRepo.SetFavorite(ctx, domain.ClusterId(stackId), user.GetUserId()) if err != nil { return err } @@ -609,7 +646,7 @@ func (u *StackUsecase) DeleteFavorite(ctx context.Context, stackId domain.StackI return httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") } - err := u.clusterRepo.DeleteFavorite(domain.ClusterId(stackId), user.GetUserId()) + err := u.clusterRepo.DeleteFavorite(ctx, domain.ClusterId(stackId), user.GetUserId()) if err != nil { return err } @@ -617,9 +654,13 @@ func (u *StackUsecase) DeleteFavorite(ctx context.Context, stackId domain.StackI return nil } -func reflectClusterToStack(cluster domain.Cluster, appGroups []domain.AppGroup) (out domain.Stack) { - if err := serializer.Map(cluster, &out); err != nil { - log.Error(err) +func reflectClusterToStack(ctx context.Context, cluster model.Cluster, appGroups []model.AppGroup) (out model.Stack) { + if err := serializer.Map(ctx, cluster, &out); err != nil { + log.Error(ctx, err) + } + + if err := serializer.Map(ctx, cluster, &out.Conf); err != nil { + log.Error(ctx, err) } status, statusDesc := getStackStatus(cluster, appGroups) @@ -629,7 +670,7 @@ func reflectClusterToStack(cluster domain.Cluster, appGroups []domain.AppGroup) out.StatusDesc = statusDesc /* - return domain.Stack{ + return model.Stack{ ID: domain.StackId(cluster.ID), OrganizationId: cluster.OrganizationId, Name: cluster.Name, @@ -663,7 +704,7 @@ func reflectClusterToStack(cluster domain.Cluster, appGroups []domain.AppGroup) } // [TODO] more pretty -func getStackStatus(cluster domain.Cluster, appGroups []domain.AppGroup) (domain.StackStatus, string) { +func getStackStatus(cluster model.Cluster, appGroups []model.AppGroup) (domain.StackStatus, string) { for _, appGroup := range appGroups { if appGroup.Status == domain.AppGroupStatus_PENDING && cluster.Status == domain.ClusterStatus_RUNNING { return domain.StackStatus_APPGROUP_INSTALLING, appGroup.StatusDesc diff --git a/internal/usecase/system-notification-rule.go b/internal/usecase/system-notification-rule.go new file mode 100644 index 00000000..2c737b08 --- /dev/null +++ b/internal/usecase/system-notification-rule.go @@ -0,0 +1,392 @@ +package usecase + +import ( + "context" + "fmt" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type ISystemNotificationRuleUsecase interface { + Get(ctx context.Context, systemNotificationRuleId uuid.UUID) (model.SystemNotificationRule, error) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotificationRule, error) + Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRule uuid.UUID, err error) + Update(ctx context.Context, dto model.SystemNotificationRule) error + Delete(ctx context.Context, systemNotificationRuleId uuid.UUID) (model.SystemNotificationRule, error) + GetByName(ctx context.Context, name string) (model.SystemNotificationRule, error) + MakeDefaultSystemNotificationRules(ctx context.Context, organizationId string, dto *model.Organization) error +} + +type SystemNotificationRuleUsecase struct { + repo repository.ISystemNotificationRuleRepository + organizationRepo repository.IOrganizationRepository + userRepo repository.IUserRepository + systemNotificationTemplateRepo repository.ISystemNotificationTemplateRepository +} + +func NewSystemNotificationRuleUsecase(r repository.Repository) ISystemNotificationRuleUsecase { + return &SystemNotificationRuleUsecase{ + repo: r.SystemNotificationRule, + organizationRepo: r.Organization, + userRepo: r.User, + systemNotificationTemplateRepo: r.SystemNotificationTemplate, + } +} + +func (u *SystemNotificationRuleUsecase) Create(ctx context.Context, dto model.SystemNotificationRule) (systemNotificationRuleId uuid.UUID, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + dto.CreatorId = &userId + dto.UpdatorId = &userId + + if _, err = u.GetByName(ctx, dto.Name); err == nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("duplicate systemNotificationRule name"), "SNR_CREATE_ALREADY_EXISTED_NAME", "") + } + + // Users + dto.TargetUsers = make([]model.User, 0) + for _, strId := range dto.TargetUserIds { + userId, err := uuid.Parse(strId) + if err == nil { + user, err := u.userRepo.GetByUuid(ctx, userId) + if err == nil { + dto.TargetUsers = append(dto.TargetUsers, user) + } + } + } + + // Make parameters + dto.SystemNotificationCondition.Parameter = []byte(helper.ModelToJson(dto.SystemNotificationCondition.Parameters)) + + systemNotificationRuleId, err = u.repo.Create(ctx, dto) + if err != nil { + return uuid.Nil, err + } + + return +} + +func (u *SystemNotificationRuleUsecase) Update(ctx context.Context, dto model.SystemNotificationRule) error { + rule, err := u.repo.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewBadRequestError(err, "SNR_NOT_EXISTED_STACK_TEMPLATE", "") + } + + // Users + dto.TargetUsers = make([]model.User, 0) + for _, strId := range dto.TargetUserIds { + userId, err := uuid.Parse(strId) + if err == nil { + user, err := u.userRepo.GetByUuid(ctx, userId) + if err == nil { + dto.TargetUsers = append(dto.TargetUsers, user) + } + } + } + + // Make parameters + dto.SystemNotificationCondition.Parameter = []byte(helper.ModelToJson(dto.SystemNotificationCondition.Parameters)) + dto.SystemNotificationCondition.ID = rule.SystemNotificationCondition.ID + + err = u.repo.Update(ctx, dto) + if err != nil { + return err + } + + // update status for appling kubernetes + if err = u.repo.UpdateStatus(ctx, dto.ID, domain.SystemNotificationRuleStatus_PENDING); err != nil { + return err + } + + return nil +} + +func (u *SystemNotificationRuleUsecase) Get(ctx context.Context, systemNotificationRuleId uuid.UUID) (res model.SystemNotificationRule, err error) { + res, err = u.repo.Get(ctx, systemNotificationRuleId) + if err != nil { + return res, err + } + return +} + +func (u *SystemNotificationRuleUsecase) GetByName(ctx context.Context, name string) (out model.SystemNotificationRule, err error) { + out, err = u.repo.GetByName(ctx, name) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return out, httpErrors.NewNotFoundError(err, "SNR_FAILED_FETCH_SYSTEM_NOTIFICATION_RULE", "") + } + return out, err + } + + return +} + +func (u *SystemNotificationRuleUsecase) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (res []model.SystemNotificationRule, err error) { + res, err = u.repo.FetchWithOrganization(ctx, organizationId, pg) + if err != nil { + return nil, err + } + return res, nil +} + +func (u *SystemNotificationRuleUsecase) Delete(ctx context.Context, systemNotificationRuleId uuid.UUID) (out model.SystemNotificationRule, err error) { + systemNotificationRule, err := u.repo.Get(ctx, systemNotificationRuleId) + if err != nil { + return out, err + } + + user, ok := request.UserFrom(ctx) + if !ok { + return out, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + systemNotificationRule.UpdatorId = &userId + + if systemNotificationRule.IsSystem { + return out, httpErrors.NewBadRequestError(fmt.Errorf("cannot delete system rules"), "SNR_CANNOT_DELETE_SYSTEM_RULE", "") + } + + err = u.repo.Delete(ctx, systemNotificationRule) + if err != nil { + return out, err + } + + // update status for appling kubernetes + if err = u.repo.UpdateStatus(ctx, systemNotificationRuleId, domain.SystemNotificationRuleStatus_PENDING); err != nil { + return out, err + } + + return systemNotificationRule, nil +} + +func (u *SystemNotificationRuleUsecase) MakeDefaultSystemNotificationRules(ctx context.Context, organizationId string, dto *model.Organization) error { + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return err + } + + /* + // 240426 : 기본 알림 설정은 "전체" 를 대상자로 한다. + organizationAdmin, err := u.userRepo.GetByUuid(ctx, *organization.AdminId) + if err != nil { + return err + } + */ + + pg := pagination.NewPaginationWithFilter("is_system", "", "$eq", []string{"1"}) + templates, err := u.systemNotificationTemplateRepo.Fetch(ctx, pg) + if err != nil { + return err + } + + err = u.organizationRepo.AddSystemNotificationTemplates(ctx, organizationId, templates) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_ADD_ORGANIZATION_SYSTEM_NOTIFICATION_TEMPLATE", "") + } + + rules := make([]model.SystemNotificationRule, 0) + for _, template := range templates { + if template.Name == domain.SN_TYPE_NODE_CPU_HIGH_LOAD { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_NODE_CPU_HIGH_LOAD + "-warning", + Description: "기본 시스템 알림 - CPU 사용량", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "warning", + Duration: "3m", + Parameter: []byte("[{\"order\": 0, \"value\": \"10\", \"operator\": \"<\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "CPU 사용량이 높습니다", + MessageContent: "스택 (<>)의 노드(<>)의 idle process의 cpu 점유율이 3분 동안 0% 입니다. (현재 사용률 {{$value}}). 워커 노드 CPU가 과부하 상태입니다. 일시적인 서비스 Traffic 증가, Workload의 SW 오류, Server HW Fan Fail등 다양한 원인으로 인해 발생할 수 있습니다.", + MessageActionProposal: "일시적인 Service Traffic의 증가가 관측되지 않았다면, Alert발생 노드에서 실행 되는 pod중 CPU 자원을 많이 점유하는 pod의 설정을 점검해 보시길 제안드립니다. 예를 들어 pod spec의 limit 설정으로 과도한 CPU자원 점유을 막을 수 있습니다.", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_NODE_MEMORY_HIGH_UTILIZATION { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_NODE_MEMORY_HIGH_UTILIZATION + "-warning", + Description: "기본 시스템 알림 - 메모리 사용량", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "warning", + Duration: "3m", + Parameter: []byte("[{\"order\": 0, \"value\": \"0.2\", \"operator\": \"<\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "메모리 사용량이 높습니다", + MessageContent: "스택 (<>)의 노드(<>)의 Memory 사용량이 3분동안 80% 를 넘어서고 있습니다. (현재 사용률 {{$value}}). 워커 노드의 Memory 사용량이 80%를 넘었습니다. 일시적인 서비스 증가 및 SW 오류등 다양한 원인으로 발생할 수 있습니다.", + MessageActionProposal: "일시적인 Service Traffic의 증가가 관측되지 않았다면, Alert발생 노드에서 실행되는 pod중 Memory 사용량이 높은 pod들에 대한 점검을 제안드립니다.", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_NODE_DISK_FULL { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_NODE_DISK_FULL + "-critical", + Description: "기본 시스템 알림 - 노드 디스크 사용량", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "critical", + Duration: "30m", + Parameter: []byte("[{\"order\": 0, \"value\": \"0\", \"operator\": \"<\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "노드 디스크 사용량이 높습니다.", + MessageContent: "지난 6시간동안의 추세로 봤을 때, 스택 (<>)의 노드(<>)의 root 볼륨은 24시간 안에 Disk full이 예상됨. 현재 Disk 사용 추세기준 24시간 내에 Disk 용량이 꽉 찰 것으로 예상됩니다.", + MessageActionProposal: "Disk 용량 최적화(삭제 및 Backup)을 수행하시길 권고합니다. 삭제할 내역이 없으면 증설 계획을 수립해 주십시요.", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_PVC_FULL { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_PVC_FULL + "-critical", + Description: "기본 시스템 알림 - PVC 사용량", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "critical", + Duration: "30m", + Parameter: []byte("[{\"order\": 0, \"value\": \"0\", \"operator\": \"<\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "PVC 사용량이 높습니다.", + MessageContent: "지난 6시간동안의 추세로 봤을 때, 스택 (<>)의 파드(<>)가 24시간 안에 Disk full이 예상됨. 현재 Disk 사용 추세기준 24시간 내에 Disk 용량이 꽉 찰것으로 예상됩니다. (<> 스택, <> PVC)", + MessageActionProposal: "Disk 용량 최적화(삭제 및 Backup)을 수행하시길 권고합니다. 삭제할 내역이 없으면 증설 계획을 수립해 주십시요.", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_POD_RESTART_FREQUENTLY { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_POD_RESTART_FREQUENTLY + "-critical", + Description: "기본 시스템 알림 - Pod 재기동", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "critical", + Duration: "30m", + Parameter: []byte("[{\"order\": 0, \"value\": \"2\", \"operator\": \">\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "스택의 Pod가 재기동되고 있습니다.", + MessageContent: "스택 (<>)의 파드(<>)가 30분 동안 5회 이상 재기동 ({{$value}} 회). 특정 Pod가 빈번하게 재기동 되고 있습니다. 점검이 필요합니다. (<> 스택, <> 파드)", + MessageActionProposal: "pod spec. 에 대한 점검이 필요합니다. pod의 log 및 status를 확인해 주세요.", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_POLICY_AUDITED { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_POLICY_AUDITED + "-critical", + Description: "기본 시스템 알림 - 정책 위반", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "critical", + Duration: "1m", + Parameter: []byte("[{\"order\": 0, \"value\": \"1\", \"operator\": \"==\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "정책 위반(<> / <>)", + MessageContent: "스택 (<>)의 자원(<> - <> / <>)에서 정책(<> / <>)위반이 발생했습니다. 메시지 - <>", + MessageActionProposal: "정책위반이 발생하였습니다.(<> / <>)", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } else if template.Name == domain.SN_TYPE_POLICY_BLOCKED { + ruleId := uuid.New() + rules = append(rules, model.SystemNotificationRule{ + ID: ruleId, + Name: domain.SN_TYPE_POLICY_BLOCKED + "-critical", + Description: "기본 시스템 알림 - 정책 위반 시도", + OrganizationId: organizationId, + NotificationType: template.NotificationType, + IsSystem: true, + SystemNotificationTemplateId: template.ID, + SystemNotificationCondition: model.SystemNotificationCondition{ + SystemNotificationRuleId: ruleId, + Severity: "critical", + Duration: "1m", + Parameter: []byte("[{\"order\": 0, \"value\": \"1\", \"operator\": \"==\"}]"), + EnableEmail: false, + EnablePortal: true, + }, + TargetUsers: []model.User{}, + MessageTitle: "정책 위반(<> / <>) 시도", + MessageContent: "스택 (<>)의 자원(<> - <> / <>)에서 정책(<> / <>)위반 시도가 발생했습니다. 메시지 - <>", + MessageActionProposal: "정책위반이 시도가 발생하였습니다.(<> / <>)", + Status: domain.SystemNotificationRuleStatus_PENDING, + CreatorId: organization.AdminId, + UpdatorId: organization.AdminId, + }) + } + } + + err = u.repo.Creates(ctx, rules) + if err != nil { + return err + } + + return nil +} diff --git a/internal/usecase/system-notification-template.go b/internal/usecase/system-notification-template.go new file mode 100644 index 00000000..99240baf --- /dev/null +++ b/internal/usecase/system-notification-template.go @@ -0,0 +1,232 @@ +package usecase + +import ( + "context" + "fmt" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type ISystemNotificationTemplateUsecase interface { + Get(ctx context.Context, alertId uuid.UUID) (model.SystemNotificationTemplate, error) + GetByName(ctx context.Context, name string) (model.SystemNotificationTemplate, error) + Fetch(ctx context.Context, pg *pagination.Pagination) ([]model.SystemNotificationTemplate, error) + FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotificationTemplate, error) + Create(ctx context.Context, dto model.SystemNotificationTemplate) (systemNotificationTemplate uuid.UUID, err error) + Update(ctx context.Context, dto model.SystemNotificationTemplate) error + Delete(ctx context.Context, systemNotificationTemplateId uuid.UUID) error + AddOrganizationSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplateIds []string) error + RemoveOrganizationSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplateIds []string) error +} + +type SystemNotificationTemplateUsecase struct { + repo repository.ISystemNotificationTemplateRepository + clusterRepo repository.IClusterRepository + organizationRepo repository.IOrganizationRepository + appGroupRepo repository.IAppGroupRepository + systemNotificationRuleRepo repository.ISystemNotificationRuleRepository +} + +func NewSystemNotificationTemplateUsecase(r repository.Repository) ISystemNotificationTemplateUsecase { + return &SystemNotificationTemplateUsecase{ + repo: r.SystemNotificationTemplate, + clusterRepo: r.Cluster, + appGroupRepo: r.AppGroup, + organizationRepo: r.Organization, + systemNotificationRuleRepo: r.SystemNotificationRule, + } +} + +func (u *SystemNotificationTemplateUsecase) Create(ctx context.Context, dto model.SystemNotificationTemplate) (systemNotificationTemplate uuid.UUID, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + dto.CreatorId = &userId + dto.UpdatorId = &userId + + if _, err = u.GetByName(ctx, dto.Name); err == nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("duplicate systemNotificationTemplate name"), "SNT_CREATE_ALREADY_EXISTED_NAME", "") + } + + systemNotificationTemplate, err = u.repo.Create(ctx, dto) + if err != nil { + return uuid.Nil, httpErrors.NewInternalServerError(err, "", "") + } + log.Info(ctx, "newly created SystemNotificationTemplate ID:", systemNotificationTemplate) + + dto.ID = systemNotificationTemplate + err = u.UpdateOrganizations(ctx, dto) + if err != nil { + return uuid.Nil, err + } + + return systemNotificationTemplate, nil +} + +func (u *SystemNotificationTemplateUsecase) Update(ctx context.Context, dto model.SystemNotificationTemplate) error { + _, err := u.repo.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewBadRequestError(err, "SNT_NOT_EXISTED_ALERT_TEMPLATE", "") + } + + err = u.repo.Update(ctx, dto) + if err != nil { + return err + } + + err = u.UpdateOrganizations(ctx, dto) + if err != nil { + return err + } + return nil +} + +func (u *SystemNotificationTemplateUsecase) Get(ctx context.Context, systemNotificationTemplateId uuid.UUID) (systemNotificationTemplate model.SystemNotificationTemplate, err error) { + systemNotificationTemplate, err = u.repo.Get(ctx, systemNotificationTemplateId) + if err != nil { + return systemNotificationTemplate, err + } + return +} + +func (u *SystemNotificationTemplateUsecase) GetByName(ctx context.Context, name string) (out model.SystemNotificationTemplate, err error) { + out, err = u.repo.GetByName(ctx, name) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return out, httpErrors.NewNotFoundError(err, "SNT_FAILED_FETCH_ALERT_TEMPLATE", "") + } + return out, err + } + + return +} + +func (u *SystemNotificationTemplateUsecase) Fetch(ctx context.Context, pg *pagination.Pagination) (alerts []model.SystemNotificationTemplate, err error) { + alerts, err = u.repo.Fetch(ctx, pg) + if err != nil { + return nil, err + } + return +} + +func (u *SystemNotificationTemplateUsecase) FetchWithOrganization(ctx context.Context, organizationId string, pg *pagination.Pagination) (res []model.SystemNotificationTemplate, err error) { + res, err = u.repo.FetchWithOrganization(ctx, organizationId, pg) + if err != nil { + return nil, err + } + return res, nil +} + +func (u *SystemNotificationTemplateUsecase) Delete(ctx context.Context, systemNotificationTemplateId uuid.UUID) (err error) { + systemNotificationTemplate, err := u.repo.Get(ctx, systemNotificationTemplateId) + if err != nil { + return err + } + + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("Invalid token"), "", "") + } + userId := user.GetUserId() + systemNotificationTemplate.UpdatorId = &userId + + if systemNotificationTemplate.IsSystem { + return httpErrors.NewBadRequestError(fmt.Errorf("cannot delete systemNotificationTemplate"), "SNT_CANNOT_DELETE_SYSTEM_TEMPLATE", "") + } + + // check if used + // system_notification_rules + pg := pagination.NewPaginationWithFilter("system_notification_template_id", "", "$eq", []string{systemNotificationTemplateId.String()}) + res, err := u.systemNotificationRuleRepo.Fetch(ctx, pg) + if err != nil { + return err + } + if len(res) > 0 { + return httpErrors.NewBadRequestError(fmt.Errorf("Failed to delete systemNotificationTemplate %s", systemNotificationTemplateId.String()), "SNT_FAILED_DELETE_EXIST_RULES", "") + } + + err = u.repo.Delete(ctx, systemNotificationTemplate) + if err != nil { + return err + } + + return +} + +func (u *SystemNotificationTemplateUsecase) UpdateOrganizations(ctx context.Context, dto model.SystemNotificationTemplate) error { + _, err := u.repo.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewBadRequestError(err, "SNT_NOT_EXISTED_ALERT_TEMPLATE", "") + } + + organizations := make([]model.Organization, 0) + for _, organizationId := range dto.OrganizationIds { + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err == nil { + organizations = append(organizations, organization) + } + } + + err = u.repo.UpdateOrganizations(ctx, dto.ID, organizations) + if err != nil { + return httpErrors.NewBadRequestError(err, "SNT_FAILED_UPDATE_ORGANIZATION", "") + } + + return nil +} + +func (u *SystemNotificationTemplateUsecase) AddOrganizationSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplateIds []string) error { + _, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_NOT_EXISTED_NAME", "") + } + + systemNotificationTemplates := make([]model.SystemNotificationTemplate, 0) + for _, strId := range systemNotificationTemplateIds { + systemNotificationTemplateId, _ := uuid.Parse(strId) + systemNotificationTemplate, err := u.repo.Get(ctx, systemNotificationTemplateId) + if err == nil { + systemNotificationTemplates = append(systemNotificationTemplates, systemNotificationTemplate) + } + } + + err = u.organizationRepo.AddSystemNotificationTemplates(ctx, organizationId, systemNotificationTemplates) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_ADD_ORGANIZATION_SYSTEM_NOTIFICATION_TEMPLATE", "") + } + + return nil +} + +func (u *SystemNotificationTemplateUsecase) RemoveOrganizationSystemNotificationTemplates(ctx context.Context, organizationId string, systemNotificationTemplateIds []string) error { + _, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + return httpErrors.NewBadRequestError(err, "O_NOT_EXISTED_NAME", "") + } + + systemNotificationTemplates := make([]model.SystemNotificationTemplate, 0) + for _, strId := range systemNotificationTemplateIds { + systemNotificationTemplateId, _ := uuid.Parse(strId) + systemNotificationTemplate, err := u.repo.Get(ctx, systemNotificationTemplateId) + if err == nil { + systemNotificationTemplates = append(systemNotificationTemplates, systemNotificationTemplate) + } + } + + err = u.organizationRepo.RemoveSystemNotificationTemplates(ctx, organizationId, systemNotificationTemplates) + if err != nil { + return httpErrors.NewBadRequestError(err, "ST_FAILED_REMOVE_ORGANIZATION_SYSTEM_NOTIFICATION_TEMPLATE", "") + } + + return nil +} diff --git a/internal/usecase/system-notification.go b/internal/usecase/system-notification.go new file mode 100644 index 00000000..6a2c577f --- /dev/null +++ b/internal/usecase/system-notification.go @@ -0,0 +1,396 @@ +package usecase + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/google/uuid" + "github.com/openinfradev/tks-api/internal/helper" + "github.com/openinfradev/tks-api/internal/mail" + "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" + "github.com/openinfradev/tks-api/internal/pagination" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" + "github.com/openinfradev/tks-api/pkg/log" + "github.com/pkg/errors" + "gorm.io/gorm" +) + +type ISystemNotificationUsecase interface { + Get(ctx context.Context, systemNotificationId uuid.UUID) (model.SystemNotification, error) + GetByName(ctx context.Context, organizationId string, name string) (model.SystemNotification, error) + FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) ([]model.SystemNotification, error) + Create(ctx context.Context, dto domain.CreateSystemNotificationRequest) (err error) + Update(ctx context.Context, dto model.SystemNotification) error + Delete(ctx context.Context, dto model.SystemNotification) error + + CreateSystemNotificationAction(ctx context.Context, dto model.SystemNotificationAction) (systemNotificationActionId uuid.UUID, err error) +} + +type SystemNotificationUsecase struct { + repo repository.ISystemNotificationRepository + clusterRepo repository.IClusterRepository + organizationRepo repository.IOrganizationRepository + appGroupRepo repository.IAppGroupRepository + systemNotificationRuleRepo repository.ISystemNotificationRuleRepository + userRepo repository.IUserRepository +} + +func NewSystemNotificationUsecase(r repository.Repository) ISystemNotificationUsecase { + return &SystemNotificationUsecase{ + repo: r.SystemNotification, + clusterRepo: r.Cluster, + appGroupRepo: r.AppGroup, + organizationRepo: r.Organization, + systemNotificationRuleRepo: r.SystemNotificationRule, + userRepo: r.User, + } +} + +func (u *SystemNotificationUsecase) Create(ctx context.Context, input domain.CreateSystemNotificationRequest) (err error) { + if input.SystemNotifications == nil || len(input.SystemNotifications) == 0 { + return fmt.Errorf("No data found") + } + + log.Info(ctx, helper.ModelToJson(input)) + + allClusters, err := u.clusterRepo.Fetch(ctx, nil) + if err != nil { + return fmt.Errorf("No clusters") + } + + for _, systemNotification := range input.SystemNotifications { + clusterId := systemNotification.Labels.TacoCluster + organizationId, err := u.getOrganizationFromCluster(&allClusters, clusterId) + if err != nil { + log.Error(ctx, err) + continue + } + + organization, err := u.organizationRepo.Get(ctx, organizationId) + if err != nil { + log.Error(ctx, err) + continue + } + primaryCluster, err := u.clusterRepo.Get(ctx, domain.ClusterId(organization.PrimaryClusterId)) + if err != nil { + log.Error(ctx, err) + continue + } + + rawData, err := json.Marshal(systemNotification) + if err != nil { + rawData = []byte{} + } + + /* + target := "" + // discriminative 에 target 에 대한 정보가 있다. + // discriminative: $labels.taco_cluster, $labels.instance + discriminative := systemNotification.Annotations.Discriminative + if discriminative != "" { + trimed := strings.TrimLeft(discriminative, " ") + trimed = strings.TrimLeft(trimed, "$") + arr := strings.Split(trimed, ",") + + for _, refer := range arr { + + } + } + */ + + node := "" + if strings.Contains(systemNotification.Labels.AlertName, "node") { + node = systemNotification.Labels.Instance + } + + var systemNotificationRuleId *uuid.UUID + if systemNotification.Annotations.SystemNotificationRuleId != "" { + id, err := uuid.Parse(systemNotification.Annotations.SystemNotificationRuleId) + if err == nil { + systemNotificationRuleId = &id + } + } + + dto := model.SystemNotification{ + OrganizationId: organizationId, + Name: systemNotification.Labels.AlertName, + Severity: systemNotification.Labels.Severity, + Node: node, + MessageTitle: systemNotification.Annotations.Message, + MessageContent: systemNotification.Annotations.Description, + MessageActionProposal: systemNotification.Annotations.Checkpoint, + Summary: systemNotification.Annotations.Summary, + ClusterId: domain.ClusterId(clusterId), + GrafanaUrl: u.makeGrafanaUrl(ctx, primaryCluster, systemNotification, domain.ClusterId(clusterId)), + RawData: rawData, + SystemNotificationRuleId: systemNotificationRuleId, + NotificationType: systemNotification.Annotations.AlertType, + } + + if systemNotification.Annotations.AlertType == "POLICY_NOTIFICATION" { + dto.PolicyName = systemNotification.Annotations.PolicyName + if strings.Contains(systemNotification.Labels.AlertName, "policy-audited") { + dto.MessageActionProposal = "감사" + } else if strings.Contains(systemNotification.Labels.AlertName, "policy-blocked") { + dto.MessageActionProposal = "거부" + } else { + dto.MessageActionProposal = "" + } + } + + _, err = u.repo.Create(ctx, dto) + if err != nil { + log.Error(ctx, "Failed to create systemNotification ", err) + continue + } + + if systemNotificationRuleId != nil { + rule, err := u.systemNotificationRuleRepo.Get(ctx, *systemNotificationRuleId) + if err != nil { + log.Error(ctx, "Failed to get systemNotificationRule ", err) + continue + } + + if rule.SystemNotificationCondition.EnableEmail { + to := []string{} + + // 아무것도 지정되어 있지 않다면, organization 전체 대상으로 발송한다. + if rule.TargetUsers == nil || len(rule.TargetUsers) == 0 { + users, err := u.userRepo.List(ctx, u.userRepo.OrganizationFilter(organizationId)) + if err != nil || users == nil { + log.Error(ctx, "Failed to get users ", err) + continue + } + for _, user := range *users { + to = append(to, user.Email) + } + } else { + for _, user := range rule.TargetUsers { + to = append(to, user.Email) + } + } + + message, err := mail.MakeSystemNotificationMessage(ctx, organizationId, systemNotification.Annotations.Message, systemNotification.Annotations.Description, to) + if err != nil { + log.Error(ctx, fmt.Sprintf("Failed to make email content. err : %s", err.Error())) + continue + } + mailer := mail.New(message) + if err := mailer.SendMail(ctx); err != nil { + log.Error(ctx, fmt.Sprintf("Failed to send email to %s. err : %s", to, err.Error())) + continue + } + } + } + + } + + return nil +} + +func (u *SystemNotificationUsecase) Update(ctx context.Context, dto model.SystemNotification) error { + return nil +} + +func (u *SystemNotificationUsecase) Get(ctx context.Context, systemNotificationId uuid.UUID) (systemNotification model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotification, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + systemNotification, err = u.repo.Get(ctx, systemNotificationId) + if err != nil { + return systemNotification, err + } + u.makeAdditionalInfo(&systemNotification, userInfo.GetUserId()) + + user, err := u.userRepo.GetByUuid(ctx, userInfo.GetUserId()) + if err == nil { + err = u.repo.UpdateRead(ctx, systemNotificationId, user) + if err != nil { + return systemNotification, err + } + } + + return +} + +func (u *SystemNotificationUsecase) GetByName(ctx context.Context, organizationId string, name string) (out model.SystemNotification, err error) { + out, err = u.repo.GetByName(ctx, organizationId, name) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return out, httpErrors.NewNotFoundError(err, "", "") + } + return out, err + } + return +} + +func (u *SystemNotificationUsecase) FetchSystemNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (systemNotifications []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotifications, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + systemNotifications, err = u.repo.FetchSystemNotifications(ctx, organizationId, pg) + if err != nil { + return nil, err + } + + for i := range systemNotifications { + u.makeAdditionalInfo(&systemNotifications[i], userInfo.GetUserId()) + } + + return systemNotifications, nil +} + +func (u *SystemNotificationUsecase) FetchPolicyNotifications(ctx context.Context, organizationId string, pg *pagination.Pagination) (systemNotifications []model.SystemNotification, err error) { + userInfo, ok := request.UserFrom(ctx) + if !ok { + return systemNotifications, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + systemNotifications, err = u.repo.FetchPolicyNotifications(ctx, organizationId, pg) + if err != nil { + return nil, err + } + + for i := range systemNotifications { + u.makeAdditionalInfo(&systemNotifications[i], userInfo.GetUserId()) + } + + return systemNotifications, nil +} + +func (u *SystemNotificationUsecase) Delete(ctx context.Context, dto model.SystemNotification) (err error) { + _, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + _, err = u.Get(ctx, dto.ID) + if err != nil { + return httpErrors.NewNotFoundError(err, "EV_NOT_FOUND_EVENT", "") + } + + err = u.repo.Delete(ctx, dto) + if err != nil { + return err + } + + return nil +} + +func (u *SystemNotificationUsecase) CreateSystemNotificationAction(ctx context.Context, dto model.SystemNotificationAction) (systemNotificationActionId uuid.UUID, err error) { + user, ok := request.UserFrom(ctx) + if !ok { + return uuid.Nil, httpErrors.NewUnauthorizedError(fmt.Errorf("Invalid token"), "A_INVALID_TOKEN", "") + } + + _, err = u.repo.Get(ctx, dto.SystemNotificationId) + if err != nil { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("Not found systemNotification"), "EV_NOT_FOUND_EVENT", "") + } + + userId := user.GetUserId() + dto.TakerId = &userId + dto.CreatedAt = time.Now() + + systemNotificationActionId, err = u.repo.CreateSystemNotificationAction(ctx, dto) + if err != nil { + return uuid.Nil, err + } + log.Info(ctx, "newly created systemNotificationActionId:", systemNotificationActionId) + + return +} + +func (u *SystemNotificationUsecase) getOrganizationFromCluster(clusters *[]model.Cluster, strId string) (organizationId string, err error) { + clusterId := domain.ClusterId(strId) + if !clusterId.Validate() { + return "", fmt.Errorf("Invalid clusterId %s", strId) + } + + for _, cluster := range *clusters { + if cluster.ID == clusterId { + return cluster.OrganizationId, nil + } + } + + return "", fmt.Errorf("No martched organization %s", strId) +} + +func (u *SystemNotificationUsecase) makeAdditionalInfo(systemNotification *model.SystemNotification, userId uuid.UUID) { + + systemNotification.FiredAt = &systemNotification.CreatedAt + //systemNotification.Status = model.SystemNotificationActionStatus_CREATED + + if len(systemNotification.SystemNotificationActions) > 0 { + systemNotification.TakedAt = &systemNotification.SystemNotificationActions[0].CreatedAt + for _, action := range systemNotification.SystemNotificationActions { + if action.Status == domain.SystemNotificationActionStatus_CLOSED { + systemNotification.ClosedAt = &action.CreatedAt + systemNotification.ProcessingSec = int((action.CreatedAt).Sub(systemNotification.CreatedAt).Seconds()) + } + } + + systemNotification.LastTaker = systemNotification.SystemNotificationActions[len(systemNotification.SystemNotificationActions)-1].Taker + systemNotification.TakedSec = int((systemNotification.SystemNotificationActions[0].CreatedAt).Sub(systemNotification.CreatedAt).Seconds()) + //systemNotification.Status = systemNotification.SystemNotificationActions[len(systemNotification.SystemNotificationActions)-1].Status + } + + systemNotification.Read = false + for _, v := range systemNotification.Readers { + if v.ID == userId { + systemNotification.Read = true + break + } + } +} + +func (u *SystemNotificationUsecase) makeGrafanaUrl(ctx context.Context, primaryCluster model.Cluster, systemNotification domain.SystemNotificationRequest, clusterId domain.ClusterId) (url string) { + primaryGrafanaEndpoint := "" + appGroups, err := u.appGroupRepo.Fetch(ctx, primaryCluster.ID, nil) + if err == nil { + for _, appGroup := range appGroups { + if appGroup.AppGroupType == domain.AppGroupType_LMA { + applications, err := u.appGroupRepo.GetApplications(ctx, appGroup.ID, domain.ApplicationType_GRAFANA) + if err != nil { + return "" + } + if len(applications) > 0 { + primaryGrafanaEndpoint = applications[0].Endpoint + } + } + } + } + + //// check type + //url = primaryGrafanaEndpoint + + // tks_node_dashboard/tks-kubernetes-view-nodes?orgId=1&refresh=30s&var-datasource=default&var-taco_cluster=c19rjkn4j&var-job=prometheus-node-exporter&var-hostname=All&var-node=10.0.168.71:9100&var-device=All&var-maxmount=%2F&var-show_hostname=prometheus-node-exporter-xt4vb + + switch systemNotification.Labels.AlertName { + case "node-memory-high-utilization": + url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" + case "node-cpu-high-load": + url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" + case "node-disk-full": + url = primaryGrafanaEndpoint + "/d/tks_node_dashboard/tks-kubernetes-view-nodes?var-taco_cluster=" + clusterId.String() + "&kiosk" + case "pod-restart-frequently": + url = primaryGrafanaEndpoint + "/d/tks_podv1_dashboard/tks-kubernetes-view-pods-v1?var-taco_cluster=" + clusterId.String() + "&kiosk" + case "pvc-full": + url = primaryGrafanaEndpoint + "/d/tks_cluster_dashboard/tks-kubernetes-view-cluster-global?var-taco_cluster=" + clusterId.String() + "&kiosk" + default: + url = primaryGrafanaEndpoint + "/d/tks_cluster_dashboard" + } + + return url +} diff --git a/internal/usecase/usecase.go b/internal/usecase/usecase.go index d9a67132..46302b69 100644 --- a/internal/usecase/usecase.go +++ b/internal/usecase/usecase.go @@ -1,22 +1,23 @@ package usecase -import ( - kube "github.com/openinfradev/tks-api/internal/kubernetes" - gcache "github.com/patrickmn/go-cache" - "k8s.io/client-go/kubernetes" -) - -func GetKubeClient(cache *gcache.Cache, clusterId string) (*kubernetes.Clientset, error) { - const prefix = "CACHE_KEY_KUBE_CLIENT_" - value, found := cache.Get(prefix + clusterId) - if found { - return value.(*kubernetes.Clientset), nil - } - client, err := kube.GetClientFromClusterId(clusterId) - if err != nil { - return nil, err - } - - cache.Set(prefix+clusterId, client, gcache.DefaultExpiration) - return client, nil +type Usecase struct { + Auth IAuthUsecase + User IUserUsecase + Cluster IClusterUsecase + Organization IOrganizationUsecase + AppGroup IAppGroupUsecase + AppServeApp IAppServeAppUsecase + CloudAccount ICloudAccountUsecase + StackTemplate IStackTemplateUsecase + Dashboard IDashboardUsecase + SystemNotification ISystemNotificationUsecase + SystemNotificationTemplate ISystemNotificationTemplateUsecase + SystemNotificationRule ISystemNotificationRuleUsecase + Stack IStackUsecase + Project IProjectUsecase + Role IRoleUsecase + Permission IPermissionUsecase + Audit IAuditUsecase + PolicyTemplate IPolicyTemplateUsecase + Policy IPolicyUsecase } diff --git a/internal/usecase/user.go b/internal/usecase/user.go index f507a7eb..35be8fb9 100644 --- a/internal/usecase/user.go +++ b/internal/usecase/user.go @@ -10,49 +10,54 @@ import ( "github.com/openinfradev/tks-api/internal/helper" "github.com/openinfradev/tks-api/internal/keycloak" "github.com/openinfradev/tks-api/internal/mail" - "github.com/openinfradev/tks-api/internal/middleware/auth/request" + "github.com/openinfradev/tks-api/internal/model" "github.com/openinfradev/tks-api/internal/pagination" "github.com/openinfradev/tks-api/internal/repository" - "github.com/openinfradev/tks-api/pkg/domain" "github.com/openinfradev/tks-api/pkg/httpErrors" "github.com/openinfradev/tks-api/pkg/log" "github.com/pkg/errors" ) type IUserUsecase interface { - CreateAdmin(organizationId string, email string) (*domain.User, error) - DeleteAdmin(organizationId string) error + CreateAdmin(ctx context.Context, user *model.User) (*model.User, error) + DeleteAdmin(ctx context.Context, organizationId string) error DeleteAll(ctx context.Context, organizationId string) error - Create(ctx context.Context, user *domain.User) (*domain.User, error) - List(ctx context.Context, organizationId string) (*[]domain.User, error) - ListWithPagination(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]domain.User, error) - Get(userId uuid.UUID) (*domain.User, error) - Update(ctx context.Context, userId uuid.UUID, user *domain.User) (*domain.User, error) - ResetPassword(userId uuid.UUID) error - ResetPasswordByAccountId(accountId string, organizationId string) error - Delete(userId uuid.UUID, organizationId string) error - GetByAccountId(ctx context.Context, accountId string, organizationId string) (*domain.User, error) - GetByEmail(ctx context.Context, email string, organizationId string) (*domain.User, error) - - UpdateByAccountId(ctx context.Context, accountId string, user *domain.User) (*domain.User, error) + Create(ctx context.Context, user *model.User) (*model.User, error) + List(ctx context.Context, organizationId string) (*[]model.User, error) + ListWithPagination(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]model.User, error) + Get(ctx context.Context, userId uuid.UUID) (*model.User, error) + Update(ctx context.Context, user *model.User) (*model.User, error) + ResetPassword(ctx context.Context, userId uuid.UUID) error + ResetPasswordByAccountId(ctx context.Context, accountId string, organizationId string) error + GenerateRandomPassword(ctx context.Context) string + Delete(ctx context.Context, userId uuid.UUID, organizationId string) error + GetByAccountId(ctx context.Context, accountId string, organizationId string) (*model.User, error) + GetByEmail(ctx context.Context, email string, organizationId string) (*model.User, error) + SendEmailForTemporaryPassword(ctx context.Context, accountId string, organizationId string, password string) error + + UpdateByAccountId(ctx context.Context, user *model.User) (*model.User, error) UpdatePasswordByAccountId(ctx context.Context, accountId string, originPassword string, newPassword string, organizationId string) error RenewalPasswordExpiredTime(ctx context.Context, userId uuid.UUID) error RenewalPasswordExpiredTimeByAccountId(ctx context.Context, accountId string, organizationId string) error DeleteByAccountId(ctx context.Context, accountId string, organizationId string) error - ValidateAccount(userId uuid.UUID, password string, organizationId string) error - ValidateAccountByAccountId(accountId string, password string, organizationId string) error + ValidateAccount(ctx context.Context, userId uuid.UUID, password string, organizationId string) error + ValidateAccountByAccountId(ctx context.Context, accountId string, password string, organizationId string) error - UpdateByAccountIdByAdmin(ctx context.Context, accountId string, user *domain.User) (*domain.User, error) + UpdateByAccountIdByAdmin(ctx context.Context, user *model.User) (*model.User, error) + + ListUsersByRole(ctx context.Context, organizationId string, roleId string, pg *pagination.Pagination) (*[]model.User, error) } type UserUsecase struct { + authRepository repository.IAuthRepository userRepository repository.IUserRepository + roleRepository repository.IRoleRepository organizationRepository repository.IOrganizationRepository kc keycloak.IKeycloak } func (u *UserUsecase) RenewalPasswordExpiredTime(ctx context.Context, userId uuid.UUID) error { - user, err := u.userRepository.GetByUuid(userId) + user, err := u.userRepository.GetByUuid(ctx, userId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status != http.StatusNotFound { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") @@ -60,9 +65,9 @@ func (u *UserUsecase) RenewalPasswordExpiredTime(ctx context.Context, userId uui return httpErrors.NewInternalServerError(err, "", "") } - err = u.userRepository.UpdatePassword(userId, user.Organization.ID, user.Password, false) + err = u.userRepository.UpdatePasswordAt(ctx, userId, user.Organization.ID, false) if err != nil { - log.ErrorfWithContext(ctx, "failed to update password expired time: %v", err) + log.Errorf(ctx, "failed to update password expired time: %v", err) return httpErrors.NewInternalServerError(err, "", "") } @@ -70,28 +75,24 @@ func (u *UserUsecase) RenewalPasswordExpiredTime(ctx context.Context, userId uui } func (u *UserUsecase) RenewalPasswordExpiredTimeByAccountId(ctx context.Context, accountId string, organizationId string) error { - user, err := u.userRepository.Get(accountId, organizationId) + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status != http.StatusNotFound { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") } return httpErrors.NewInternalServerError(err, "", "") } - userId, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - return u.RenewalPasswordExpiredTime(ctx, userId) + return u.RenewalPasswordExpiredTime(ctx, user.ID) } -func (u *UserUsecase) ResetPassword(userId uuid.UUID) error { - user, err := u.userRepository.GetByUuid(userId) +func (u *UserUsecase) ResetPassword(ctx context.Context, userId uuid.UUID) error { + user, err := u.userRepository.GetByUuid(ctx, userId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") } } - userInKeycloak, err := u.kc.GetUser(user.Organization.ID, user.AccountId) + userInKeycloak, err := u.kc.GetUser(ctx, user.Organization.ID, user.AccountId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") @@ -107,61 +108,58 @@ func (u *UserUsecase) ResetPassword(userId uuid.UUID) error { Temporary: gocloak.BoolP(false), }, } - if err = u.kc.UpdateUser(user.Organization.ID, userInKeycloak); err != nil { + if err = u.kc.UpdateUser(ctx, user.Organization.ID, userInKeycloak); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - if user.Password, err = helper.HashPassword(randomPassword); err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - if err = u.userRepository.UpdatePassword(userId, user.Organization.ID, user.Password, true); err != nil { + if err = u.userRepository.UpdatePasswordAt(ctx, userId, user.Organization.ID, true); err != nil { return httpErrors.NewInternalServerError(err, "", "") } - message, err := mail.MakeTemporaryPasswordMessage(user.Email, user.Organization.ID, user.AccountId, randomPassword) + message, err := mail.MakeTemporaryPasswordMessage(ctx, user.Email, user.Organization.ID, user.AccountId, randomPassword) if err != nil { - log.Errorf("mail.MakeVerityIdentityMessage error. %v", err) + log.Errorf(ctx, "mail.MakeVerityIdentityMessage error. %v", err) return httpErrors.NewInternalServerError(err, "", "") } mailer := mail.New(message) - if err := mailer.SendMail(); err != nil { + if err := mailer.SendMail(ctx); err != nil { return httpErrors.NewInternalServerError(err, "", "") } return nil } -func (u *UserUsecase) ResetPasswordByAccountId(accountId string, organizationId string) error { - user, err := u.userRepository.Get(accountId, organizationId) +func (u *UserUsecase) ResetPasswordByAccountId(ctx context.Context, accountId string, organizationId string) error { + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") } return httpErrors.NewInternalServerError(err, "", "") } - userId, err := uuid.Parse(user.ID) - if err != nil { - return httpErrors.NewInternalServerError(err, "", "") - } - return u.ResetPassword(userId) + return u.ResetPassword(ctx, user.ID) } -func (u *UserUsecase) ValidateAccount(userId uuid.UUID, password string, organizationId string) error { - user, err := u.userRepository.GetByUuid(userId) +func (u *UserUsecase) GenerateRandomPassword(ctx context.Context) string { + return helper.GenerateRandomString(passwordLength) +} + +func (u *UserUsecase) ValidateAccount(ctx context.Context, userId uuid.UUID, password string, organizationId string) error { + user, err := u.userRepository.GetByUuid(ctx, userId) if err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") } - _, err = u.kc.Login(user.AccountId, password, organizationId) + _, err = u.kc.Login(ctx, user.AccountId, password, organizationId) if err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("invalid password"), "A_INVALID_PASSWORD", "") } return nil } -func (u *UserUsecase) ValidateAccountByAccountId(accountId string, password string, organizationId string) error { - _, err := u.kc.Login(organizationId, accountId, password) +func (u *UserUsecase) ValidateAccountByAccountId(ctx context.Context, accountId string, password string, organizationId string) error { + _, err := u.kc.Login(ctx, organizationId, accountId, password) return err } @@ -169,7 +167,7 @@ func (u *UserUsecase) DeleteAll(ctx context.Context, organizationId string) erro // TODO: implement me as transaction // TODO: clean users in keycloak - err := u.userRepository.Flush(organizationId) + err := u.userRepository.Flush(ctx, organizationId) if err != nil { return err } @@ -177,13 +175,13 @@ func (u *UserUsecase) DeleteAll(ctx context.Context, organizationId string) erro return nil } -func (u *UserUsecase) DeleteAdmin(organizationId string) error { - user, err := u.kc.GetUser(organizationId, "admin") +func (u *UserUsecase) DeleteAdmin(ctx context.Context, organizationId string) error { + user, err := u.kc.GetUser(ctx, organizationId, "admin") if err != nil { return errors.Wrap(err, "get user failed") } - err = u.kc.DeleteUser(organizationId, "admin") + err = u.kc.DeleteUser(ctx, organizationId, "admin") if err != nil { return errors.Wrap(err, "delete user failed") } @@ -193,7 +191,7 @@ func (u *UserUsecase) DeleteAdmin(organizationId string) error { return errors.Wrap(err, "parse user id failed") } - err = u.userRepository.DeleteWithUuid(userUuid) + err = u.userRepository.DeleteWithUuid(ctx, userUuid) if err != nil { return errors.Wrap(err, "delete user failed") } @@ -201,94 +199,53 @@ func (u *UserUsecase) DeleteAdmin(organizationId string) error { return nil } -func (u *UserUsecase) CreateAdmin(orgainzationId string, email string) (*domain.User, error) { +func (u *UserUsecase) CreateAdmin(ctx context.Context, user *model.User) (*model.User, error) { + // Generate Admin user object randomPassword := helper.GenerateRandomString(passwordLength) - user := domain.User{ - AccountId: "admin", - Password: randomPassword, - Email: email, - Role: domain.Role{ - Name: "admin", - }, - Organization: domain.Organization{ - ID: orgainzationId, - }, - Name: "admin", - } + user.Password = randomPassword - // Create user in keycloak - groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, orgainzationId)} - err := u.kc.CreateUser(orgainzationId, &gocloak.User{ - Username: gocloak.StringP(user.AccountId), - Email: gocloak.StringP(user.Email), - Credentials: &[]gocloak.CredentialRepresentation{ - { - Type: gocloak.StringP("password"), - Value: gocloak.StringP(user.Password), - Temporary: gocloak.BoolP(false), - }, - }, - Groups: &groups, - FirstName: gocloak.StringP(user.Name), - }) - if err != nil { - return nil, errors.Wrap(err, "creating user in keycloak failed") - } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) - if err != nil { - return nil, errors.Wrap(err, "getting user from keycloak failed") - } - - userUuid, err := uuid.Parse(*keycloakUser.ID) + // Create Admin user in keycloak & DB + resUser, err := u.Create(context.Background(), user) if err != nil { return nil, err } - hashedPassword, err := helper.HashPassword(user.Password) + // Send mail of temporary password + organizationInfo, err := u.organizationRepository.Get(ctx, resUser.Organization.ID) if err != nil { return nil, err } - - roles, err := u.userRepository.FetchRoles() + message, err := mail.MakeGeneratingOrganizationMessage(ctx, resUser.Organization.ID, organizationInfo.Name, user.Email, user.AccountId, randomPassword) if err != nil { - return nil, err - } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID - } - } - roleUuid, err := uuid.Parse(user.Role.ID) - if err != nil { - return nil, err - } - resUser, err := u.userRepository.CreateWithUuid(userUuid, user.AccountId, user.Name, hashedPassword, user.Email, - user.Department, user.Description, user.Organization.ID, roleUuid) - if err != nil { - return nil, err + return nil, httpErrors.NewInternalServerError(err, "", "") } - err = u.userRepository.UpdatePassword(userUuid, user.Organization.ID, hashedPassword, true) - if err != nil { - return nil, err + mailer := mail.New(message) + if err := mailer.SendMail(ctx); err != nil { + return nil, httpErrors.NewInternalServerError(err, "", "") } - organizationInfo, err := u.organizationRepository.Get(orgainzationId) + return resUser, nil +} + +func (u *UserUsecase) SendEmailForTemporaryPassword(ctx context.Context, accountId string, organizationId string, password string) error { + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { - return nil, err + return err } - message, err := mail.MakeGeneratingOrganizationMessage(orgainzationId, organizationInfo.Name, user.Email, user.AccountId, randomPassword) + message, err := mail.MakeTemporaryPasswordMessage(ctx, user.Email, organizationId, accountId, password) if err != nil { - return nil, httpErrors.NewInternalServerError(err, "", "") + return err } mailer := mail.New(message) - if err := mailer.SendMail(); err != nil { - return nil, httpErrors.NewInternalServerError(err, "", "") + if err := mailer.SendMail(ctx); err != nil { + return err } - return &resUser, nil + return nil + } func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId string, originPassword string, newPassword string, @@ -296,10 +253,10 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s if originPassword == newPassword { return httpErrors.NewBadRequestError(fmt.Errorf("new password is same with origin password"), "A_SAME_OLD_PASSWORD", "") } - if _, err := u.kc.Login(accountId, originPassword, organizationId); err != nil { + if _, err := u.kc.Login(ctx, accountId, originPassword, organizationId); err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("invalid origin password"), "A_INVALID_PASSWORD", "") } - originUser, err := u.kc.GetUser(organizationId, accountId) + originUser, err := u.kc.GetUser(ctx, organizationId, accountId) if err != nil { return err } @@ -311,27 +268,18 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s }, } - err = u.kc.UpdateUser(organizationId, originUser) + err = u.kc.UpdateUser(ctx, organizationId, originUser) if err != nil { return errors.Wrap(err, "updating user in keycloak failed") } - // update password in DB - - user, err := u.userRepository.Get(accountId, organizationId) + // update password UpdateAt in DB + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { return errors.Wrap(err, "getting user from repository failed") } - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return errors.Wrap(err, "parsing uuid failed") - } - hashedPassword, err := helper.HashPassword(newPassword) - if err != nil { - return errors.Wrap(err, "hashing password failed") - } - err = u.userRepository.UpdatePassword(userUuid, organizationId, hashedPassword, false) + err = u.userRepository.UpdatePasswordAt(ctx, user.ID, organizationId, false) if err != nil { return errors.Wrap(err, "updating user in repository failed") } @@ -339,8 +287,8 @@ func (u *UserUsecase) UpdatePasswordByAccountId(ctx context.Context, accountId s return nil } -func (u *UserUsecase) List(ctx context.Context, organizationId string) (users *[]domain.User, err error) { - users, err = u.userRepository.List(u.userRepository.OrganizationFilter(organizationId)) +func (u *UserUsecase) List(ctx context.Context, organizationId string) (users *[]model.User, err error) { + users, err = u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId)) if err != nil { return nil, err } @@ -348,8 +296,8 @@ func (u *UserUsecase) List(ctx context.Context, organizationId string) (users *[ return } -func (u *UserUsecase) ListWithPagination(ctx context.Context, organizationId string, pg *pagination.Pagination) (users *[]domain.User, err error) { - users, err = u.userRepository.ListWithPagination(pg, organizationId) +func (u *UserUsecase) ListWithPagination(ctx context.Context, organizationId string, pg *pagination.Pagination) (users *[]model.User, err error) { + users, err = u.userRepository.ListWithPagination(ctx, pg, organizationId) if err != nil { return nil, err } @@ -357,8 +305,8 @@ func (u *UserUsecase) ListWithPagination(ctx context.Context, organizationId str return } -func (u *UserUsecase) Get(userId uuid.UUID) (*domain.User, error) { - user, err := u.userRepository.GetByUuid(userId) +func (u *UserUsecase) Get(ctx context.Context, userId uuid.UUID) (*model.User, error) { + user, err := u.userRepository.GetByUuid(ctx, userId) if err != nil { if _, status := httpErrors.ErrorResponse(err); status == http.StatusNotFound { return nil, httpErrors.NewBadRequestError(fmt.Errorf("user not found"), "U_NO_USER", "") @@ -369,8 +317,8 @@ func (u *UserUsecase) Get(userId uuid.UUID) (*domain.User, error) { return &user, nil } -func (u *UserUsecase) GetByAccountId(ctx context.Context, accountId string, organizationId string) (*domain.User, error) { - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), +func (u *UserUsecase) GetByAccountId(ctx context.Context, accountId string, organizationId string) (*model.User, error) { + users, err := u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.AccountIdFilter(accountId)) if err != nil { return nil, err @@ -379,8 +327,8 @@ func (u *UserUsecase) GetByAccountId(ctx context.Context, accountId string, orga return &(*users)[0], nil } -func (u *UserUsecase) GetByEmail(ctx context.Context, email string, organizationId string) (*domain.User, error) { - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(organizationId), +func (u *UserUsecase) GetByEmail(ctx context.Context, email string, organizationId string) (*model.User, error) { + users, err := u.userRepository.List(ctx, u.userRepository.OrganizationFilter(organizationId), u.userRepository.EmailFilter(email)) if err != nil { return nil, err @@ -389,42 +337,19 @@ func (u *UserUsecase) GetByEmail(ctx context.Context, email string, organization return &(*users)[0], nil } -func (u *UserUsecase) Update(ctx context.Context, userId uuid.UUID, user *domain.User) (*domain.User, error) { - storedUser, err := u.Get(userId) +func (u *UserUsecase) Update(ctx context.Context, user *model.User) (*model.User, error) { + storedUser, err := u.Get(ctx, user.ID) if err != nil { return nil, err } user.AccountId = storedUser.AccountId - return u.UpdateByAccountId(ctx, storedUser.AccountId, user) + return u.UpdateByAccountId(ctx, user) } -func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, user *domain.User) (*domain.User, error) { - userInfo, ok := request.UserFrom(ctx) - if !ok { - return nil, fmt.Errorf("user in the context is empty") - } - - _, err := u.kc.Login(user.AccountId, user.Password, userInfo.GetOrganizationId()) - if err != nil { - return nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid password"), "", "") - } - - originUser, err := u.kc.GetUser(userInfo.GetOrganizationId(), accountId) - if err != nil { - return nil, err - } - if (originUser.Email == nil || *originUser.Email != user.Email) || (originUser.FirstName == nil || *originUser.FirstName != user.Name) { - originUser.Email = gocloak.StringP(user.Email) - originUser.FirstName = gocloak.StringP(user.Name) - err = u.kc.UpdateUser(userInfo.GetOrganizationId(), originUser) - if err != nil { - return nil, err - } - } - - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(userInfo.GetOrganizationId()), - u.userRepository.AccountIdFilter(accountId)) +func (u *UserUsecase) UpdateByAccountId(ctx context.Context, user *model.User) (*model.User, error) { + users, err := u.userRepository.List(ctx, u.userRepository.OrganizationFilter(user.Organization.ID), + u.userRepository.AccountIdFilter(user.AccountId)) if err != nil { if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") @@ -437,40 +362,39 @@ func (u *UserUsecase) UpdateByAccountId(ctx context.Context, accountId string, u return nil, fmt.Errorf("multiple users found") } - userUuid, err := uuid.Parse((*users)[0].ID) - if err != nil { - return nil, err - } - - originPassword := (*users)[0].Password - - roleUuid, err := uuid.Parse((*users)[0].Role.ID) - if err != nil { - return nil, err + if ((*users)[0].Email != user.Email) || ((*users)[0].Name != user.Name) { + err = u.kc.UpdateUser(ctx, user.Organization.ID, &gocloak.User{ + ID: gocloak.StringP(user.ID.String()), + Email: gocloak.StringP(user.Email), + FirstName: gocloak.StringP(user.Name), + }) + if err != nil { + return nil, err + } } + user.ID = (*users)[0].ID - *user, err = u.userRepository.UpdateWithUuid(userUuid, user.AccountId, user.Name, originPassword, roleUuid, user.Email, - user.Department, user.Description) + resp, err := u.userRepository.Update(ctx, user) if err != nil { return nil, errors.Wrap(err, "updating user in repository failed") } - return user, nil + return resp, nil } -func (u *UserUsecase) Delete(userId uuid.UUID, organizationId string) error { - user, err := u.userRepository.GetByUuid(userId) +func (u *UserUsecase) Delete(ctx context.Context, userId uuid.UUID, organizationId string) error { + user, err := u.userRepository.GetByUuid(ctx, userId) if err != nil { return httpErrors.NewBadRequestError(fmt.Errorf("not found user"), "", "") } - err = u.userRepository.DeleteWithUuid(userId) + err = u.userRepository.DeleteWithUuid(ctx, userId) if err != nil { return err } // Delete user in keycloak - err = u.kc.DeleteUser(organizationId, user.AccountId) + err = u.kc.DeleteUser(ctx, organizationId, user.AccountId) if err != nil { return err } @@ -478,22 +402,18 @@ func (u *UserUsecase) Delete(userId uuid.UUID, organizationId string) error { return nil } func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, organizationId string) error { - user, err := u.userRepository.Get(accountId, organizationId) + user, err := u.userRepository.Get(ctx, accountId, organizationId) if err != nil { return err } - userUuid, err := uuid.Parse(user.ID) - if err != nil { - return err - } - err = u.userRepository.DeleteWithUuid(userUuid) + err = u.userRepository.DeleteWithUuid(ctx, user.ID) if err != nil { return err } // Delete user in keycloak - err = u.kc.DeleteUser(organizationId, accountId) + err = u.kc.DeleteUser(ctx, organizationId, accountId) if err != nil { return err } @@ -501,10 +421,14 @@ func (u *UserUsecase) DeleteByAccountId(ctx context.Context, accountId string, o return nil } -func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.User, error) { +func (u *UserUsecase) Create(ctx context.Context, user *model.User) (*model.User, error) { // Create user in keycloak - groups := []string{fmt.Sprintf("%s@%s", user.Role.Name, user.Organization.ID)} - err := u.kc.CreateUser(user.Organization.ID, &gocloak.User{ + var groups []string + for _, role := range user.Roles { + groups = append(groups, fmt.Sprintf("%s@%s", role.Name, user.Organization.ID)) + } + + userUuidStr, err := u.kc.CreateUser(ctx, user.Organization.ID, &gocloak.User{ Username: gocloak.StringP(user.AccountId), Credentials: &[]gocloak.CredentialRepresentation{ { @@ -517,129 +441,98 @@ func (u *UserUsecase) Create(ctx context.Context, user *domain.User) (*domain.Us Groups: &groups, FirstName: gocloak.StringP(user.Name), }) - if err != nil { - if _, err := u.kc.GetUser(user.Organization.ID, user.AccountId); err == nil { - return nil, httpErrors.NewConflictError(errors.New("user already exists"), "", "") - } - - return nil, errors.Wrap(err, "creating user in keycloak failed") - } - keycloakUser, err := u.kc.GetUser(user.Organization.ID, user.AccountId) - if err != nil { - return nil, errors.Wrap(err, "getting user from keycloak failed") - } - - userUuid, err := uuid.Parse(*keycloakUser.ID) - if err != nil { - return nil, err - } - - hashedPassword, err := helper.HashPassword(user.Password) if err != nil { return nil, err } - roles, err := u.userRepository.FetchRoles() - if err != nil { - return nil, err - } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID - } - } - roleUuid, err := uuid.Parse(user.Role.ID) - if err != nil { + if user.ID, err = uuid.Parse(userUuidStr); err != nil { return nil, err } - resUser, err := u.userRepository.CreateWithUuid(userUuid, user.AccountId, user.Name, hashedPassword, user.Email, - user.Department, user.Description, user.Organization.ID, roleUuid) + // Create user in DB + resUser, err := u.userRepository.Create(ctx, user) + //resUser, err := u.userRepository.Create(ctx, userUuid, user.AccountId, user.Name, user.Email, + // user.Department, user.Description, user.Organization.ID, roleUuid) if err != nil { return nil, err } - return &resUser, nil + return resUser, nil } -func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, accountId string, user *domain.User) (*domain.User, error) { - userInfo, ok := request.UserFrom(ctx) - if !ok { - return nil, fmt.Errorf("user in the context is empty") +func (u *UserUsecase) UpdateByAccountIdByAdmin(ctx context.Context, newUser *model.User) (*model.User, error) { + if newUser.AccountId == "" { + return nil, httpErrors.NewBadRequestError(fmt.Errorf("accountId is required"), "C_INVALID_ACCOUNT_ID", "") } - originUser, err := u.kc.GetUser(userInfo.GetOrganizationId(), accountId) + originUser, err := u.userRepository.Get(ctx, newUser.AccountId, newUser.Organization.ID) if err != nil { return nil, err } - if (originUser.Email == nil || *originUser.Email != user.Email) || (originUser.FirstName == nil || *originUser.FirstName != user.Name) { - originUser.Email = gocloak.StringP(user.Email) - originUser.FirstName = gocloak.StringP(user.Name) - err = u.kc.UpdateUser(userInfo.GetOrganizationId(), originUser) - if err != nil { - return nil, err - } - } - users, err := u.userRepository.List(u.userRepository.OrganizationFilter(userInfo.GetOrganizationId()), - u.userRepository.AccountIdFilter(accountId)) - if err != nil { - if _, code := httpErrors.ErrorResponse(err); code == http.StatusNotFound { - return nil, httpErrors.NewNotFoundError(httpErrors.NotFound, "", "") - } - return nil, errors.Wrap(err, "getting users from repository failed") + unassigningRoleIds, assigningRoleIds := make(map[string]model.Role), make(map[string]model.Role) + for _, role := range newUser.Roles { + assigningRoleIds[role.ID] = role } - if len(*users) == 0 { - return nil, fmt.Errorf("user not found") - } else if len(*users) > 1 { - return nil, fmt.Errorf("multiple users found") + for _, role := range originUser.Roles { + if _, ok := assigningRoleIds[role.ID]; !ok { + unassigningRoleIds[role.ID] = role + } else { + delete(assigningRoleIds, role.ID) + } } - if user.Role.Name != (*users)[0].Role.Name { - originGroupName := fmt.Sprintf("%s@%s", (*users)[0].Role.Name, userInfo.GetOrganizationId()) - newGroupName := fmt.Sprintf("%s@%s", user.Role.Name, userInfo.GetOrganizationId()) - if err := u.kc.LeaveGroup(userInfo.GetOrganizationId(), (*users)[0].ID, originGroupName); err != nil { - log.ErrorfWithContext(ctx, "leave group in keycloak failed: %v", err) + + for _, role := range unassigningRoleIds { + groupName := fmt.Sprintf("%s@%s", role.Name, originUser.Organization.ID) + if err := u.kc.LeaveGroup(ctx, originUser.Organization.ID, originUser.ID.String(), groupName); err != nil { + log.Errorf(ctx, "leave group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } - if err := u.kc.JoinGroup(userInfo.GetOrganizationId(), (*users)[0].ID, newGroupName); err != nil { - log.ErrorfWithContext(ctx, "join group in keycloak failed: %v", err) + } + + for _, role := range assigningRoleIds { + groupName := fmt.Sprintf("%s@%s", role.Name, originUser.Organization.ID) + if err := u.kc.JoinGroup(ctx, originUser.Organization.ID, originUser.ID.String(), groupName); err != nil { + log.Errorf(ctx, "join group in keycloak failed: %v", err) return nil, httpErrors.NewInternalServerError(err, "", "") } } - userUuid, err := uuid.Parse((*users)[0].ID) + err = u.authRepository.UpdateExpiredTimeOnToken(ctx, originUser.Organization.ID, originUser.ID.String()) if err != nil { - return nil, err + log.Errorf(ctx, "update expired time on token failed: %v", err) + return nil, httpErrors.NewInternalServerError(err, "", "") } - originPassword := (*users)[0].Password + originUser.Name = newUser.Name + originUser.Email = newUser.Email + originUser.Department = newUser.Department + originUser.Description = newUser.Description + originUser.Roles = newUser.Roles - roles, err := u.userRepository.FetchRoles() + resp, err := u.userRepository.Update(ctx, &originUser) if err != nil { - return nil, err - } - for _, role := range *roles { - if role.Name == user.Role.Name { - user.Role.ID = role.ID - } + return nil, errors.Wrap(err, "updating user in repository failed") } - roleUuid, err := uuid.Parse(user.Role.ID) + + return resp, nil +} + +func (u *UserUsecase) ListUsersByRole(ctx context.Context, organizationId string, roleId string, pg *pagination.Pagination) (*[]model.User, error) { + users, err := u.userRepository.ListUsersByRole(ctx, organizationId, roleId, pg) if err != nil { return nil, err } - *user, err = u.userRepository.UpdateWithUuid(userUuid, user.AccountId, user.Name, originPassword, roleUuid, user.Email, - user.Department, user.Description) - if err != nil { - return nil, errors.Wrap(err, "updating user in repository failed") - } + return users, nil - return user, nil } func NewUserUsecase(r repository.Repository, kc keycloak.IKeycloak) IUserUsecase { return &UserUsecase{ + authRepository: r.Auth, userRepository: r.User, + roleRepository: r.Role, kc: kc, organizationRepository: r.Organization, } diff --git a/internal/validator/validator.go b/internal/validator/validator.go index 212a6394..3a161738 100644 --- a/internal/validator/validator.go +++ b/internal/validator/validator.go @@ -1,17 +1,27 @@ package validator import ( + "context" "regexp" + "strings" "unicode/utf8" "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" validator "github.com/go-playground/validator/v10" en_translations "github.com/go-playground/validator/v10/translations/en" - "github.com/opentracing/opentracing-go/log" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/log" ) -const REGEX_RFC1123 = `^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$` +const ( + REGEX_RFC1123 = `^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$` + REGEX_SIMPLE_SEMVER = `^v\d+\.\d+\.\d+$` + REGEX_PASCAL_CASE = `^([A-Z][a-z\d]+)+$` // 대문자로 시작하는 camel case(pascal case or upper camel case)를 표현한 정규식 + REGEX_RFC1123_DNS_LABEL = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" + REGEX_RESOURCE_NAME = `^` + REGEX_RFC1123_DNS_LABEL + "$" + REGEX_RFC1123_SUBDOMAIN = `^` + REGEX_RFC1123_DNS_LABEL + `(\.` + REGEX_RFC1123_DNS_LABEL + `)*$` +) func NewValidator() (*validator.Validate, *ut.UniversalTranslator) { en := en.New() @@ -21,12 +31,17 @@ func NewValidator() (*validator.Validate, *ut.UniversalTranslator) { v := validator.New() err := en_translations.RegisterDefaultTranslations(v, trans) if err != nil { - log.Error(err) + log.Error(context.TODO(), err) } // register custom validator _ = v.RegisterValidation("rfc1123", validateRfc1123) _ = v.RegisterValidation("name", validateName) + _ = v.RegisterValidation("version", validateVersion) + _ = v.RegisterValidation("pascalcase", validatePascalCase) + _ = v.RegisterValidation("resourcename", validateResourceName) + _ = v.RegisterValidation("matchnamespace", validateMatchNamespace) + _ = v.RegisterValidation("matchkinds", validateMatchKinds) // register custom error _ = v.RegisterTranslation("required", trans, func(ut ut.Translator) error { @@ -65,3 +80,131 @@ func validateName(fl validator.FieldLevel) bool { return utf8.RuneCountInString(fl.Field().String()) <= 30 } + +func validateVersion(fl validator.FieldLevel) bool { + if fl.Field().String() == "" { + return false + } + + r, _ := regexp.Compile(REGEX_SIMPLE_SEMVER) + return r.MatchString(fl.Field().String()) +} + +func validatePascalCase(fl validator.FieldLevel) bool { + if fl.Field().String() == "" { + return false + } + + r, _ := regexp.Compile(REGEX_PASCAL_CASE) + return r.MatchString(fl.Field().String()) +} + +func validateResourceName(fl validator.FieldLevel) bool { + // 정책 리소스 이름을 지정하지 않으면 지정하기 때문에 유효함 + // 다른 리소스 이름을 처리할 때에는 분리 필요 + if fl.Field().String() == "" { + return true + } + + r, _ := regexp.Compile(REGEX_RESOURCE_NAME) + return r.MatchString(fl.Field().String()) +} + +func validateMatchKinds(fl validator.FieldLevel) bool { + kinds, ok := fl.Field().Interface().([]domain.Kinds) + if !ok { + return false + } + + for _, kind := range kinds { + if ok := validateMatchKindAPIGroup(kind.APIGroups) && validateMatchKindKind(kind.Kinds); !ok { + return false + } + } + + return true +} + +func validateMatchKindAPIGroup(apigroups []string) bool { + if len(apigroups) == 0 { + return true + } + + containsWildcard := false + + r, _ := regexp.Compile(REGEX_RFC1123_SUBDOMAIN) + + for _, apigroup := range apigroups { + if apigroup == "*" || apigroup == "" { + containsWildcard = true + } else { + if !r.MatchString(apigroup) { + return false + } + } + } + + if containsWildcard && len(apigroups) != 1 { + return false + } + + return true +} + +func validateMatchKindKind(kinds []string) bool { + if len(kinds) == 0 { + return true + } + + containsWildcard := false + + r, _ := regexp.Compile(REGEX_PASCAL_CASE) + + for _, kind := range kinds { + if kind == "*" || kind == "" { + containsWildcard = true + } else { + if !r.MatchString(kind) { + return false + } + } + } + + if containsWildcard && len(kinds) != 1 { + return false + } + + return true +} + +func validateMatchNamespace(fl validator.FieldLevel) bool { + namespaces, ok := fl.Field().Interface().([]string) + if !ok { + return false + } + + if len(namespaces) == 0 { + return true + } + + containsWildcard := false + + r, _ := regexp.Compile(REGEX_RESOURCE_NAME) + + for _, namespace := range namespaces { + if namespace == "*" || namespace == "" { + containsWildcard = true + } else { + trimmed := strings.TrimSuffix(strings.TrimPrefix(namespace, "*"), "*") + if !r.MatchString(trimmed) { + return false + } + } + } + + if containsWildcard && len(namespaces) != 1 { + return false + } + + return true +} diff --git a/pkg/argo-client/client-mock.go b/pkg/argo-client/client-mock.go index d3a22de5..47891107 100644 --- a/pkg/argo-client/client-mock.go +++ b/pkg/argo-client/client-mock.go @@ -1,6 +1,7 @@ package argowf import ( + "context" "net/http" "time" ) @@ -23,22 +24,22 @@ func NewMock() (ArgoClient, error) { }, nil } -func (c *ArgoClientMockImpl) GetWorkflowTemplates(namespace string) (*GetWorkflowTemplatesResponse, error) { +func (c *ArgoClientMockImpl) GetWorkflowTemplates(ctx context.Context, namespace string) (*GetWorkflowTemplatesResponse, error) { return nil, nil } -func (c *ArgoClientMockImpl) GetWorkflow(namespace string, workflowName string) (*Workflow, error) { +func (c *ArgoClientMockImpl) GetWorkflow(ctx context.Context, namespace string, workflowName string) (*Workflow, error) { return nil, nil } -func (c *ArgoClientMockImpl) GetWorkflowLog(namespace string, container string, workflowName string) (string, error) { +func (c *ArgoClientMockImpl) GetWorkflowLog(ctx context.Context, namespace string, container string, workflowName string) (string, error) { return "", nil } -func (c *ArgoClientMockImpl) GetWorkflows(namespace string) (*GetWorkflowsResponse, error) { +func (c *ArgoClientMockImpl) GetWorkflows(ctx context.Context, namespace string) (*GetWorkflowsResponse, error) { return nil, nil } -func (c *ArgoClientMockImpl) SumbitWorkflowFromWftpl(wftplName string, opts SubmitOptions) (string, error) { +func (c *ArgoClientMockImpl) SumbitWorkflowFromWftpl(ctx context.Context, wftplName string, opts SubmitOptions) (string, error) { return "", nil } diff --git a/pkg/argo-client/client.go b/pkg/argo-client/client.go index b100b5a4..3b10eb50 100644 --- a/pkg/argo-client/client.go +++ b/pkg/argo-client/client.go @@ -2,6 +2,7 @@ package argowf import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -12,11 +13,11 @@ import ( ) type ArgoClient interface { - GetWorkflowTemplates(namespace string) (*GetWorkflowTemplatesResponse, error) - GetWorkflow(namespace string, workflowName string) (*Workflow, error) - GetWorkflowLog(namespace string, container string, workflowName string) (logs string, err error) - GetWorkflows(namespace string) (*GetWorkflowsResponse, error) - SumbitWorkflowFromWftpl(wftplName string, opts SubmitOptions) (string, error) + GetWorkflowTemplates(ctx context.Context, namespace string) (*GetWorkflowTemplatesResponse, error) + GetWorkflow(ctx context.Context, namespace string, workflowName string) (*Workflow, error) + GetWorkflowLog(ctx context.Context, namespace string, container string, workflowName string) (logs string, err error) + GetWorkflows(ctx context.Context, namespace string) (*GetWorkflowsResponse, error) + SumbitWorkflowFromWftpl(ctx context.Context, wftplName string, opts SubmitOptions) (string, error) } type ArgoClientImpl struct { @@ -46,7 +47,7 @@ func New(host string, port int, ssl bool, token string) (ArgoClient, error) { }, nil } -func (c *ArgoClientImpl) GetWorkflowTemplates(namespace string) (*GetWorkflowTemplatesResponse, error) { +func (c *ArgoClientImpl) GetWorkflowTemplates(ctx context.Context, namespace string) (*GetWorkflowTemplatesResponse, error) { res, err := c.client.Get(fmt.Sprintf("%s/api/v1/workflow-templates/%s", c.url, namespace)) if err != nil { return nil, err @@ -60,7 +61,7 @@ func (c *ArgoClientImpl) GetWorkflowTemplates(namespace string) (*GetWorkflowTem defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -71,13 +72,13 @@ func (c *ArgoClientImpl) GetWorkflowTemplates(namespace string) (*GetWorkflowTem wftplRes := GetWorkflowTemplatesResponse{} if err := json.Unmarshal(body, &wftplRes); err != nil { - log.Error("an error was unexpected while parsing response from api /workflow template.") + log.Error(ctx, "an error was unexpected while parsing response from api /workflow template.") return nil, err } return &wftplRes, nil } -func (c *ArgoClientImpl) GetWorkflow(namespace string, workflowName string) (*Workflow, error) { +func (c *ArgoClientImpl) GetWorkflow(ctx context.Context, namespace string, workflowName string) (*Workflow, error) { res, err := c.client.Get(fmt.Sprintf("%s/api/v1/workflows/%s/%s", c.url, namespace, workflowName)) if err != nil { return nil, err @@ -91,7 +92,7 @@ func (c *ArgoClientImpl) GetWorkflow(namespace string, workflowName string) (*Wo defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -102,15 +103,15 @@ func (c *ArgoClientImpl) GetWorkflow(namespace string, workflowName string) (*Wo workflowRes := Workflow{} if err := json.Unmarshal(body, &workflowRes); err != nil { - log.Error("an error was unexpected while parsing response from api /workflow template.") + log.Error(ctx, "an error was unexpected while parsing response from api /workflow template.") return nil, err } return &workflowRes, nil } -func (c *ArgoClientImpl) GetWorkflowLog(namespace string, container string, workflowName string) (logs string, err error) { - log.Info(fmt.Sprintf("%s/api/v1/workflows/%s/%s/log?logOptions.container=%s", c.url, namespace, workflowName, container)) +func (c *ArgoClientImpl) GetWorkflowLog(ctx context.Context, namespace string, container string, workflowName string) (logs string, err error) { + log.Info(ctx, fmt.Sprintf("%s/api/v1/workflows/%s/%s/log?logOptions.container=%s", c.url, namespace, workflowName, container)) res, err := c.client.Get(fmt.Sprintf("%s/api/v1/workflows/%s/%s/log?logOptions.container=%s", c.url, namespace, workflowName, container)) if err != nil { return logs, err @@ -124,7 +125,7 @@ func (c *ArgoClientImpl) GetWorkflowLog(namespace string, container string, work defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -136,7 +137,7 @@ func (c *ArgoClientImpl) GetWorkflowLog(namespace string, container string, work return string(body[:]), nil } -func (c *ArgoClientImpl) GetWorkflows(namespace string) (*GetWorkflowsResponse, error) { +func (c *ArgoClientImpl) GetWorkflows(ctx context.Context, namespace string) (*GetWorkflowsResponse, error) { res, err := c.client.Get(fmt.Sprintf("%s/api/v1/workflows/%s", c.url, namespace)) if err != nil { return nil, err @@ -150,7 +151,7 @@ func (c *ArgoClientImpl) GetWorkflows(namespace string) (*GetWorkflowsResponse, defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -161,21 +162,21 @@ func (c *ArgoClientImpl) GetWorkflows(namespace string) (*GetWorkflowsResponse, workflowsRes := GetWorkflowsResponse{} if err := json.Unmarshal(body, &workflowsRes); err != nil { - log.Error("an error was unexpected while parsing response from api /workflow template.") + log.Error(ctx, "an error was unexpected while parsing response from api /workflow template.") return nil, err } return &workflowsRes, nil } -func (c *ArgoClientImpl) SumbitWorkflowFromWftpl(wftplName string, opts SubmitOptions) (string, error) { +func (c *ArgoClientImpl) SumbitWorkflowFromWftpl(ctx context.Context, wftplName string, opts SubmitOptions) (string, error) { reqBody := submitWorkflowRequestBody{ Namespace: "argo", ResourceKind: "WorkflowTemplate", ResourceName: wftplName, SubmitOptions: opts, } - log.Debug("SumbitWorkflowFromWftpl reqBody ", reqBody) + log.Debug(ctx, "SumbitWorkflowFromWftpl reqBody ", reqBody) reqBodyBytes, err := json.Marshal(reqBody) if err != nil { @@ -199,7 +200,7 @@ func (c *ArgoClientImpl) SumbitWorkflowFromWftpl(wftplName string, opts SubmitOp defer func() { if res != nil { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } } }() @@ -211,7 +212,7 @@ func (c *ArgoClientImpl) SumbitWorkflowFromWftpl(wftplName string, opts SubmitOp submitRes := SubmitWorkflowResponse{} if err := json.Unmarshal(body, &submitRes); err != nil { - log.Error("an error was unexpected while parsing response from api /submit.") + log.Error(ctx, "an error was unexpected while parsing response from api /submit.") return "", err } return submitRes.Metadata.Name, nil diff --git a/pkg/domain/admin/policy-template.go b/pkg/domain/admin/policy-template.go new file mode 100644 index 00000000..9ce9e483 --- /dev/null +++ b/pkg/domain/admin/policy-template.go @@ -0,0 +1,174 @@ +package admin + +import ( + "time" + + "github.com/openinfradev/tks-api/pkg/domain" +) + +// type PermittedOrganization struct { +// OrganizationId string `json:"organizationId"` +// OrganizationName string `json:"organizationName"` +// Permitted bool `json:"permitted"` +// } + +type PolicyTemplateResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Type string `json:"type" enums:"tks,organization" example:"tks"` + Creator domain.SimpleUserResponse `json:"creator"` + Updator domain.SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + TemplateName string `json:"templateName" example:"필수 Label 검사"` + Kind string `json:"kind" example:"K8sRequiredLabels"` + Severity string `json:"severity" enums:"low,medium,high" example:"medium"` + Deprecated bool `json:"deprecated" example:"false"` + Version string `json:"version,omitempty" example:"v1.0.1"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` + ParametersSchema []*domain.ParameterDef `json:"parametersSchema,omitempty"` + Rego string `json:"rego" example:"rego 코드"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` + + PermittedOrganizations []domain.SimpleOrganizationResponse `json:"permittedOrganizations"` +} + +type SimplePolicyTemplateResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Type string `json:"type" enums:"tks,organization" example:"tks"` + Name string `json:"templateName" example:"필수 Label 검사"` + Version string `json:"version,omitempty" example:"v1.0.1"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` +} + +type CreatePolicyTemplateRequest struct { + TemplateName string `json:"templateName" validate:"required,name" example:"필수 Label 검사"` + Kind string `json:"kind" example:"K8sRequiredLabels" validate:"required,pascalcase"` + Severity string `json:"severity" validate:"required,oneof=low medium high" enums:"low,medium,high" example:"medium"` + Deprecated bool `json:"deprecated" example:"false"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` + ParametersSchema []*domain.ParameterDef `json:"parametersSchema,omitempty"` + // "type: object\nproperties: message:\n type: string\n labels:\n type: array\n items:\n type: object\n properties:\n key:\n type: string\n allowedRegex:\n type: string" + + Rego string `json:"rego" example:"rego 코드" validate:"required"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` + + PermittedOrganizationIds []string `json:"permittedOrganizationIds"` +} + +type CreatePolicyTemplateReponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` +} + +type CreateOrganizationPolicyTemplateReponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` +} + +type UpdatePolicyTemplateRequest struct { + TemplateName *string `json:"templateName,omitempty" validate:"required,name" example:"필수 Label 검사"` + Description *string `json:"description,omitempty"` + Severity *string `json:"severity,omitempty" validate:"omitempty,oneof=low medium high" enums:"low,medium,high" example:"medium"` + Deprecated *bool `json:"deprecated,omitempty" example:"false"` + PermittedOrganizationIds *[]string `json:"permittedOrganizationIds,omitempty"` +} + +type GetPolicyTemplateDeployResponse struct { + DeployVersion map[string]string `json:"deployVersion"` +} + +type GetOrganizationPolicyTemplateDeployResponse struct { + DeployVersion map[string]string `json:"deployVersion"` +} + +type ListPolicyTemplateVersionsResponse struct { + Versions []string `json:"versions" example:"v1.1.0,v1.0.1,v1.0.0"` +} + +type ListOrganizationPolicyTemplateVersionsResponse struct { + Versions []string `json:"versions" example:"v1.1.0,v1.0.1,v1.0.0"` +} + +type GetPolicyTemplateVersionResponse struct { + PolicyTemplate PolicyTemplateResponse `json:"policyTemplate"` +} + +type CreatePolicyTemplateVersionRequest struct { + VersionUpType string `json:"versionUpType" validate:"required,oneof=major minor patch" enums:"major,minor,patch" example:"minor"` + CurrentVersion string `json:"currentVersion" validate:"required,version" example:"v1.0.0"` + ExpectedVersion string `json:"expectedVersion" validate:"required,version" example:"v1.1.0"` + + ParametersSchema []*domain.ParameterDef `json:"parametersSchema,omitempty"` + // "type: object\nproperties: message:\n type: string\n labels:\n type: array\n items:\n type: object\n properties:\n key:\n type: string\n allowedRegex:\n type: string" + + Rego string `json:"rego" example:"rego 코드" validate:"required"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` +} + +type CreatePolicyTemplateVersionResponse struct { + Version string `json:"version" example:"v1.1.1"` +} + +type GetPolicyTemplateResponse struct { + PolicyTemplate PolicyTemplateResponse `json:"policyTemplate"` +} + +type ListPolicyTemplateResponse struct { + PolicyTemplates []PolicyTemplateResponse `json:"policyTemplates"` + Pagination domain.PaginationResponse `json:"pagination"` +} + +type PolicyTemplateStatistics struct { + OrganizationId string `json:"organizationId"` + OrganizationName string `json:"organizationName"` + UsageCount int `json:"usageCount"` +} + +type ListPolicyTemplateStatisticsResponse struct { + PolicyTemplateStatistics []PolicyTemplateStatistics `json:"policyTemplateStatistics"` +} + +type ExistsPolicyTemplateNameResponse struct { + Existed bool `json:"existed"` +} + +type ExistsPolicyTemplateKindResponse struct { + Existed bool `json:"existed"` +} + +type RegoCompileRequest struct { + Rego string `json:"rego" example:"Rego 코드" validate:"required"` + Libs []string `json:"libs,omitempty"` +} + +type RegoCompileResponse struct { + ParametersSchema []*domain.ParameterDef `json:"parametersSchema,omitempty"` + Errors []domain.RegoCompieError `json:"errors,omitempty"` +} + +type ExtractParametersRequest struct { + Rego string `json:"rego" example:"Rego 코드" validate:"required"` + Libs []string `json:"libs,omitempty"` +} + +type ExtractParametersResponse struct { + ParametersSchema []*domain.ParameterDef `json:"parametersSchema,omitempty"` + Errors []domain.RegoCompieError `json:"errors,omitempty"` +} + +type AddPermittedPolicyTemplatesForOrganizationRequest struct { + PolicyTemplateIds []string `json:"policyTemplateIds"` +} + +type UpdatePermittedPolicyTemplatesForOrganizationRequest struct { + PolicyTemplateIds []string `json:"policyTemplateIds"` +} + +type DeletePermittedPolicyTemplatesForOrganizationRequest struct { + PolicyTemplateIds []string `json:"policyTemplateIds"` +} diff --git a/pkg/domain/alert.go b/pkg/domain/alert.go deleted file mode 100644 index 7201ef24..00000000 --- a/pkg/domain/alert.go +++ /dev/null @@ -1,181 +0,0 @@ -package domain - -import ( - "time" - - "github.com/google/uuid" -) - -// enum -type AlertActionStatus int32 - -const ( - AlertActionStatus_CREATED AlertActionStatus = iota - AlertActionStatus_INPROGRESS - AlertActionStatus_CLOSED - AlertActionStatus_ERROR -) - -var alertActionStatus = [...]string{ - "CREATED", - "INPROGRESS", - "CLOSED", - "ERROR", -} - -func (m AlertActionStatus) String() string { return alertActionStatus[(m)] } -func (m AlertActionStatus) FromString(s string) AlertActionStatus { - for i, v := range alertActionStatus { - if v == s { - return AlertActionStatus(i) - } - } - return AlertActionStatus_ERROR -} - -// 내부 -type Alert struct { - ID uuid.UUID - OrganizationId string - Organization Organization - Name string - Description string - Code string - Grade string - Message string - ClusterId ClusterId - Cluster Cluster - Node string - CheckPoint string - Summary string - GrafanaUrl string - FiredAt *time.Time - TakedAt *time.Time - ClosedAt *time.Time - TakedSec int - ProcessingSec int - Status AlertActionStatus - AlertActions []AlertAction - LastTaker User - RawData []byte - CreatorId *uuid.UUID - Creator User - UpdatorId *uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time -} - -type AlertAction struct { - ID uuid.UUID - AlertId uuid.UUID - Content string - Status AlertActionStatus - TakerId *uuid.UUID - Taker User - CreatedAt time.Time - UpdatedAt time.Time -} - -type CreateAlertRequestAlert struct { - Status string `json:"status"` - GeneratorURL string `json:"generatorURL"` - FingerPrint string `json:"fingerprint"` - StartsAt time.Time `json:"startsAt"` - EndsAt time.Time `json:"endsAt"` - Labels struct { - AlertName string `json:"alertname"` - Container string `json:"container"` - Endpoint string `json:"endpoint"` - Job string `json:"job"` - Namespace string `json:"namespace"` - Pod string `json:"pod"` - Prometheus string `json:"prometheus"` - Service string `json:"service"` - Severity string `json:"severity"` - Instance string `json:"instance"` - TacoCluster string `json:"taco_cluster"` - } `json:"labels"` - Annotations struct { - Message string `json:"message"` - Summary string `json:"summary"` - Description string `json:"description"` - Checkpoint string `json:"Checkpoint"` - Discriminative string `json:"discriminative"` - } `json:"annotations"` -} - -type CreateAlertRequest struct { - Receiver string `json:"receiver"` - Status string `json:"status"` - ExternalURL string `json:"externalURL"` - Version string `json:"version"` - GroupKey string `json:"groupKey"` - TruncateAlerts int `json:"truncateAlerts"` - Alerts []CreateAlertRequestAlert `json:"alerts"` - GroupLabels struct { - Alertname string `json:"alertname"` - } `json:"groupLabels"` - //CommonLabels string `json:"commonLabels"` - //CommonAnnotations string `json:"commonAnnotations"` -} - -type AlertResponse struct { - ID string `json:"id"` - OrganizationId string `json:"organizationId"` - Name string `json:"name"` - Description string `json:"description"` - Message string `json:"message"` - Code string `json:"code"` - Grade string `json:"grade"` - Cluster SimpleClusterResponse `json:"cluster"` - GrafanaUrl string `json:"grafanaUrl"` - Node string `json:"node"` - FiredAt *time.Time `json:"firedAt"` - TakedAt *time.Time `json:"takedAt"` - ClosedAt *time.Time `json:"closedAt"` - Status string `json:"status"` - ProcessingSec int `json:"processingSec"` - TakedSec int `json:"takedSec"` - AlertActions []AlertActionResponse `json:"alertActions"` - LastTaker SimpleUserResponse `json:"lastTaker"` - RawData string `json:"rawData"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type AlertActionResponse struct { - ID uuid.UUID `json:"id"` - AlertId uuid.UUID `json:"alertId"` - Content string `json:"content"` - Status string `json:"status"` - Taker SimpleUserResponse `json:"taker"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type GetAlertsResponse struct { - Alerts []AlertResponse `json:"alerts"` - Pagination PaginationResponse `json:"pagination"` -} - -type GetAlertResponse struct { - Alert AlertResponse `json:"alert"` -} - -type CreateAlertResponse struct { - ID string `json:"id"` -} - -type UpdateAlertRequest struct { - Description string `json:"description"` -} - -type CreateAlertActionRequest struct { - Content string `json:"content"` - Status string `json:"status" validate:"oneof=INPROGRESS CLOSED"` -} - -type CreateAlertActionResponse struct { - ID string `json:"id"` -} diff --git a/pkg/domain/app-group.go b/pkg/domain/app-group.go index d4e3211d..87ed1bbe 100644 --- a/pkg/domain/app-group.go +++ b/pkg/domain/app-group.go @@ -114,33 +114,6 @@ func (m AppGroupType) FromString(s string) AppGroupType { return AppGroupType_UNSPECIFIED } -type AppGroup = struct { - ID AppGroupId - Name string - ClusterId ClusterId - AppGroupType AppGroupType - Description string - WorkflowId string - Status AppGroupStatus - StatusDesc string - CreatorId *uuid.UUID - Creator User - UpdatorId *uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time -} - -type Application = struct { - ID uuid.UUID - AppGroupId AppGroupId - Endpoint string - Metadata string - ApplicationType ApplicationType - CreatedAt time.Time - UpdatedAt time.Time -} - type AppGroupResponse = struct { ID AppGroupId `json:"id"` Name string `json:"name"` @@ -157,13 +130,13 @@ type AppGroupResponse = struct { } type ApplicationResponse = struct { - ID uuid.UUID `json:"id"` - AppGroupId AppGroupId `json:"appGroupId"` - Endpoint string `json:"endpoint"` - Metadata string `json:"metadata"` - ApplicationType ApplicationType `json:"applicationType"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID uuid.UUID `json:"id"` + AppGroupId AppGroupId `json:"appGroupId"` + Endpoint string `json:"endpoint"` + Metadata string `json:"metadata"` + Type ApplicationType `json:"applicationType"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } type CreateAppGroupRequest struct { @@ -178,9 +151,9 @@ type CreateAppGroupResponse struct { } type CreateApplicationRequest struct { - ApplicationType string `json:"applicationType"` - Endpoint string `json:"endpoint"` - Metadata string `json:"metadata"` + Type string `json:"applicationType"` + Endpoint string `json:"endpoint"` + Metadata string `json:"metadata"` } type GetAppGroupsResponse struct { diff --git a/pkg/domain/app-serve-app.go b/pkg/domain/app-serve-app.go index 5cb82ff1..757b877a 100644 --- a/pkg/domain/app-serve-app.go +++ b/pkg/domain/app-serve-app.go @@ -1,70 +1,56 @@ package domain -import ( - "time" - - "github.com/google/uuid" - "gorm.io/gorm" -) - -type AppServeApp struct { - ID string `gorm:"primarykey" json:"id,omitempty"` - Name string `gorm:"index" json:"name,omitempty"` // application name - Namespace string `json:"namespace,omitempty"` // application namespace - OrganizationId string `json:"organizationId,omitempty"` // contractId is a contract ID which this app belongs to - Type string `json:"type,omitempty"` // type (build/deploy/all) - AppType string `json:"appType,omitempty"` // appType (spring/springboot) - EndpointUrl string `json:"endpointUrl,omitempty"` // endpoint URL of deployed app - PreviewEndpointUrl string `json:"previewEndpointUrl,omitempty"` // preview svc endpoint URL in B/G deployment - TargetClusterId string `json:"targetClusterId,omitempty"` // target cluster to which the app is deployed - TargetClusterName string `gorm:"-:all" json:"targetClusterName,omitempty"` // target cluster name - Status string `gorm:"index" json:"status,omitempty"` // status is status of deployed app - GrafanaUrl string `json:"grafanaUrl,omitempty"` // grafana dashboard URL for deployed app - CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt" ` - UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` - DeletedAt *time.Time `json:"deletedAt"` - AppServeAppTasks []AppServeAppTask `gorm:"foreignKey:AppServeAppId" json:"appServeAppTasks"` -} - -type AppServeAppTask struct { - ID string `gorm:"primarykey" json:"id,omitempty"` - AppServeAppId string `gorm:"not null" json:"appServeAppId,omitempty"` // ID for appServeApp that this task belongs to - Version string `json:"version,omitempty"` // application version - Status string `json:"status,omitempty"` // status is app status - Output string `json:"output,omitempty"` // output for task result - ArtifactUrl string `json:"artifactUrl,omitempty"` // URL of java app artifact (Eg, Jar) - ImageUrl string `json:"imageUrl,omitempty"` // URL of built image for app - ExecutablePath string `json:"executablePath,omitempty"` // Executable path of app image - Profile string `json:"profile,omitempty"` // java app profile - AppConfig string `json:"appConfig,omitempty"` // java app config - AppSecret string `json:"appSecret,omitempty"` // java app secret - ExtraEnv string `json:"extraEnv,omitempty"` // env variable list for java app - Port string `json:"port,omitempty"` // java app port - ResourceSpec string `json:"resourceSpec,omitempty"` // resource spec of app pod - HelmRevision int32 `gorm:"default:0" json:"helmRevision,omitempty"` // revision of deployed helm release - Strategy string `json:"strategy,omitempty"` // deployment strategy (eg, rolling-update) - RollbackVersion string `json:"rollbackVersion,omitempty"` // rollback target version +import "time" + +type AppServeAppResponse struct { + ID string `json:"id"` + Name string `json:"name"` // application name + Namespace string `json:"namespace"` // application namespace + OrganizationId string `json:"organizationId"` // contractId is a contract ID which this app belongs to + ProjectId string `json:"projectId"` // project ID which this app belongs to + Type string `json:"type"` // type (build/deploy/all) + AppType string `json:"appType"` // appType (spring/springboot) + EndpointUrl string `json:"endpointUrl"` // endpoint URL of deployed app + PreviewEndpointUrl string `json:"previewEndpointUrl"` // preview svc endpoint URL in B/G deployment + TargetClusterId string `json:"targetClusterId"` // target cluster to which the app is deployed + TargetClusterName string `json:"targetClusterName"` // target cluster name + Status string `json:"status"` // status is status of deployed app + GrafanaUrl string `json:"grafanaUrl"` // grafana dashboard URL for deployed app + Description string `json:"description"` // description for application + CreatedAt time.Time `json:"createdAt" ` + UpdatedAt *time.Time `json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type AppServeAppTaskResponse struct { + ID string `json:"id"` + AppServeAppId string `json:"appServeAppId"` // ID for appServeApp that this task belongs to + Version string `json:"version"` // application version + Status string `json:"status"` // status is app status + Output string `json:"output"` // output for task result + ArtifactUrl string `json:"artifactUrl"` // URL of java app artifact (Eg, Jar) + ImageUrl string `json:"imageUrl"` // URL of built image for app + ExecutablePath string `json:"executablePath"` // Executable path of app image + Profile string `json:"profile"` // java app profile + AppConfig string `json:"appConfig"` // java app config + AppSecret string `json:"appSecret"` // java app secret + ExtraEnv string `json:"extraEnv"` // env variable list for java app + Port string `json:"port"` // java app port + ResourceSpec string `json:"resourceSpec"` // resource spec of app pod + HelmRevision int32 `json:"helmRevision"` // revision of deployed helm release + Strategy string `json:"strategy"` // deployment strategy (eg, rolling-update) + RollbackVersion string `json:"rollbackVersion"` // rollback target version PvEnabled bool `json:"pvEnabled"` PvStorageClass string `json:"pvStorageClass"` PvAccessMode string `json:"pvAccessMode"` PvSize string `json:"pvSize"` PvMountPath string `json:"pvMountPath"` - AvailableRollback bool `gorm:"-:all" json:"availableRollback"` - CreatedAt time.Time `gorm:"autoCreateTime:false" json:"createdAt"` // createdAt is a creation timestamp for the application - UpdatedAt *time.Time `gorm:"autoUpdateTime:false" json:"updatedAt"` + AvailableRollback bool `json:"availableRollback"` + CreatedAt time.Time `json:"createdAt"` // createdAt is a creation timestamp for the application + UpdatedAt *time.Time `json:"updatedAt"` DeletedAt *time.Time `json:"deletedAt"` } -func (a *AppServeApp) BeforeCreate(tx *gorm.DB) (err error) { - a.ID = uuid.New().String() - return nil -} - -func (t *AppServeAppTask) BeforeCreate(tx *gorm.DB) (err error) { - t.ID = uuid.New().String() - return nil -} - type CreateAppServeAppRequest struct { // App Name string `json:"name" validate:"required,rfc1123,name"` @@ -157,17 +143,19 @@ type RollbackAppServeAppRequest struct { } type GetAppServeAppsResponse struct { - AppServeApps []AppServeApp `json:"appServeApps"` - Pagination PaginationResponse `json:"pagination"` + AppServeApps []AppServeAppResponse `json:"appServeApps"` + Pagination PaginationResponse `json:"pagination"` } -type GetAppServeAppResponse struct { - AppServeApp AppServeApp `json:"appServeApp"` - Stages []StageResponse `json:"stages"` +type GetAppServeAppTasksResponse struct { + AppServeAppTasks []AppServeAppTaskResponse `json:"appServeAppTasks"` + Pagination PaginationResponse `json:"pagination"` } type GetAppServeAppTaskResponse struct { - AppServeAppTask AppServeAppTask `json:"appServeAppTask"` + AppServeApp AppServeAppResponse `json:"appServeApp"` + AppServeAppTask AppServeAppTaskResponse `json:"appServeAppTask"` + Stages []StageResponse `json:"stages"` } type StageResponse struct { diff --git a/pkg/domain/audit.go b/pkg/domain/audit.go new file mode 100644 index 00000000..12b3b7ba --- /dev/null +++ b/pkg/domain/audit.go @@ -0,0 +1,34 @@ +package domain + +import ( + "time" +) + +type AuditResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + OrganizationName string `json:"organizationName"` + Description string `json:"description"` + Group string `json:"group"` + Message string `json:"message"` + ClientIP string `json:"clientIP"` + UserId string `json:"userId"` + UserAccountId string `json:"userAccountId"` + UserName string `json:"userName"` + UserRoles string `json:"userRoles"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type CreateAuditRequest struct { +} +type CreateAuditResponse struct { +} + +type GetAuditResponse struct { + Audit AuditResponse `json:"audit"` +} +type GetAuditsResponse struct { + Audits []AuditResponse `json:"audits"` + Pagination PaginationResponse `json:"pagination"` +} diff --git a/pkg/domain/auth.go b/pkg/domain/auth.go index abab6f97..238a9fb4 100644 --- a/pkg/domain/auth.go +++ b/pkg/domain/auth.go @@ -6,27 +6,18 @@ type LoginRequest struct { OrganizationId string `json:"organizationId" validate:"required"` } -type PingTokenRequest struct { - Token string `json:"token" validate:"required"` - OrganizationId string `json:"organizationId" validate:"required"` -} - type LoginResponse struct { User struct { - AccountId string `json:"accountId"` - Name string `json:"name"` - Token string `json:"token"` - Role Role `json:"role"` - Department string `json:"department"` - Organization Organization `json:"organization"` - PasswordExpired bool `json:"passwordExpired"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Token string `json:"token"` + Roles []SimpleRoleResponse `json:"roles"` + Department string `json:"department"` + Organization OrganizationResponse `json:"organization"` + PasswordExpired bool `json:"passwordExpired"` } `json:"user"` } -type LogoutResponse struct { - SsoUrls map[string][]string `json:"ssoUrls"` -} - type VerifyIdentityForLostIdRequest struct { OrganizationId string `json:"organizationId" validate:"required"` Email string `json:"email" validate:"required,email"` diff --git a/pkg/domain/cloud-account.go b/pkg/domain/cloud-account.go index 5e4720c8..dfac5409 100644 --- a/pkg/domain/cloud-account.go +++ b/pkg/domain/cloud-account.go @@ -2,8 +2,6 @@ package domain import ( "time" - - "github.com/google/uuid" ) const CLOUD_ACCOUNT_INCLUSTER = "INCLUSTER" @@ -49,30 +47,6 @@ func (m CloudAccountStatus) FromString(s string) CloudAccountStatus { return CloudAccountStatus_PENDING } -// 내부 -type CloudAccount struct { - ID uuid.UUID - OrganizationId string - Name string - Description string - CloudService string - Resource string - Clusters int - AwsAccountId string - AccessKeyId string - SecretAccessKey string - SessionToken string - Status CloudAccountStatus - StatusDesc string - CreatedIAM bool - CreatorId uuid.UUID - Creator User - UpdatorId uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time -} - type ResourceQuotaAttr struct { Type string `json:"type"` Usage int `json:"usage"` @@ -157,3 +131,8 @@ type GetCloudAccountResourceQuotaResponse struct { Available bool `json:"available"` ResourceQuota ResourceQuota `json:"resourceQuota"` } + +type DeleteCloudAccountResponse struct { + ID string `json:"id"` + Name string `json:"name"` +} diff --git a/pkg/domain/cluster.go b/pkg/domain/cluster.go index 6c2ea884..891abfad 100644 --- a/pkg/domain/cluster.go +++ b/pkg/domain/cluster.go @@ -3,7 +3,6 @@ package domain import ( "time" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" ) @@ -83,34 +82,6 @@ func (m ClusterType) FromString(s string) ClusterType { return ClusterType_USER } -// model -type Cluster struct { - ID ClusterId - CloudService string - OrganizationId string - Name string - Description string - CloudAccountId uuid.UUID - CloudAccount CloudAccount - StackTemplateId uuid.UUID - StackTemplate StackTemplate - Status ClusterStatus - StatusDesc string - Conf ClusterConf - Favorited bool - CreatorId *uuid.UUID - Creator User - ClusterType ClusterType - UpdatorId *uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time - ByoClusterEndpointHost string - ByoClusterEndpointPort int - IsStack bool - Kubeconfig []byte -} - type ClusterConf struct { TksCpNode int TksCpNodeMax int @@ -169,25 +140,26 @@ func (m *ClusterConf) SetDefault() { } type CreateClusterRequest struct { - OrganizationId string `json:"organizationId" validate:"required"` - CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"` - StackTemplateId string `json:"stackTemplateId" validate:"required"` - Name string `json:"name" validate:"required,name"` - Description string `json:"description"` - CloudAccountId string `json:"cloudAccountId"` - ClusterType string `json:"clusterType"` - ByoClusterEndpointHost string `json:"byoClusterEndpointHost,omitempty"` - ByoClusterEndpointPort int `json:"byoClusterEndpointPort,omitempty"` - IsStack bool `json:"isStack,omitempty"` - TksCpNode int `json:"tksCpNode"` - TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` - TksCpNodeType string `json:"tksCpNodeType,omitempty"` - TksInfraNode int `json:"tksInfraNode"` - TksInfraNodeMax int `json:"tksInfraNodeMax,omitempty"` - TksInfraNodeType string `json:"tksInfraNodeType,omitempty"` - TksUserNode int `json:"tksUserNode"` - TksUserNodeMax int `json:"tksUserNodeMax,omitempty"` - TksUserNodeType string `json:"tksUserNodeType,omitempty"` + OrganizationId string `json:"organizationId" validate:"required"` + CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"` + StackTemplateId string `json:"stackTemplateId" validate:"required"` + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + CloudAccountId string `json:"cloudAccountId"` + ClusterType string `json:"clusterType"` + ByoClusterEndpointHost string `json:"byoClusterEndpointHost,omitempty"` + ByoClusterEndpointPort int `json:"byoClusterEndpointPort,omitempty"` + IsStack bool `json:"isStack,omitempty"` + PolicyIds []string `json:"policyIds,omitempty"` + TksCpNode int `json:"tksCpNode"` + TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` + TksCpNodeType string `json:"tksCpNodeType,omitempty"` + TksInfraNode int `json:"tksInfraNode"` + TksInfraNodeMax int `json:"tksInfraNodeMax,omitempty"` + TksInfraNodeType string `json:"tksInfraNodeType,omitempty"` + TksUserNode int `json:"tksUserNode"` + TksUserNodeMax int `json:"tksUserNodeMax,omitempty"` + TksUserNodeType string `json:"tksUserNodeType,omitempty"` } type ImportClusterRequest struct { @@ -239,6 +211,7 @@ type ClusterResponse struct { ByoClusterEndpointHost string `json:"byoClusterEndpointHost,omitempty"` ByoClusterEndpointInt int `json:"byoClusterEndpointPort,omitempty"` IsStack bool `json:"isStack,omitempty"` + Favorited bool `json:"favorited,omitempty"` } type SimpleClusterResponse struct { diff --git a/pkg/domain/dashboard.go b/pkg/domain/dashboard.go index c7699fb7..e6cf09c9 100644 --- a/pkg/domain/dashboard.go +++ b/pkg/domain/dashboard.go @@ -37,14 +37,6 @@ func (m ChartType) FromString(s string) ChartType { return ChartType_ERROR } -// [TODO] -func (m ChartType) All() (out []string) { - for _, v := range chartType { - out = append(out, v) - } - return -} - // 내부 type DashboardChart struct { ChartType ChartType @@ -72,6 +64,14 @@ type DashboardStack struct { UpdatedAt time.Time } +// [TODO] +func (m ChartType) All() (out []string) { + for _, v := range chartType { + out = append(out, v) + } + return +} + type Unit struct { Name string `json:"name"` Data []string `json:"data"` @@ -115,7 +115,10 @@ type GetDashboardChartResponse struct { } type DashboardResource struct { - Stack string `json:"stack"` + Stack struct { + Normal string `json:"normal"` + Abnormal string `json:"abnormal"` + } `json:"stack"` Cpu string `json:"cpu"` Memory string `json:"memory"` Storage string `json:"storage"` @@ -141,3 +144,113 @@ type DashboardStackResponse struct { type GetDashboardStacksResponse struct { Stacks []DashboardStackResponse `json:"stacks"` } + +type WidgetResponse struct { + Key string `json:"widgetKey"` + StartX int `json:"startX"` + StartY int `json:"startY"` + SizeX int `json:"sizeX"` + SizeY int `json:"sizeY"` +} + +type DashboardContents struct { + GroupName string `json:"groupName"` + SizeX int `json:"sizeX"` + SizeY int `json:"sizeY"` + Widgets []WidgetResponse `json:"widgets"` +} + +type CreateDashboardRequest struct { + DashboardKey string `json:"dashboardKey"` + Contents []DashboardContents `json:"contents"` +} + +type CreateDashboardResponse struct { + DashboardId string `json:"dashboardId"` +} + +type GetDashboardResponse struct { + GroupName string `json:"groupName"` + SizeX int `json:"sizeX"` + SizeY int `json:"sizeY"` + Widgets []WidgetResponse `json:"widgets"` +} + +type UpdateDashboardRequest struct { + DashboardContents +} + +type CommonDashboardResponse struct { + Result string `json:"result"` +} + +type DashboardPolicyStatus struct { + Normal int `json:"normal"` + Warning int `json:"warning"` + Error int `json:"error"` +} + +type GetDashboardPolicyStatusResponse struct { + PolicyStatus DashboardPolicyStatus `json:"statuses"` +} + +type DashboardPolicyUpdate struct { + PolicyTemplate int `json:"policyTemplate"` + Policy int `json:"policy"` +} + +type GetDashboardPolicyUpdateResponse struct { + PolicyUpdate DashboardPolicyUpdate `json:"updatedResources"` +} + +type GetDashboardPolicyEnforcementResponse struct { + BarChart + ChartData BarChartData `json:"chartData"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetDashboardPolicyViolationResponse struct { + BarChart + ChartData BarChartData `json:"chartData"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetDashboardPolicyViolationLogResponse struct { + // TODO implement me +} + +type GetDashboardPolicyStatisticsResponse struct { + PolicyStatisticsResponse +} + +type WorkloadData struct { + Name string `json:"name"` + Value int `json:"value"` +} +type GetDashboardWorkloadResponse struct { + Title string `json:"title"` + Data []WorkloadData `json:"data"` +} + +type GetDashboardPolicyViolationTop5Response struct { + GetDashboardPolicyViolationResponse +} + +type BarChart struct { + ChartType string `json:"chartType"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + Duration string `json:"duration"` + Interval string `json:"interval"` +} + +type BarChartData struct { + XAxis *Axis `json:"xAxis,omitempty"` + Series []UnitNumber `json:"series,omitempty"` +} + +type UnitNumber struct { + Name string `json:"name"` + Data []int `json:"data"` +} diff --git a/pkg/domain/endpoint.go b/pkg/domain/endpoint.go new file mode 100644 index 00000000..33acdc92 --- /dev/null +++ b/pkg/domain/endpoint.go @@ -0,0 +1,6 @@ +package domain + +type EndpointResponse struct { + Name string `json:"name"` + Group string `json:"group"` +} diff --git a/pkg/domain/mapper.go b/pkg/domain/mapper.go deleted file mode 100644 index e3fa80ac..00000000 --- a/pkg/domain/mapper.go +++ /dev/null @@ -1,140 +0,0 @@ -package domain - -import ( - "fmt" - "reflect" - - "github.com/google/uuid" - "github.com/openinfradev/tks-api/pkg/log" -) - -type ConverterMap map[compositeKey]func(interface{}) (interface{}, error) - -type compositeKey struct { - srcType reflect.Type - dstType reflect.Type -} - -func recursiveMap(src interface{}, dst interface{}, converterMap ConverterMap) error { - srcVal := reflect.ValueOf(src) - srcType := srcVal.Type() - - dstVal := reflect.ValueOf(dst) - if dstVal.Kind() != reflect.Ptr || dstVal.IsNil() { - return fmt.Errorf("dst must be a non-nil pointer") - } - dstElem := dstVal.Elem() - - for i := 0; i < srcVal.NumField(); i++ { - fieldName := srcType.Field(i).Name - srcField := srcVal.Field(i) - dstField := dstElem.FieldByName(fieldName) - - if dstField.IsValid() && dstField.CanSet() { - if dstField.Type() == srcField.Type() { - dstField.Set(srcField) - continue - } else if srcField.Type().Kind() == reflect.Struct && dstField.Type().Kind() == reflect.Struct { - if err := recursiveMap(srcField.Interface(), dstField.Addr().Interface(), converterMap); err != nil { - return err - } - } else { - converterKey := compositeKey{srcType: srcField.Type(), dstType: dstField.Type()} - if converter, ok := converterMap[converterKey]; ok { - if converted, err := converter(srcField.Interface()); err != nil { - return err - } else { - dstField.Set(reflect.ValueOf(converted)) - } - } else { - log.Debugf("no converter found for %s -> %s", srcField.Type(), dstField.Type()) - continue - } - } - - /* - else if srcField.Type().Kind() == reflect.Ptr && dstField.Type().Kind() == reflect.Ptr { - log.Info("AAA ", dstField.Type()) - ptr := reflect.New(dstField.Elem().Type()) - if err := recursiveMap(srcField.Elem().Interface(), ptr.Elem().Interface(), converterMap); err != nil { - return err - } - } - */ - - } - } - - return nil -} -func Map(src interface{}, dst interface{}) error { - return recursiveMap(src, dst, ConverterMap{ - {srcType: reflect.TypeOf((*uuid.UUID)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(uuid.UUID).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*uuid.UUID)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - val, _ := uuid.Parse(i.(string)) - return val, nil - }, - {srcType: reflect.TypeOf((*OrganizationStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(OrganizationStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*OrganizationStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return organizationStatusMap[i.(string)], nil - }, - {srcType: reflect.TypeOf((*Role)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(Role).Name, nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*Role)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return Role{Name: i.(string)}, nil - }, - {srcType: reflect.TypeOf((*ClusterStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(ClusterStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*ClusterStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(ClusterStatus).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*AppGroupStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(AppGroupStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*AppGroupStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(AppGroupStatus).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*AppGroupType)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(AppGroupType).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*AppGroupType)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(AppGroupType).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*StackStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(StackStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*StackStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(StackStatus).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*ChartType)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(ChartType).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*ChartType)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(ChartType).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*AlertActionStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(AlertActionStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*AlertActionStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(AlertActionStatus).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*ApplicationType)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(ApplicationType).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*ApplicationType)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(ApplicationType).FromString(i.(string)), nil - }, - {srcType: reflect.TypeOf((*CloudAccountStatus)(nil)).Elem(), dstType: reflect.TypeOf("")}: func(i interface{}) (interface{}, error) { - return i.(CloudAccountStatus).String(), nil - }, - {srcType: reflect.TypeOf(""), dstType: reflect.TypeOf((*CloudAccountStatus)(nil)).Elem()}: func(i interface{}) (interface{}, error) { - return new(CloudAccountStatus).FromString(i.(string)), nil - }, - }) -} diff --git a/pkg/domain/mapper_test.go b/pkg/domain/mapper_test.go deleted file mode 100644 index f1d57270..00000000 --- a/pkg/domain/mapper_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package domain - -import ( - "fmt" - "testing" - "time" -) - -// test case -func TestConvert(t *testing.T) { - type args struct { - src interface{} - dst interface{} - } - tests := []struct { - name string - args args - wantErr bool - }{ - { - name: "test case: CreateOrganizationRequest->Organization", - args: args{ - src: CreateOrganizationRequest{ - Name: "test", - Description: "test", - Phone: "test", - }, - dst: &Organization{}, - }, - wantErr: false, - }, - { - name: "test case Organization->CreateOrganizationResponse", - args: args{ - src: Organization{ - ID: "", - Name: "test", - Description: "test", - Phone: "test", - StatusDesc: "good", - Creator: "", - CreatedAt: time.Time{}, - UpdatedAt: time.Time{}, - }, - dst: &CreateOrganizationResponse{}, - }, - wantErr: false, - }, - { - name: "test case Organization->GetOrganizationResponse", - args: args{ - src: Organization{ - ID: "", - Name: "test", - Description: "test", - Phone: "test", - Status: OrganizationStatus_CREATE, - StatusDesc: "good", - Creator: "", - CreatedAt: time.Time{}, - UpdatedAt: time.Time{}, - }, - dst: &(&GetOrganizationResponse{}).Organization, - }, - wantErr: false, - }, - { - name: "test case CreateUserRequest->User", - args: args{ - src: CreateUserRequest{ - AccountId: "testAccount", - Password: "testPassword", - Name: "testName", - Email: "testEmail", - Department: "testDepartment", - Role: "testRole", - Description: "testDescription", - }, - dst: &User{}, - }, - wantErr: false, - }, - { - name: "test case User->GetUserResponse", - args: args{ - src: User{ - ID: "", - AccountId: "testAccount", - Password: "testPassword", - Name: "testName", - Token: "testToken", - Role: Role{}, - Organization: Organization{}, - Creator: "", - CreatedAt: time.Time{}, - UpdatedAt: time.Time{}, - Email: "", - Department: "", - Description: "", - }, - dst: &GetUserResponse{}, - }, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := Map(tt.args.src, tt.args.dst); (err != nil) != tt.wantErr { - t.Errorf("Map() error = %v, wantErr %v", err, tt.wantErr) - } else { - fmt.Printf("Input: %+v\n", tt.args.src) - fmt.Printf("Output: %+v\n\n", tt.args.dst) - } - }) - } -} diff --git a/pkg/domain/organization.go b/pkg/domain/organization.go index 6e3706da..46479c4f 100644 --- a/pkg/domain/organization.go +++ b/pkg/domain/organization.go @@ -48,24 +48,34 @@ func (m OrganizationStatus) FromString(s string) OrganizationStatus { return OrganizationStatus_ERROR } -type Organization = struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Phone string `json:"phone"` - PrimaryClusterId string `json:"primaryClusterId"` - Status OrganizationStatus `json:"status"` - StatusDesc string `json:"statusDesc"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` +type OrganizationResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + PrimaryClusterId string `json:"primaryClusterId"` + Status string `json:"status"` + StatusDesc string `json:"statusDesc"` + StackTemplates []SimpleStackTemplateResponse `json:"stackTemplates"` + PolicyTemplates []SimplePolicyTemplateResponse `json:"policyTemplates"` + SystemNotificationTemplates []SimpleSystemNotificationTemplateResponse `json:"systemNotificationTemplates"` + Admin SimpleUserResponse `json:"admin"` + ClusterCount int `json:"stackCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SimpleOrganizationResponse = struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` } type CreateOrganizationRequest struct { - Name string `json:"name" validate:"required,name"` - Description string `json:"description" validate:"omitempty,min=0,max=100"` - Phone string `json:"phone"` - Email string `json:"Email" validate:"required,email"` + Name string `json:"name" validate:"required,name"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` + AdminAccountId string `json:"adminAccountId" validate:"required"` + AdminName string `json:"adminName" validate:"name"` + AdminEmail string `json:"adminEmail" validate:"required,email"` } type CreateOrganizationResponse struct { @@ -73,48 +83,33 @@ type CreateOrganizationResponse struct { } type GetOrganizationResponse struct { - Organization struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Phone string `json:"phone"` - PrimaryClusterId string `json:"primaryClusterId"` - Status string `json:"status"` - StatusDesc string `json:"statusDesc"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - } `json:"organization"` + Organization OrganizationResponse `json:"organization"` } type ListOrganizationResponse struct { - Organizations []ListOrganizationBody `json:"organizations"` + Organizations []OrganizationResponse `json:"organizations"` Pagination PaginationResponse `json:"pagination"` } -type ListOrganizationBody struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Phone string `json:"phone"` - PrimaryClusterId string `json:"primaryClusterId"` - Status string `json:"status"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} type UpdateOrganizationRequest struct { - PrimaryClusterId string `json:"primaryClusterId"` - Name string `json:"name" validate:"required,min=1,max=30"` - Description string `json:"description" validate:"omitempty,min=0,max=100"` - Phone string `json:"phone"` + Name string `json:"name" validate:"required,min=1,max=30"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` + AdminAccountId string `json:"adminAccountId"` } type UpdateOrganizationResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Phone string `json:"phone"` + ID string `json:"id"` } type UpdatePrimaryClusterRequest struct { PrimaryClusterId string `json:"primaryClusterId"` } + +type UpdateOrganizationTemplatesRequest struct { + StackTemplateIds *[]string `json:"stackTemplateIds,omitempty"` + PolicyTemplateIds *[]string `json:"policyTemplateIds,omitempty"` + SystemNotificationTemplateIds *[]string `json:"systemNotificationTemplateIds,omitempty"` +} + +type DeleteOrganizationResponse struct { + ID string `json:"id"` +} diff --git a/pkg/domain/pagination.go b/pkg/domain/pagination.go index 33efaef7..21f9c1ba 100644 --- a/pkg/domain/pagination.go +++ b/pkg/domain/pagination.go @@ -11,6 +11,8 @@ type PaginationResponse struct { } type FilterResponse struct { - Column string `json:"column"` - Values []string `json:"values"` + Column string `json:"column"` + Values []string `json:"values"` + Operator string `json:"operator"` + Relation string `json:"releation"` } diff --git a/pkg/domain/permission.go b/pkg/domain/permission.go new file mode 100644 index 00000000..3ae4805c --- /dev/null +++ b/pkg/domain/permission.go @@ -0,0 +1,78 @@ +package domain + +import ( + "github.com/google/uuid" +) + +type GetPermissionTemplatesResponse struct { + //Permissions *PermissionTemplateResponse `json:"permissions"` + Permissions []*TemplateResponse `json:"permissions"` +} + +//type PermissionTemplateResponse struct { +// Dashboard *TemplateResponse `json:"dashboard,omitempty"` +// Stack *TemplateResponse `json:"stack,omitempty"` +// Policy *TemplateResponse `json:"policy,omitempty"` +// ProjectManagement *TemplateResponse `json:"project_management,omitempty"` +// Notification *TemplateResponse `json:"notification,omitempty"` +// Configuration *TemplateResponse `json:"configuration,omitempty"` +//} + +type TemplateResponse struct { + Name string `json:"name"` + Key string `json:"key"` + IsAllowed *bool `json:"isAllowed,omitempty"` + Children []*TemplateResponse `json:"children,omitempty"` +} + +type GetPermissionsByRoleIdResponse struct { + //Permissions *PermissionSetResponse `json:"permissions"` + Permissions []*PermissionResponse `json:"permissions"` +} + +//type PermissionSetResponse struct { +// Dashboard *PermissionResponse `json:"dashboard,omitempty"` +// Stack *PermissionResponse `json:"stack,omitempty"` +// Policy *PermissionResponse `json:"policy,omitempty"` +// ProjectManagement *PermissionResponse `json:"project_management,omitempty"` +// Notification *PermissionResponse `json:"notification,omitempty"` +// Configuration *PermissionResponse `json:"configuration,omitempty"` +//} + +type PermissionResponse struct { + ID *uuid.UUID `json:"ID,omitempty"` + Name string `json:"name"` + Key string `json:"key"` + IsAllowed *bool `json:"isAllowed,omitempty"` + Endpoints []*EndpointResponse `json:"endpoints,omitempty"` + Children []*PermissionResponse `json:"children,omitempty"` +} + +type UpdatePermissionUpdateRequest struct { + ID uuid.UUID `json:"ID" validate:"required"` + IsAllowed *bool `json:"isAllowed" validate:"required,oneof=true false"` +} + +type UpdatePermissionsByRoleIdRequest struct { + Permissions []*UpdatePermissionUpdateRequest `json:"permissions"` +} + +type GetUsersPermissionsResponse struct { + //Permissions *MergedPermissionSetResponse `json:"permissions"` + Permissions []*MergePermissionResponse `json:"permissions"` +} + +//type MergedPermissionSetResponse struct { +// Dashboard *MergePermissionResponse `json:"dashboard,omitempty"` +// Stack *MergePermissionResponse `json:"stack,omitempty"` +// Policy *MergePermissionResponse `json:"policy,omitempty"` +// ProjectManagement *MergePermissionResponse `json:"project_management,omitempty"` +// Notification *MergePermissionResponse `json:"notification,omitempty"` +// Configuration *MergePermissionResponse `json:"configuration,omitempty"` +//} + +type MergePermissionResponse struct { + Key string `json:"key"` + IsAllowed *bool `json:"isAllowed,omitempty"` + Children []*MergePermissionResponse `json:"children,omitempty"` +} diff --git a/pkg/domain/policy-notification.go b/pkg/domain/policy-notification.go new file mode 100644 index 00000000..f57b8de7 --- /dev/null +++ b/pkg/domain/policy-notification.go @@ -0,0 +1,32 @@ +package domain + +import ( + "time" +) + +type PolicyNotificationResponse struct { + ID string `json:"id"` + PolicyName string `json:"policyName"` + OrganizationId string `json:"organizationId"` + Severity string `json:"severity"` + MessageTitle string `json:"messageTitle"` + MessageContent string `json:"messageContent"` + MessageActionProposal string `json:"messageActionProposal"` + Cluster SimpleClusterResponse `json:"cluster"` + GrafanaUrl string `json:"grafanaUrl"` + Status string `json:"status"` + RawData string `json:"rawData"` + NotificationType string `json:"notificationType"` + Read bool `json:"read"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetPolicyNotificationsResponse struct { + PolicyNotifications []PolicyNotificationResponse `json:"policyNotifications"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetPolicyNotificationResponse struct { + PolicyNotification PolicyNotificationResponse `json:"policyNotification"` +} diff --git a/pkg/domain/policy-template.go b/pkg/domain/policy-template.go new file mode 100644 index 00000000..835a1225 --- /dev/null +++ b/pkg/domain/policy-template.go @@ -0,0 +1,215 @@ +package domain + +import ( + "time" +) + +type PolicyTemplateResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Type string `json:"type" enums:"tks,organization" example:"tks"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + TemplateName string `json:"templateName" example:"필수 Label 검사"` + Kind string `json:"kind" example:"K8sRequiredLabels"` + Severity string `json:"severity" enums:"low,medium,high" example:"medium"` + Deprecated bool `json:"deprecated" example:"false"` + Version string `json:"version,omitempty" example:"v1.0.1"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` + ParametersSchema []*ParameterDef `json:"parametersSchema,omitempty"` + Rego string `json:"rego" example:"rego 코드"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` +} + +type PolicyTemplateTwoVersionResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Type string `json:"type" enums:"tks,organization" example:"tks"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + + TemplateName string `json:"templateName" example:"필수 Label 검사"` + Kind string `json:"kind" example:"K8sRequiredLabels"` + Severity string `json:"severity" enums:"low,medium,high" example:"medium"` + Deprecated bool `json:"deprecated" example:"false"` + CurrentVersion string `json:"currentVersion" example:"v1.0.1"` + LatestVersion string `json:"latestVersion" example:"v1.0.1"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` + ParametersSchema []*ParameterDef `json:"parametersSchema,omitempty"` + Rego string `json:"rego" example:"rego 코드"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` +} + +type SimplePolicyTemplateResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Type string `json:"type" enums:"tks,organization" example:"tks"` + Name string `json:"templateName" example:"필수 Label 검사"` + Version string `json:"version,omitempty" example:"v1.0.1"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` +} + +type CreatePolicyTemplateRequest struct { + TemplateName string `json:"templateName" validate:"required,name" example:"필수 Label 검사"` + Kind string `json:"kind" example:"K8sRequiredLabels" validate:"required,pascalcase"` + Severity string `json:"severity" validate:"required,oneof=low medium high" enums:"low,medium,high" example:"medium"` + Deprecated bool `json:"deprecated" example:"false"` + Description string `json:"description,omitempty" example:"이 정책은 ..."` + ParametersSchema []*ParameterDef `json:"parametersSchema,omitempty"` + // "type: object\nproperties: message:\n type: string\n labels:\n type: array\n items:\n type: object\n properties:\n key:\n type: string\n allowedRegex:\n type: string" + + Rego string `json:"rego" example:"rego 코드" validate:"required"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` + + PermittedOrganizationIds []string `json:"permittedOrganizationIds"` +} + +type CreatePolicyTemplateReponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` +} + +type UpdatePolicyTemplateRequest struct { + TemplateName *string `json:"templateName,omitempty" validate:"required,name" example:"필수 Label 검사"` + Description *string `json:"description,omitempty"` + Severity *string `json:"severity,omitempty" validate:"omitempty,oneof=low medium high" enums:"low,medium,high" example:"medium"` + Deprecated *bool `json:"deprecated,omitempty" example:"false"` + PermittedOrganizationIds *[]string `json:"permittedOrganizationIds,omitempty"` +} + +type GetPolicyTemplateDeployResponse struct { + DeployVersion map[string]string `json:"deployVersion"` +} + +type ListPolicyTemplateVersionsResponse struct { + Versions []string `json:"versions" example:"v1.1.0,v1.0.1,v1.0.0"` +} + +type GetPolicyTemplateVersionResponse struct { + PolicyTemplate PolicyTemplateResponse `json:"policyTemplate"` +} + +type CreatePolicyTemplateVersionRequest struct { + VersionUpType string `json:"versionUpType" validate:"required,oneof=major minor patch" enums:"major,minor,patch" example:"minor"` + CurrentVersion string `json:"currentVersion" validate:"required,version" example:"v1.0.0"` + ExpectedVersion string `json:"expectedVersion" validate:"required,version" example:"v1.1.0"` + + ParametersSchema []*ParameterDef `json:"parametersSchema,omitempty"` + // "type: object\nproperties: message:\n type: string\n labels:\n type: array\n items:\n type: object\n properties:\n key:\n type: string\n allowedRegex:\n type: string" + + Rego string `json:"rego" example:"rego 코드" validate:"required"` + Libs []string `json:"libs" example:"rego 코드"` + SyncKinds *[]string `json:"syncKinds,omitempty" example:"Ingress"` + SyncJson *string `json:"SyncJson,omitempty" example:"[[]]"` +} + +type CreatePolicyTemplateVersionResponse struct { + Version string `json:"version" example:"v1.1.1"` +} + +type GetPolicyTemplateResponse struct { + PolicyTemplate PolicyTemplateResponse `json:"policyTemplate"` +} + +type ListPolicyTemplateResponse struct { + PolicyTemplates []PolicyTemplateTwoVersionResponse `json:"policyTemplates"` + Pagination PaginationResponse `json:"pagination"` +} + +type PolicyTemplateStatistics struct { + OrganizationId string `json:"organizationId"` + OrganizationName string `json:"organizationName"` + UsageCount int `json:"usageCount"` +} + +type ListPolicyTemplateStatisticsResponse struct { + PolicyTemplateStatistics []PolicyTemplateStatistics `json:"policyTemplateStatistics"` +} + +type ExistsPolicyTemplateNameResponse struct { + Existed bool `json:"existed"` +} + +type ExistsPolicyTemplateKindResponse struct { + Existed bool `json:"existed"` +} + +type ParameterDef struct { + Key string `json:"key" exmaples:"repos"` + Type string `json:"type" examples:"string[]"` + DefaultValue string `json:"defaultValue"` + Children []*ParameterDef `json:"children"` + IsArray bool `json:"isArray" examples:"true"` + IsNew bool `json:"isNew,omitempty" examples:"true"` +} + +func (pd *ParameterDef) MarkNewRecursive() { + pd.IsNew = true + + for _, child := range pd.Children { + child.MarkNewRecursive() + } +} + +func (pd *ParameterDef) GetChildrenByName(name string) *ParameterDef { + for _, child := range pd.Children { + if child.Key == name { + return child + } + } + + return nil +} + +type RegoCompileRequest struct { + Rego string `json:"rego" example:"Rego 코드" validate:"required"` + Libs []string `json:"libs,omitempty"` +} + +type RegoCompieError struct { + Status int `json:"status" example:"400"` + Code string `json:"code" example:"P_INVALID_REGO_SYNTAX"` + Message string `json:"message" example:"Invalid rego syntax"` + Text string `json:"text" example:"Rego 문법 에러입니다. 라인:2 컬럼:1 에러메시지: var testnum is not safe"` +} + +type RegoCompileResponse struct { + ParametersSchema []*ParameterDef `json:"parametersSchema"` + Errors []RegoCompieError `json:"errors"` +} + +type ExtractParametersRequest struct { + Rego string `json:"rego" example:"Rego 코드" validate:"required"` + Libs []string `json:"libs,omitempty"` +} + +type ExtractParametersResponse struct { + ParametersSchema []*ParameterDef `json:"parametersSchema"` + Errors []RegoCompieError `json:"errors"` +} + +type AddPoliciesForStackRequest struct { + PolicyIds []string `json:"policyIds"` +} + +type UpdatePoliciesForStackRequest struct { + PolicyIds []string `json:"policyIds"` +} + +type DeletePoliciesForStackRequest struct { + PolicyIds []string `json:"policyIds"` +} + +// opa gatekeeper에서 복사 +type CompactGVKEquivalenceSet struct { + Groups []string `json:"groups"` + Versions []string `json:"versions"` + Kinds []string `json:"kinds"` +} diff --git a/pkg/domain/policy.go b/pkg/domain/policy.go new file mode 100644 index 00000000..6550f948 --- /dev/null +++ b/pkg/domain/policy.go @@ -0,0 +1,218 @@ +package domain + +import ( + "encoding/json" + "time" +) + +type Kinds struct { + APIGroups []string `json:"apiGroups,omitempty"` + Kinds []string `json:"kinds,omitempty"` +} + +type Match struct { + Namespaces []string `json:"namespaces,omitempty" validate:"matchnamespace"` + ExcludedNamespaces []string `json:"excludedNamespaces,omitempty" validate:"matchnamespace"` + Kinds []Kinds `json:"kinds,omitempty" validate:"matchkinds"` +} + +func (m *Match) JSON() string { + jsonBytes, err := json.Marshal(m) + + if err != nil { + return "" + } + + return string(jsonBytes) +} + +type PolicyResponse struct { + ID string `json:"id" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + Creator SimpleUserResponse `json:"creator,omitempty"` + Updator SimpleUserResponse `json:"updator,omitempty"` + CreatedAt time.Time `json:"createdAt" format:"date-time"` + UpdatedAt time.Time `json:"updatedAt" format:"date-time"` + + // TargetClusterIds []string `json:"targetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + TargetClusters []SimpleClusterResponse `json:"targetClusters"` + Mandatory bool `json:"mandatory"` + + PolicyName string `json:"policyName" example:"label 정책"` + PolicyResourceName string `json:"policyResourceName,omitempty" example:"labelpolicy"` + Description string `json:"description"` + TemplateId string `json:"templateId" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + TemplateName string `json:"templateName" example:"필수 Label 검사"` + EnforcementAction string `json:"enforcementAction" enum:"warn,deny,dryrun" example:"deny"` + Parameters string `json:"parameters" example:"{\"key\":\"value\"}"` + FilledParameters []*ParameterDef `json:"filledParameters"` + Match *Match `json:"match,omitempty"` + MatchYaml *string `json:"matchYaml,omitempty" example:"namespaces:\r\n- testns1"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type CreatePolicyRequest struct { + TargetClusterIds []string `json:"targetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + Mandatory bool `json:"mandatory"` + + PolicyName string `json:"policyName" validate:"required,name" example:"label 정책"` + PolicyResourceName string `json:"policyResourceName,omitempty" validate:"resourcename" example:"labelpolicy"` + Description string `json:"description"` + TemplateId string `json:"templateId" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + EnforcementAction string `json:"enforcementAction" validate:"required,oneof=deny dryrun warn" enum:"warn,deny,dryrun" example:"deny"` + Parameters string `json:"parameters" example:"{\"key\":\"value\"}"` + Match *Match `json:"match,omitempty"` + MatchYaml *string `json:"matchYaml,omitempty" example:"namespaces:\r\n- testns1"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type CreatePolicyResponse struct { + ID string `json:"id"` +} + +type UpdatePolicyRequest struct { + TargetClusterIds *[]string `json:"targetClusterIds,omitempty" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` + Mandatory *bool `json:"mandatory,omitempty"` + + PolicyName *string `json:"policyName,omitempty" validate:"name" example:"label 정책"` + Description *string `json:"description,omitempty"` + TemplateId *string `json:"templateId,omitempty" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + EnforcementAction *string `json:"enforcementAction" validate:"omitempty,oneof=deny dryrun warn" enum:"warn,deny,dryrun" example:"deny"` + Parameters *string `json:"parameters,omitempty" example:"{\"labels\":{\"key\":\"owner\",\"allowedRegex\":\"test*\"}"` + Match *Match `json:"match,omitempty"` + MatchYaml *string `json:"matchYaml,omitempty"` + //Tags []string `json:"tags,omitempty" example:"k8s,label"` +} + +type UpdatePolicyClustersRequest struct { + CurrentTargetClusterIds []string `json:"currentTargetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90"` + NewTargetClusterIds []string `json:"newTargetClusterIds" example:"83bf8081-f0c5-4b31-826d-23f6f366ec90,83bf8081-f0c5-4b31-826d-23f6f366ec90"` +} + +type GetPolicyResponse struct { + Policy PolicyResponse `json:"policy"` +} + +type ListPolicyResponse struct { + Policies []PolicyResponse `json:"policies"` + Pagination PaginationResponse `json:"pagination"` +} + +type MandatoryPolicyInfo struct { + PolicyName string `json:"policyName" example:"org 레이블 요구"` + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + Description string `json:"description" example:"org 레이블 설정 여부 검사"` + Mandatory bool `json:"mandatory"` +} + +type MandatoryTemplateInfo struct { + TemplateName string `json:"templateName" example:"레이블 요구"` + TemplateId string `json:"templateId" example:"708d1e5b-4e6f-40e9-87a3-329e2fd051a5"` + Description string `json:"description" example:"파라미터로 설정된 레이블 검사"` + Mandatory bool `json:"mandatory"` + Policies []MandatoryPolicyInfo `json:"policies"` +} + +type GetMandatoryPoliciesResponse struct { + Templates []MandatoryTemplateInfo `json:"templates"` +} + +type MandatoryPolicyPatchInfo struct { + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + Mandatory bool `json:"mandatory"` +} + +type SetMandatoryPoliciesRequest struct { + Policies []MandatoryPolicyPatchInfo `json:"policies"` +} + +type StackPolicyStatusResponse struct { + PolicyName string `json:"policyName" example:"org 레이블 요구"` + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + PolicyDescription string `json:"policyDescription" example:"org 레이블 설정 여부 검사"` + PolicyMandatory bool `json:"policyMandatory"` + TemplateName string `json:"templateName" example:"레이블 요구"` + TemplateId string `json:"templateId" example:"708d1e5b-4e6f-40e9-87a3-329e2fd051a5"` + TemplateDescription string `json:"templateDescription" example:"파라미터로 설정된 레이블 검사"` + TemplateCurrentVersion string `json:"templateCurrentVersion" example:"v1.0.1"` + TemplateLatestVersion string `json:"templateLatestVersion" example:"v1.0.3"` +} + +type ListStackPolicyStatusResponse struct { + Polices []StackPolicyStatusResponse `json:"polices"` +} + +type GetStackPolicyTemplateStatusResponse struct { + TemplateName string `json:"templateName" example:"레이블 요구"` + TemplateId string `json:"templateId" example:"708d1e5b-4e6f-40e9-87a3-329e2fd051a5"` + TemplateDescription string `json:"templateDescription" example:"파라미터로 설정된 레이블 검사"` + TemplateMandatory bool `json:"templateMandatory"` + TemplateCurrentVersion string `json:"templateCurrentVersion" example:"v1.0.1"` + TemplateLatestVersion string `json:"templateLatestVersion" example:"v1.0.3"` + TemplateLatestVersionReleaseDate time.Time `json:"templateLatestVersionReleaseDate" format:"date-time"` + UpdatedPolicyParameters []UpdatedPolicyTemplateParameter `json:"updatedPolicyParameters"` + AffectedPolicies []PolicyStatus `json:"affectedPolicies"` +} + +type PolicyStatus struct { + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + PolicyName string `json:"policyName"` + PolicyParameters []PolicyParameter `json:"policyPolicyParameters"` +} + +type UpdatedPolicyTemplateParameter struct { + Name string `json:"name"` + Type string `json:"type"` + Value string `json:"value"` +} + +type PolicyParameter struct { + Name string `json:"name"` + Type string `json:"type"` + Value string `json:"value"` + Updatable bool `json:"updatable"` +} + +type UpdatedPolicyParameters struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type PolicyUpdate struct { + PolicyId string `json:"policyId" example:"0091fe9b-e44b-423d-9562-ac2b73089593"` + UpdatedPolicyParameters []UpdatedPolicyParameters `json:"updatedPolicyParameters"` +} + +type UpdateStackPolicyTemplateStatusRequest struct { + TemplateCurrentVersion string `json:"templateCurrentVersion" validate:"version" example:"v1.0.1"` + TemplateTargetVerson string `json:"templateTargetVerson" validate:"version" example:"v1.0.3"` + // PolicyUpdate []PolicyUpdate `json:"policyUpdate"` +} + +type TemplateCount struct { + TksTemplate int64 `json:"tksTemplate"` + OrganizationTemplate int64 `json:"organizationTemplate"` + Total int64 `json:"total"` +} + +type PolicyCount struct { + Deny int64 `json:"deny"` + Warn int64 `json:"warn"` + Dryrun int64 `json:"dryrun"` + FromTksTemplate int64 `json:"fromTksTemplate"` + FromOrgTemplate int64 `json:"fromOrgTemplate"` + Total int64 `json:"total"` +} + +type PolicyStatisticsResponse struct { + Template TemplateCount `json:"templateCount"` + Policy PolicyCount `json:"policyCount"` +} + +type StackPolicyStatistics struct { + TotalTemplateCount int `json:"totalTemplateCount"` + UptodateTemplateCount int `json:"uptodateTemplateCount"` + OutofdateTemplateCount int `json:"outofdateTemplateCount"` + TotalPolicyCount int `json:"totalPolicyCount"` + UptodatePolicyCount int `json:"uptodatePolicyCount"` + OutofdatePolicyCount int `json:"outofdatePolicyCount"` +} diff --git a/pkg/domain/project.go b/pkg/domain/project.go new file mode 100644 index 00000000..dca4cc91 --- /dev/null +++ b/pkg/domain/project.go @@ -0,0 +1,203 @@ +package domain + +import "time" + +type ProjectResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + IsMyProject string `json:"isMyProject"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRoleName string `json:"projectRoleName"` + ProjectLeaderId string `json:"projectLeaderId"` + ProjectLeaderName string `json:"projectLeaderName"` + NamespaceCount int `json:"namespaceCount"` + AppCount int `json:"appCount"` + MemberCount int `json:"memberCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type GetProjectsResponse struct { + Projects []ProjectResponse `json:"projects"` + Pagination PaginationResponse `json:"pagination"` +} + +type ProjectDetailResponse struct { + ID string `json:"id"` + OrganizationId string `json:"organizationId"` + Name string `json:"name"` + Description string `json:"description"` + ProjectLeaderId string `json:"projectLeaderId"` + ProjectLeaderName string `json:"projectLeaderName"` + ProjectLeaderAccountId string `json:"projectLeaderAccountId"` + ProjectLeaderDepartment string `json:"projectLeaderDepartment"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRoleName string `json:"projectRoleName"` + //AppCount int `json:"appCount"` + //NamespaceCount int `json:"namespaceCount"` + //MemberCount int `json:"memberCount"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type GetProjectResponse struct { + Project *ProjectDetailResponse `json:"project"` +} + +type CreateProjectRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description"` + ProjectLeaderId string `json:"projectLeaderId"` +} + +type CreateProjectResponse struct { + ProjectId string `json:"projectId"` +} + +type UpdateProjectRequest struct { + CreateProjectRequest +} + +type ProjectRoleResponse struct { + ID string `json:"id"` + Name string `json:"name"` // project-leader, project-member, project-viewer + Description string `json:"description,omitempty"` + CreatedAt time.Time `json:"createdAt" ` + UpdatedAt *time.Time `json:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt"` +} + +type GetProjectRoleResponse struct { + ProjectRole ProjectRoleResponse `json:"projectRole"` +} + +type GetProjectRolesResponse struct { + ProjectRoles []ProjectRoleResponse `json:"projectRoles"` +} + +type ProjectMemberRequest struct { + ProjectUserId string `json:"projectUserId" validate:"required"` + ProjectRoleId string `json:"projectRoleId" validate:"required"` +} +type AddProjectMemberRequest struct { + ProjectMemberRequests []ProjectMemberRequest `json:"projectMembers"` +} + +type ProjectMemberResponse struct { + ID string `json:"id"` + ProjectId string `json:"projectId"` + ProjectUserId string `json:"projectUserId"` + ProjectUserName string `json:"projectUserName"` + ProjectUserAccountId string `json:"projectUserAccountId"` + ProjectUserEmail string `json:"projectUserEmail"` + ProjectUserDepartment string `json:"projectUserDepartment"` + ProjectRoleId string `json:"projectRoleId"` + ProjectRoleName string `json:"projectRoleName"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type GetProjectMemberResponse struct { + ProjectMember *ProjectMemberResponse `json:"projectMember"` +} + +type GetProjectMembersResponse struct { + ProjectMembers []ProjectMemberResponse `json:"projectMembers"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetProjectMemberCountResponse struct { + ProjectMemberAllCount int `json:"projectMemberAllCount"` + ProjectLeaderCount int `json:"projectLeaderCount"` + ProjectMemberCount int `json:"projectMemberCount"` + ProjectViewerCount int `json:"projectViewerCount"` +} + +type RemoveProjectMemberRequest struct { + ProjectMember []struct { + ProjectMemberId string `json:"projectMemberId"` + } `json:"projectMembers"` +} + +type CommonProjectResponse struct { + Result string `json:"result"` +} + +type UpdateProjectMemberRoleRequest struct { + ProjectRoleId string `json:"projectRoleId"` +} + +type UpdateProjectMembersRoleRequest struct { + ProjectMemberRoleRequests []struct { + ProjectMemberId string `json:"projectMemberId"` + ProjectRoleId string `json:"projectRoleId"` + } `json:"projectMembers"` +} + +type CreateProjectNamespaceRequest struct { + StackId string `json:"stackId"` + Namespace string `json:"namespace"` + Description string `json:"description"` +} + +type ProjectNamespaceResponse struct { + StackId string `json:"stackId"` + Namespace string `json:"namespace"` + StackName string `json:"stackName"` + ProjectId string `json:"projectId"` + Description string `json:"description"` + Status string `json:"status"` + AppCount int `json:"appCount"` + GrafanaUrl string `json:"grafanaUrl"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type GetProjectNamespacesResponse struct { + ProjectNamespaces []ProjectNamespaceResponse `json:"projectNamespaces"` +} + +type GetProjectNamespaceResponse struct { + ProjectNamespace *ProjectNamespaceResponse `json:"projectNamespace"` +} + +type UpdateProjectNamespaceRequest struct { + Description string `json:"description"` +} + +type GetProjectKubeconfigResponse struct { + Kubeconfig string `json:"kubeconfig"` +} + +type ProjectNamespaceK8sResources struct { + Pods int `json:"pods"` + Deployments int `json:"deployments"` + Statefulsets int `json:"statefulsets"` + Daemonsets int `json:"daemonsets"` + Jobs int `json:"jobs"` + Cronjobs int `json:"cronjobs"` + PVCs int `json:"pvcs"` + Services int `json:"services"` + Ingresses int `json:"ingresses"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetProjectNamespaceK8sResourcesResponse struct { + K8sResources ProjectNamespaceK8sResources `json:"k8sResources"` +} + +type ProjectNamespaceResourcesUsage struct { + Cpu string `json:"cpu"` + Memory string `json:"memory"` + Storage string `json:"storage"` +} + +type GetProjectNamespaceResourcesUsageResponse struct { + ResourcesUsage ProjectNamespaceResourcesUsage `json:"resourcesUsage"` +} + +type GetProjectNamespaceKubeConfigResponse struct { + KubeConfig string `json:"kubeConfig"` +} diff --git a/pkg/domain/role.go b/pkg/domain/role.go new file mode 100644 index 00000000..05449c73 --- /dev/null +++ b/pkg/domain/role.go @@ -0,0 +1,78 @@ +package domain + +import ( + "time" + + "github.com/google/uuid" +) + +type RoleType string + +const ( + RoleTypeDefault RoleType = "default" + RoleTypeTks RoleType = "tks" + RoleTypeProject RoleType = "project" +) + +type RoleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + Organization OrganizationResponse `json:"organization"` + Type string `json:"type"` + Description string `json:"description"` + Creator uuid.UUID `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SimpleRoleResponse = struct { + ID string `json:"id"` + Name string `json:"name"` +} + +type CreateTksRoleRequest struct { + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} + +type CreateTksRoleResponse struct { + ID string `json:"id"` +} + +type GetTksRoleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + OrganizationID string `json:"organizationId"` + Description string `json:"description"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ListTksRoleResponse struct { + Roles []GetTksRoleResponse `json:"roles"` + Pagination PaginationResponse `json:"pagination"` +} + +type UpdateTksRoleRequest struct { + Name string `json:"name" validate:"omitempty,min=0,max=100"` + Description string `json:"description" validate:"omitempty,min=0,max=100"` +} + +type CheckRoleNameResponse struct { + IsExist bool `json:"isExist"` +} + +type AppendUsersToRoleRequest struct { + Users []uuid.UUID `json:"users" validate:"required"` +} + +type RemoveUsersFromRoleRequest struct { + Users []uuid.UUID `json:"users" validate:"required"` +} + +type GetUsersInRoleIdResponse struct { + Users []SimpleUserResponse `json:"users"` + Pagination PaginationResponse `json:"pagination"` +} diff --git a/pkg/domain/stack-template.go b/pkg/domain/stack-template.go index 0d3e6911..a2cdaca4 100644 --- a/pkg/domain/stack-template.go +++ b/pkg/domain/stack-template.go @@ -2,35 +2,11 @@ package domain import ( "time" - - "github.com/google/uuid" ) const STACK_TEMPLATE_TYPE_STANDARD = "STANDARD" const STACK_TEMPLATE_TYPE_MSA = "MSA" -// 내부 -type StackTemplate struct { - ID uuid.UUID - OrganizationId string - Name string - Description string - Template string - TemplateType string - CloudService string - Version string - Platform string - KubeVersion string - KubeType string - Services []byte - CreatorId uuid.UUID - Creator User - UpdatorId uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time -} - type StackTemplateServiceApplicationResponse struct { Name string `json:"name"` Description string `json:"description"` @@ -43,32 +19,37 @@ type StackTemplateServiceResponse struct { } type StackTemplateResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Template string `json:"template"` - TemplateType string `json:"templateType"` - CloudService string `json:"cloudService"` - Version string `json:"version"` - Platform string `json:"platform"` - KubeVersion string `json:"kubeVersion"` - KubeType string `json:"kubeType"` - Services []StackTemplateServiceResponse `json:"services"` - Creator SimpleUserResponse `json:"creator"` - Updator SimpleUserResponse `json:"updator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Template string `json:"template"` + TemplateType string `json:"templateType"` + CloudService string `json:"cloudService"` + Version string `json:"version"` + Platform string `json:"platform"` + KubeVersion string `json:"kubeVersion"` + KubeType string `json:"kubeType"` + Organizations []SimpleOrganizationResponse `json:"organizations"` + Services []StackTemplateServiceResponse `json:"services"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SimpleStackTemplateServiceResponse struct { + Type string `json:"type"` } type SimpleStackTemplateResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Template string `json:"template"` - CloudService string `json:"cloudService"` - KubeVersion string `json:"kubeVersion"` - KubeType string `json:"kubeType"` - Services []StackTemplateServiceResponse `json:"services"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Template string `json:"template"` + CloudService string `json:"cloudService"` + KubeVersion string `json:"kubeVersion"` + KubeType string `json:"kubeType"` + Services []SimpleStackTemplateServiceResponse `json:"services"` } type GetStackTemplatesResponse struct { @@ -81,13 +62,17 @@ type GetStackTemplateResponse struct { } type CreateStackTemplateRequest struct { - Name string `json:"name" validate:"required,name"` - Description string `json:"description"` - CloudService string `json:"cloudService" validate:"oneof=AWS AZZURE GCP"` - Version string `json:"version" validate:"required"` - Platform string `json:"platform" validate:"required"` - Template string `json:"template" validate:"required"` - TemplateType string `json:"templateType" validate:"oneof=STANDARD MSA"` + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + Version string `json:"version" validate:"required"` + CloudService string `json:"cloudService" validate:"oneof=AWS BYOH"` + Platform string `json:"platform" validate:"required"` + TemplateType string `json:"templateType" validate:"oneof=STANDARD MSA"` + Template string `json:"template" validate:"required"` + KubeVersion string `json:"kubeVersion" validate:"required"` + KubeType string `json:"kubeType" validate:"required"` + OrganizationIds []string `json:"organizationIds" validate:"required"` + ServiceIds []string `json:"serviceIds" validate:"required"` } type CreateStackTemplateResponse struct { @@ -95,5 +80,39 @@ type CreateStackTemplateResponse struct { } type UpdateStackTemplateRequest struct { - Description string `json:"description"` + Description string `json:"description"` + Version string `json:"version" validate:"required"` + CloudService string `json:"cloudService" validate:"oneof=AWS BYOH"` + Platform string `json:"platform" validate:"required"` + TemplateType string `json:"templateType" validate:"oneof=STANDARD MSA"` + Template string `json:"template" validate:"required"` + KubeVersion string `json:"kubeVersion" validate:"required"` + KubeType string `json:"kubeType" validate:"required"` + OrganizationIds []string `json:"organizationIds" validate:"required"` + ServiceIds []string `json:"serviceIds" validate:"required"` + Name string `json:"name" validate:"required,name"` +} + +type GetStackTemplateServicesResponse struct { + Services []StackTemplateServiceResponse `json:"services"` +} + +type UpdateStackTemplateOrganizationsRequest struct { + OrganizationIds []string `json:"organizationIds" validate:"required"` +} + +type CheckStackTemplateNameResponse struct { + Existed bool `json:"existed"` +} + +type AddOrganizationStackTemplatesRequest struct { + StackTemplateIds []string `json:"stackTemplateIds" validate:"required"` +} + +type RemoveOrganizationStackTemplatesRequest struct { + StackTemplateIds []string `json:"stackTemplateIds" validate:"required"` +} + +type GetStackTemplateTemplateIdsResponse struct { + TemplateIds []string `json:"templateIds"` } diff --git a/pkg/domain/stack.go b/pkg/domain/stack.go index b4935bcb..90f4f96b 100644 --- a/pkg/domain/stack.go +++ b/pkg/domain/stack.go @@ -3,7 +3,6 @@ package domain import ( "time" - "github.com/google/uuid" "github.com/openinfradev/tks-api/internal/helper" ) @@ -66,53 +65,14 @@ func (m StackStatus) FromString(s string) StackStatus { return StackStatus_PENDING } -const MAX_STEP_CLUSTER_CREATE = 24 -const MAX_STEP_CLUSTER_REMOVE = 14 +const MAX_STEP_CLUSTER_CREATE = 26 +const MAX_STEP_CLUSTER_REMOVE = 16 const MAX_STEP_LMA_CREATE_PRIMARY = 39 const MAX_STEP_LMA_CREATE_MEMBER = 29 const MAX_STEP_LMA_REMOVE = 12 const MAX_STEP_SM_CREATE = 22 const MAX_STEP_SM_REMOVE = 4 -// model -type Stack = struct { - ID StackId - Name string - Description string - ClusterId string - OrganizationId string - CloudService string - CloudAccountId uuid.UUID - CloudAccount CloudAccount - StackTemplateId uuid.UUID - StackTemplate StackTemplate - Status StackStatus - StatusDesc string - Conf StackConf - PrimaryCluster bool - GrafanaUrl string - CreatorId *uuid.UUID - Creator User - UpdatorId *uuid.UUID - Updator User - CreatedAt time.Time - UpdatedAt time.Time - Favorited bool - ClusterEndpoint string - Resource DashboardStackResponse -} - -type StackConf struct { - TksCpNode int - TksCpNodeMax int - TksCpNodeType string - TksInfraNode int - TksInfraNodeMax int - TksInfraNodeType string - TksUserNode int - TksUserNodeMax int - TksUserNodeType string -} type StackStepStatus struct { Status string `json:"status"` Stage string `json:"stage"` @@ -121,22 +81,23 @@ type StackStepStatus struct { } type CreateStackRequest struct { - Name string `json:"name" validate:"required,name,rfc1123"` - Description string `json:"description"` - ClusterId string `json:"clusterId"` - CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"` - StackTemplateId string `json:"stackTemplateId" validate:"required"` - CloudAccountId string `json:"cloudAccountId"` - ClusterEndpoint string `json:"userClusterEndpoint,omitempty"` - TksCpNode int `json:"tksCpNode"` - TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` - TksCpNodeType string `json:"tksCpNodeType,omitempty"` - TksInfraNode int `json:"tksInfraNode"` - TksInfraNodeMax int `json:"tksInfraNodeMax,omitempty"` - TksInfraNodeType string `json:"tksInfraNodeType,omitempty"` - TksUserNode int `json:"tksUserNode"` - TksUserNodeMax int `json:"tksUserNodeMax,omitempty"` - TksUserNodeType string `json:"tksUserNodeType,omitempty"` + Name string `json:"name" validate:"required,name,rfc1123"` + Description string `json:"description"` + ClusterId string `json:"clusterId"` + CloudService string `json:"cloudService" validate:"required,oneof=AWS BYOH"` + StackTemplateId string `json:"stackTemplateId" validate:"required"` + CloudAccountId string `json:"cloudAccountId"` + ClusterEndpoint string `json:"userClusterEndpoint,omitempty"` + PolicyIds []string `json:"policyIds,omitempty"` + TksCpNode int `json:"tksCpNode"` + TksCpNodeMax int `json:"tksCpNodeMax,omitempty"` + TksCpNodeType string `json:"tksCpNodeType,omitempty"` + TksInfraNode int `json:"tksInfraNode"` + TksInfraNodeMax int `json:"tksInfraNodeMax,omitempty"` + TksInfraNodeType string `json:"tksInfraNodeType,omitempty"` + TksUserNode int `json:"tksUserNode"` + TksUserNodeMax int `json:"tksUserNodeMax,omitempty"` + TksUserNodeType string `json:"tksUserNodeType,omitempty"` } type CreateStackResponse struct { @@ -172,10 +133,24 @@ type StackResponse struct { Favorited bool `json:"favorited"` ClusterEndpoint string `json:"userClusterEndpoint,omitempty"` Resource DashboardStackResponse `json:"resource,omitempty"` + AppServeAppCnt int `json:"appServeAppCnt"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } +type SimpleStackResponse struct { + ID StackId `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + OrganizationId string `json:"organizationId"` + StackTemplate SimpleStackTemplateResponse `json:"stackTemplate,omitempty"` + CloudAccount SimpleCloudAccountResponse `json:"cloudAccount,omitempty"` + Status string `json:"status"` + PrimaryCluster bool `json:"primaryCluster"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + type GetStacksResponse struct { Stacks []StackResponse `json:"stacks"` Pagination PaginationResponse `json:"pagination"` diff --git a/pkg/domain/system-notification-rule.go b/pkg/domain/system-notification-rule.go new file mode 100644 index 00000000..c7d1853c --- /dev/null +++ b/pkg/domain/system-notification-rule.go @@ -0,0 +1,128 @@ +package domain + +import ( + "time" +) + +// enum +type SystemNotificationRuleStatus int32 + +const ( + SystemNotificationRuleStatus_PENDING SystemNotificationRuleStatus = iota + SystemNotificationRuleStatus_APPLIED + SystemNotificationRuleStatus_ERROR +) + +var systemNotificationRuleStatus = [...]string{ + "PENDING", + "APPLIED", + "ERROR", +} + +func (m SystemNotificationRuleStatus) String() string { return systemNotificationRuleStatus[(m)] } +func (m SystemNotificationRuleStatus) FromString(s string) SystemNotificationRuleStatus { + for i, v := range systemNotificationRuleStatus { + if v == s { + return SystemNotificationRuleStatus(i) + } + } + return SystemNotificationRuleStatus_PENDING +} + +type SystemNotificationRuleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + NotificationType string `json:"notificationType"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle"` + MessageContent string `json:"messageContent"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUsers []SimpleUserResponse `json:"targetUsers"` + SystemNotificationTemplate SimpleSystemNotificationTemplateResponse `json:"systemNotificationTemplate"` + SystemNotificationCondition SystemNotificationConditionResponse `json:"systemNotificationCondition"` + IsSystem bool `json:"isSystem"` + Status string `json:"status"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SystemNotificationParameter struct { + Order int `json:"order"` + Operator string `json:"operator"` + Value string `json:"value"` +} + +type SystemNotificationConditionResponse struct { + SystemNotificationRuleId string `json:"systemNotificationRuleId"` + Severity string `json:"severity"` + Duration string `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` +} + +type SimpleSystemNotificationRuleResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Status string `json:"status"` +} + +type GetSystemNotificationRulesResponse struct { + SystemNotificationRules []SystemNotificationRuleResponse `json:"systemNotificationRules"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetSystemNotificationRuleResponse struct { + SystemNotificationRule SystemNotificationRuleResponse `json:"systemNotificationRule"` +} + +type CreateSystemNotificationRuleRequest struct { + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle" validate:"required"` + MessageContent string `json:"messageContent" validate:"required"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUserIds []string `json:"targetUserIds"` + SystemNotificationTemplateId string `json:"systemNotificationTemplateId" validate:"required"` + SystemNotificationCondition struct { + Severity string `json:"severity"` + Duration string `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` + } `json:"systemNotificationCondition"` +} + +type CreateSystemNotificationRuleResponse struct { + ID string `json:"id"` +} + +type UpdateSystemNotificationRuleRequest struct { + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + MessageTitle string `json:"messageTitle" validate:"required"` + MessageContent string `json:"messageContent" validate:"required"` + MessageActionProposal string `json:"messageActionProposal"` + TargetUserIds []string `json:"targetUserIds"` + SystemNotificationTemplateId string `json:"systemNotificationTemplateId" validate:"required"` + SystemNotificationCondition struct { + SystemNotificationRuleId string `json:"systemNotificationRuleId"` + Severity string `json:"severity"` + Duration string `json:"duration"` + Parameters []SystemNotificationParameter `json:"parameters"` + EnableEmail bool `json:"enableEmail"` + EnablePortal bool `json:"enablePortal"` + } `json:"systemNotificationCondition"` +} + +type CheckSystemNotificationRuleNameResponse struct { + Existed bool `json:"existed"` +} + +type DeleteSystemNotificationRuleResponse struct { + ID string `json:"id"` + Name string `json:"name"` +} diff --git a/pkg/domain/system-notification-template.go b/pkg/domain/system-notification-template.go new file mode 100644 index 00000000..d2e54fa0 --- /dev/null +++ b/pkg/domain/system-notification-template.go @@ -0,0 +1,88 @@ +package domain + +import ( + "time" +) + +const SN_TYPE_NODE_CPU_HIGH_LOAD = "node-cpu-high-load" +const SN_TYPE_NODE_MEMORY_HIGH_UTILIZATION = "node-memory-high-utilization" +const SN_TYPE_NODE_DISK_FULL = "node-disk-full" +const SN_TYPE_PVC_FULL = "pvc-full" +const SN_TYPE_POD_RESTART_FREQUENTLY = "pod-restart-frequently" +const SN_TYPE_POLICY_AUDITED = "policy-audited" +const SN_TYPE_POLICY_BLOCKED = "policy-blocked" + +const ( + NT_SYSTEM_NOTIFICATION = "SYSTEM_NOTIFICATION" + NT_POLICY = "POLICY" +) + +type SystemNotificationTemplateResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + NotificationType string `json:"notificationType"` + MetricQuery string `json:"metricQuery" validate:"required"` + MetricParameters []SystemNotificationMetricParameterResponse `json:"metricParameters,omitempty"` + Organizations []SimpleOrganizationResponse `json:"organizations,omitempty"` + IsSystem bool `json:"isSystem"` + Creator SimpleUserResponse `json:"creator"` + Updator SimpleUserResponse `json:"updator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SimpleSystemNotificationTemplateResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type SystemNotificationMetricParameterResponse struct { + Order int `json:"order" validate:"required"` + Key string `json:"key" validate:"required,name"` + Value string `json:"value" validate:"required"` +} + +type CreateSystemNotificationTemplateRequest struct { + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + OrganizationIds []string `json:"organizationIds" validate:"required"` + MetricQuery string `json:"metricQuery" validate:"required"` + MetricParameters []SystemNotificationMetricParameterResponse `json:"metricParameters"` +} + +type CreateSystemNotificationTemplateResponse struct { + ID string `json:"id"` +} + +type UpdateSystemNotificationTemplateRequest struct { + Name string `json:"name" validate:"required,name"` + Description string `json:"description"` + OrganizationIds []string `json:"organizationIds" validate:"required"` + MetricQuery string `json:"metricQuery" validate:"required"` + MetricParameters []SystemNotificationMetricParameterResponse `json:"metricParameters"` +} + +type GetSystemNotificationTemplatesResponse struct { + SystemNotificationTemplates []SystemNotificationTemplateResponse `json:"systemNotificationTemplates"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetSystemNotificationTemplateResponse struct { + SystemNotificationTemplate SystemNotificationTemplateResponse `json:"systemNotificationTemplate"` +} + +type AddOrganizationSystemNotificationTemplatesRequest struct { + SystemNotificationTemplateIds []string `json:"systemNotificationTemplateIds" validate:"required"` +} + +type RemoveOrganizationSystemNotificationTemplatesRequest struct { + SystemNotificationTemplateIds []string `json:"systemNotificationTemplateIds" validate:"required"` +} + +type CheckSystemNotificaionTemplateNameResponse struct { + Existed bool `json:"existed"` +} diff --git a/pkg/domain/system-notification.go b/pkg/domain/system-notification.go new file mode 100644 index 00000000..4b7e5b30 --- /dev/null +++ b/pkg/domain/system-notification.go @@ -0,0 +1,144 @@ +package domain + +import ( + "time" + + "github.com/google/uuid" +) + +// enum +type SystemNotificationActionStatus int32 + +const ( + SystemNotificationActionStatus_CREATED SystemNotificationActionStatus = iota + SystemNotificationActionStatus_INPROGRESS + SystemNotificationActionStatus_CLOSED + SystemNotificationActionStatus_ERROR +) + +var systemNotificationActionStatus = [...]string{ + "CREATED", + "INPROGRESS", + "CLOSED", + "ERROR", +} + +func (m SystemNotificationActionStatus) String() string { return systemNotificationActionStatus[(m)] } +func (m SystemNotificationActionStatus) FromString(s string) SystemNotificationActionStatus { + for i, v := range systemNotificationActionStatus { + if v == s { + return SystemNotificationActionStatus(i) + } + } + return SystemNotificationActionStatus_ERROR +} + +type SystemNotificationRequest struct { + Status string `json:"status"` + GeneratorURL string `json:"generatorURL"` + FingerPrint string `json:"fingerprint"` + StartsAt time.Time `json:"startsAt"` + EndsAt time.Time `json:"endsAt"` + Labels struct { + AlertName string `json:"alertname"` + Container string `json:"container"` + Endpoint string `json:"endpoint"` + Job string `json:"job"` + Namespace string `json:"namespace"` + Pod string `json:"pod"` + Prometheus string `json:"prometheus"` + Service string `json:"service"` + Severity string `json:"severity"` + Instance string `json:"instance"` + TacoCluster string `json:"taco_cluster"` + } `json:"labels"` + Annotations struct { + Message string `json:"message"` + Summary string `json:"summary"` + Description string `json:"description"` + Checkpoint string `json:"Checkpoint"` + Discriminative string `json:"discriminative"` + AlertType string `json:"alertType"` + SystemNotificationRuleId string `json:"systemNotificationRuleId"` + PolicyName string `json:"policyName"` + PolicyTemplateName string `json:"policyTemplateName"` + } `json:"annotations"` +} + +type CreateSystemNotificationRequest struct { + Receiver string `json:"receiver"` + Status string `json:"status"` + ExternalURL string `json:"externalURL"` + Version string `json:"version"` + GroupKey string `json:"groupKey"` + TruncatedAlerts int `json:"truncatedAlerts"` + SystemNotifications []SystemNotificationRequest `json:"alerts"` + GroupLabels struct { + SystemNotificationName string `json:"alertname"` + } `json:"groupLabels"` + //CommonLabels string `json:"commonLabels"` + //CommonAnnotations string `json:"commonAnnotations"` +} + +type SystemNotificationResponse struct { + ID string `json:"id"` + Name string `json:"name"` + OrganizationId string `json:"organizationId"` + Severity string `json:"severity"` + MessageTitle string `json:"messageTitle"` + MessageContent string `json:"messageContent"` + MessageActionProposal string `json:"messageActionProposal"` + Cluster SimpleClusterResponse `json:"cluster"` + GrafanaUrl string `json:"grafanaUrl"` + Node string `json:"node"` + FiredAt *time.Time `json:"firedAt"` + TakedAt *time.Time `json:"takedAt"` + ClosedAt *time.Time `json:"closedAt"` + Status string `json:"status"` + ProcessingSec int `json:"processingSec"` + TakedSec int `json:"takedSec"` + SystemNotificationActions []SystemNotificationActionResponse `json:"systemNotificationActions"` + LastTaker SimpleUserResponse `json:"lastTaker"` + RawData string `json:"rawData"` + NotificationType string `json:"notificationType"` + Read bool `json:"read"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + PolicyName string `json:"policyName"` +} + +type SystemNotificationActionResponse struct { + ID uuid.UUID `json:"id"` + SystemNotificationId uuid.UUID `json:"systemNotificationId"` + Content string `json:"content"` + Status string `json:"status"` + Taker SimpleUserResponse `json:"taker"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetSystemNotificationsResponse struct { + SystemNotifications []SystemNotificationResponse `json:"systemNotifications"` + Pagination PaginationResponse `json:"pagination"` +} + +type GetSystemNotificationResponse struct { + SystemNotification SystemNotificationResponse `json:"systemNotification"` +} + +type CreateSystemNotificationResponse struct { + ID string `json:"id"` +} + +type UpdateSystemNotificationRequest struct { + Description string `json:"description"` +} + +type CreateSystemNotificationActionRequest struct { + Content string `json:"content"` + Status string `json:"status" validate:"oneof=INPROGRESS CLOSED"` +} + +type CreateSystemNotificationActionResponse struct { + ID string `json:"id"` +} diff --git a/pkg/domain/user.go b/pkg/domain/user.go index fbea7635..e602d74d 100644 --- a/pkg/domain/user.go +++ b/pkg/domain/user.go @@ -2,94 +2,88 @@ package domain import ( "time" + + "github.com/google/uuid" ) -type User = struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Password string `json:"password"` - Name string `json:"name"` - Token string `json:"token"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` - PasswordExpired bool `json:"passwordExpired"` +type UserResponse struct { + ID uuid.UUID `gorm:"primarykey;type:uuid" json:"id"` + AccountId string `json:"accountId"` + Password string `gorm:"-:all" json:"password"` + Name string `json:"name"` + Token string `json:"token"` + RoleId string + Roles []SimpleRoleResponse `json:"roles"` + OrganizationId string + Organization OrganizationResponse `gorm:"foreignKey:OrganizationId;references:ID" json:"organization"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + PasswordUpdatedAt time.Time `json:"passwordUpdatedAt"` + PasswordExpired bool `json:"passwordExpired"` Email string `json:"email"` Department string `json:"department"` Description string `json:"description"` } -type Role = struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` +type CreateUserRequest struct { + AccountId string `json:"accountId" validate:"required"` + Password string `json:"password" validate:"required"` + Name string `json:"name" validate:"name"` + Email string `json:"email" validate:"required,email"` + Department string `json:"department" validate:"min=0,max=50"` + Roles []UserCreationRole `json:"roles" validate:"required"` + Description string `json:"description" validate:"min=0,max=100"` } -type Policy = struct { - ID string `json:"id"` - Name string `json:"name"` - Create bool `json:"create"` - CreatePriviledge string `json:"createPriviledge"` - Update bool `json:"update"` - UpdatePriviledge string `json:"updatePriviledge"` - Read bool `json:"read"` - ReadPriviledge string `json:"readPriviledge"` - Delete bool `json:"delete"` - DeletePriviledge string `json:"deletePriviledge"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` +type UserCreationRole struct { + ID *string `json:"id" validate:"required"` } -type CreateUserRequest struct { - AccountId string `json:"accountId" validate:"required"` - Password string `json:"password" validate:"required"` - Name string `json:"name" validate:"name"` - Email string `json:"email" validate:"required,email"` - Department string `json:"department" validate:"min=0,max=50"` - Role string `json:"role" validate:"required,oneof=admin user"` - Description string `json:"description" validate:"min=0,max=100"` +type SimpleUserResponse struct { + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Department string `json:"department"` + Email string `json:"email"` } -type SimpleUserResponse struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` +type SimpleUserResponseWithRoles struct { + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Department string `json:"department"` + Email string `json:"email"` + Roles []SimpleRoleResponse `json:"roles"` } type CreateUserResponse struct { User struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` - Description string `json:"description"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` } `json:"user"` } type GetUserResponse struct { User struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` - Description string `json:"description"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } `json:"user"` } @@ -98,69 +92,80 @@ type ListUserResponse struct { Pagination PaginationResponse `json:"pagination"` } type ListUserBody struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` - Description string `json:"description"` - Creator string `json:"creator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` + Creator string `json:"creator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } type UpdateUserRequest struct { - Name string `json:"name" validate:"omitempty,min=1,max=30"` - Role string `json:"role" validate:"oneof=admin user"` - Email string `json:"email" validate:"omitempty,email"` - Department string `json:"department" validate:"omitempty,min=0,max=50"` - Description string `json:"description" validate:"omitempty,min=0,max=100"` + Name string `json:"name" validate:"required"` + Roles []UserCreationRole `json:"roles" validate:"required"` + Email string `json:"email" validate:"required,email"` + Department string `json:"department" validate:"min=0,max=50"` + Description string `json:"description" validate:"min=0,max=100"` +} + +type UpdateUsersRequest struct { + Users []struct { + AccountId string `json:"accountId" validate:"required"` + Name string `json:"name" validate:"required,name"` + Roles []UserCreationRole `json:"roles" validate:"required"` + Email string `json:"email" validate:"required,email"` + Department string `json:"department" validate:"min=0,max=50"` + Description string `json:"description" validate:"min=0,max=100"` + } `json:"users"` } type UpdateUserResponse struct { User struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` - Description string `json:"description"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } `json:"user"` } type GetMyProfileResponse struct { User struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` } `json:"user"` } type UpdateMyProfileRequest struct { Password string `json:"password" validate:"required"` - Name string `json:"name" validate:"omitempty,min=1,max=30"` - Email string `json:"email" validate:"omitempty,email"` - Department string `json:"department" validate:"omitempty,min=0,max=50"` + Name string `json:"name" validate:"required,min=1,max=30"` + Email string `json:"email" validate:"required,email"` + Department string `json:"department" validate:"min=0,max=50"` } type UpdateMyProfileResponse struct { User struct { - ID string `json:"id"` - AccountId string `json:"accountId"` - Name string `json:"name"` - Role Role `json:"role"` - Organization Organization `json:"organization"` - Email string `json:"email"` - Department string `json:"department"` + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` } `json:"user"` } @@ -172,3 +177,49 @@ type UpdatePasswordRequest struct { type CheckExistedResponse struct { Existed bool `json:"existed"` } + +type Admin_CreateUserRequest struct { + AccountId string `json:"accountId" validate:"required"` + Name string `json:"name" validate:"name"` + Email string `json:"email" validate:"required,email"` + Roles []UserCreationRole `json:"roles" validate:"required"` + Department string `json:"department" validate:"min=0,max=50"` + Description string `json:"description" validate:"min=0,max=100"` + AdminPassword string `json:"adminPassword"` +} + +type Admin_CreateUserResponse struct { + ID string `json:"id"` +} + +type Admin_UpdateUserRequest struct { + Name string `json:"name" validate:"name"` + Email string `json:"email" validate:"required,email"` + Department string `json:"department" validate:"min=0,max=50"` + Roles []UserCreationRole `json:"roles" validate:"required"` + Description string `json:"description" validate:"min=0,max=100"` + AdminPassword string `json:"adminPassword"` +} + +type Admin_UpdateUserResponse struct { + User struct { + ID string `json:"id"` + AccountId string `json:"accountId"` + Name string `json:"name"` + Roles []SimpleRoleResponse `json:"roles"` + Organization OrganizationResponse `json:"organization"` + Email string `json:"email"` + Department string `json:"department"` + Description string `json:"description"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + } `json:"user"` +} + +type DeleteUserRequest struct { + AdminPassword string `json:"adminPassword"` +} + +type DeleteUserResponse struct { + AccountId string `json:"accountId"` +} diff --git a/pkg/httpErrors/errorCode.go b/pkg/httpErrors/errorCode.go index 0fa40986..2d8319ed 100644 --- a/pkg/httpErrors/errorCode.go +++ b/pkg/httpErrors/errorCode.go @@ -4,29 +4,44 @@ type ErrorCode string var errorMap = map[ErrorCode]string{ // Common - "C_INTERNAL_ERROR": "예상하지 못한 오류가 발생했습니다. 문제가 계속되면 관리자에게 문의해주세요.", - "C_INVALID_ACCOUNT_ID": "유효하지 않은 어카운트 아이디입니다. 어카운트 아이디를 확인하세요.", - "C_INVALID_STACK_ID": "유효하지 않은 스택 아이디입니다. 스택 아이디를 확인하세요.", - "C_INVALID_CLUSTER_ID": "유효하지 않은 클러스터 아이디입니다. 클러스터 아이디를 확인하세요.", - "C_INVALID_APPGROUP_ID": "유효하지 않은 앱그룹 아이디입니다. 앱그룹 아이디를 확인하세요.", - "C_INVALID_ORGANIZATION_ID": "유효하지 않은 조직 아이디입니다. 조직 아이디를 확인하세요.", - "C_INVALID_CLOUD_ACCOUNT_ID": "유효하지 않은 클라우드어카운트 아이디입니다. 클라우드어카운트 아이디를 확인하세요.", - "C_INVALID_STACK_TEMPLATE_ID": "유효하지 않은 스택템플릿 아이디입니다. 스택템플릿 아이디를 확인하세요.", - "C_INVALID_ASA_ID": "유효하지 않은 앱서빙앱 아이디입니다. 앱서빙앱 아이디를 확인하세요.", - "C_INVALID_ASA_TASK_ID": "유효하지 않은 테스크 아이디입니다. 테스크 아이디를 확인하세요.", - "C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.", - "C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.", + "C_INTERNAL_ERROR": "예상하지 못한 오류가 발생했습니다. 문제가 계속되면 관리자에게 문의해주세요.", + "C_INVALID_ACCOUNT_ID": "유효하지 않은 어카운트 아이디입니다. 어카운트 아이디를 확인하세요.", + "C_INVALID_STACK_ID": "유효하지 않은 스택 아이디입니다. 스택 아이디를 확인하세요.", + "C_INVALID_CLUSTER_ID": "유효하지 않은 클러스터 아이디입니다. 클러스터 아이디를 확인하세요.", + "C_INVALID_APPGROUP_ID": "유효하지 않은 앱그룹 아이디입니다. 앱그룹 아이디를 확인하세요.", + "C_INVALID_ORGANIZATION_ID": "유효하지 않은 조직 아이디입니다. 조직 아이디를 확인하세요.", + "C_INVALID_PROJECT_ID": "유효하지 않은 프로젝트 아이디입니다. 아이디를 확인하세요.", + "C_INVALID_CLOUD_ACCOUNT_ID": "유효하지 않은 클라우드어카운트 아이디입니다. 클라우드어카운트 아이디를 확인하세요.", + "C_INVALID_STACK_TEMPLATE_ID": "유효하지 않은 스택템플릿 아이디입니다. 스택템플릿 아이디를 확인하세요.", + "C_INVALID_SYSTEM_NOTIFICATION_TEMPLATE_ID": "유효하지 않은 알림템플릿 아이디입니다. 알림템플릿 아이디를 확인하세요.", + "C_INVALID_SYSTEM_NOTIFICATION_RULE_ID": "유효하지 않은 알림설정 아이디입니다. 알림설정 아이디를 확인하세요.", + "C_INVALID_ASA_ID": "유효하지 않은 앱서빙앱 아이디입니다. 앱서빙앱 아이디를 확인하세요.", + "C_INVALID_ASA_TASK_ID": "유효하지 않은 테스크 아이디입니다. 테스크 아이디를 확인하세요.", + "C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.", + "C_INVALID_AUDIT_ID": "유효하지 않은 로그 아이디입니다. 로그 아이디를 확인하세요.", + "C_INVALID_POLICY_TEMPLATE_ID": "유효하지 않은 정책 템플릿 아이디입니다. 정책 템플릿 아이디를 확인하세요.", + "C_INVALID_POLICY_ID": "유효하지 않은 정책 아이디입니다. 정책 아이디를 확인하세요.", + "C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.", // Auth "A_INVALID_ID": "아이디가 존재하지 않습니다.", "A_INVALID_PASSWORD": "비밀번호가 일치하지 않습니다.", "A_SAME_OLD_PASSWORD": "기존 비밀번호와 동일합니다.", "A_INVALID_TOKEN": "사용자 토큰 오류", + "A_EXPIRED_TOKEN": "사용자 토큰 만료", "A_INVALID_USER_CREDENTIAL": "비밀번호가 일치하지 않습니다.", "A_INVALID_ORIGIN_PASSWORD": "기존 비밀번호가 일치하지 않습니다.", "A_INVALID_CODE": "인증번호가 일치하지 않습니다.", "A_NO_SESSION": "세션 정보를 찾을 수 없습니다.", "A_EXPIRED_CODE": "인증번호가 만료되었습니다.", + "A_UNUSABLE_TOKEN": "사용할 수 없는 토큰입니다.", + + // Organization + "O_INVALID_ORGANIZATION_NAME": "조직에 이미 존재하는 이름입니다.", + "O_NOT_EXISTED_NAME": "조직이 존재하지 않습니다.", + "O_FAILED_UPDATE_STACK_TEMPLATES": "조직에 스택템플릿을 설정하는데 실패했습니다", + "O_FAILED_UPDATE_POLICY_TEMPLATES": "조직에 정책템플릿을 설정하는데 실패했습니다", + "O_FAILED_UPDATE_SYSTEM_NOTIFICATION_TEMPLATES": "조직에 알림템플릿을 설정하는데 실패했습니다", // User "U_NO_USER": "해당 사용자 정보를 찾을 수 없습니다.", @@ -64,15 +79,74 @@ var errorMap = map[ErrorCode]string{ "S_INVALID_CLUSTER_URL": "BYOH 타입의 클러스터 생성은 반드시 userClusterEndpoint 값이 필요합니다.", "S_INVALID_CLUSTER_ID": "BYOH 타입의 클러스터 생성은 반드시 clusterId 값이 필요합니다.", "S_INVALID_CLOUD_SERVICE": "클라우드 서비스 타입이 잘못되었습니다.", + "S_FAILED_DELETE_POLICIES": "스택의 폴리시들을 삭제하는 실패하였습니다", // Alert "AL_NOT_FOUND_ALERT": "지정한 앨럿이 존재하지 않습니다.", + // SystemNotificationTemplate + "SNT_CREATE_ALREADY_EXISTED_NAME": "알림템플릿에 이미 존재하는 이름입니다.", + "SNT_FAILED_FETCH_ALERT_TEMPLATE": "알림템플릿을 가져오는데 실패했습니다.", + "SNT_FAILED_UPDATE_ORGANIZATION": "알림템플릿에 조직을 설정하는데 실패했습니다.", + "SNT_NOT_EXISTED_ALERT_TEMPLATE": "업데이트할 알림템플릿이 존재하지 않습니다.", + "SNT_FAILED_DELETE_EXIST_RULES": "알림템플릿을 사용하고 있는 알림 설정이 있습니다. 알림 설정을 삭제하세요.", + "SNT_CANNOT_DELETE_SYSTEM_TEMPLATE": "시스템 알림템플릿은 삭제 할 수 없습니다.", + + // SystemNotificationRule + "SNR_CREATE_ALREADY_EXISTED_NAME": "알림 설정에 이미 존재하는 이름입니다.", + "SNR_FAILED_FETCH_SYSTEM_NOTIFICATION_RULE": "알림 설정을 가져오는데 실패했습니다.", + "SNR_FAILED_UPDATE_ORGANIZATION": "알림 설정에 조직을 설정하는데 실패했습니다.", + "SNR_NOT_EXISTED_SYSTEM_NOTIFICATION_RULE": "업데이트할 알림 설정이 존재하지 않습니다.", + "SNR_INVALID_ENABLE_PORTAL": "알림 방법의 포탈은 설정을 변경할 수 없습니다.", + "SNR_CANNOT_DELETE_SYSTEM_RULE": "시스템 알림 설정은 삭제 할 수 없습니다.", + // AppGroup "AG_NOT_FOUND_CLUSTER": "지장한 클러스터가 존재하지 않습니다.", "AG_NOT_FOUND_APPGROUP": "지장한 앱그룹이 존재하지 않습니다.", "AG_FAILED_TO_CREATE_APPGROUP": "앱그룹 생성에 실패하였습니다.", "AG_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패하였습니다.", + + // StackTemplate + "ST_CREATE_ALREADY_EXISTED_NAME": "스택템플릿에 이미 존재하는 이름입니다.", + "ST_FAILED_UPDATE_ORGANIZATION": "스택템플릿에 조직을 설정하는데 실패했습니다.", + "ST_NOT_EXISTED_STACK_TEMPLATE": "업데이트할 스택템플릿이 존재하지 않습니다.", + "ST_INVALID_STACK_TEMAPLTE_NAME": "유효하지 않은 스택템플릿 이름입니다. 스택템플릿 이름을 확인하세요.", + "ST_FAILED_FETCH_STACK_TEMPLATE": "스택템플릿을 가져오는데 실패했습니다.", + "ST_FAILED_ADD_ORGANIZATION_STACK_TEMPLATE": "조직에 스택템플릿을 추가하는데 실패하였습니다.", + "ST_FAILED_REMOVE_ORGANIZATION_STACK_TEMPLATE": "조직에서 스택템플릿을 삭제하는데 실패하였습니다.", + "ST_FAILED_ADD_ORGANIZATION_SYSTEM_NOTIFICATION_TEMPLATE": "조직에 시스템알람템플릿을 추가하는데 실패하였습니다.", + "ST_FAILED_REMOVE_ORGANIZATION_SYSTEM_NOTIFICATION_TEMPLATE": "조직에서 시스템알람템플릿을 삭제하는데 실패하였습니다.", + "ST_FAILED_DELETE_EXIST_CLUSTERS": "스택템플릿을 사용하고 있는 스택이 있습니다. 스택을 삭제하세요.", + "C_INVALID_STACK_TEMPLATE_TEMPLATE_IDS": "템플릿아이디를 조회하는데 실패하였습니다.", + + // PolicyTemplate + "PT_CREATE_ALREADY_EXISTED_NAME": "정첵 템플릿에 이미 존재하는 이름입니다.", + "PT_CREATE_ALREADY_EXISTED_KIND": "정책 템플릿에 이미 존재하는 유형입니다.", + "PT_NOT_FOUND_POLICY_TEMPLATE": "정책 템플릿이 존재하지 않습니다.", + "PT_INVALID_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.", + "PT_FAILED_FETCH_POLICY_TEMPLATE": "정책 템플릿 ID에 해당하는 정책 템플릿을 가져오는데 실패했습니다.", + "PT_INVALID_REGO_SYNTAX": "Rego 문법 오류입니다.", + "PT_INVALID_POLICY_TEMPLATE_VERSION": "유효하지 않은 정책 템플릿 버전닙니다. 정책 템플릿 버전을 확인하세요.", + "PT_NOT_FOUND_POLICY_TEMPLATE_VERSION": "정책 템플릿 버전이 존재하지 않습니다.", + "PT_INVALID_POLICY_TEMPLATE_NAME": "유효하지 않은 정책 템플릿 이름입니다. 정책 템플릿 이름을 확인하세요.", + "PT_INVALID_POLICY_TEMPLATE_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.", + "PT_INVALID_REGO_PARSEPARAMETER": "유효하지 않은 Rego 파싱 설정입니다. Rego 파싱 설정을 확인하세요.", + "PT_NOT_PERMITTED_ON_TKS_POLICY_TEMPLATE": "tks 템플릿에 대해 해당 동작을 수행할 수 없습니다.", + "PT_INVALID_PARAMETER_SCHEMA": "파라미터 스키마에 잘못된 타입이 지정되었습니다.", + "PT_INVALID_SYNC": "잘못된 데이터 동기화 설정입니다. 데이터 동기화 설정을 확인하세요.", + + // Policy + "P_CREATE_ALREADY_EXISTED_NAME": "정첵에 이미 존재하는 이름입니다.", + "P_NOT_FOUND_POLICY": "정책이 존재하지 않습니다.", + "P_INVALID_POLICY_NAME": "유효하지 않은 정책 이름입니다. 정책 이름을 확인하세요.", + "P_INVALID_POLICY_RESOURCE_NAME": "유효하지 않은 정책 자원 이름(k8s 자원 이름)입니다. 정책 자원 이름을 확인하세요.", + "P_INVALID_MATCH": "유효하지 않은 match 설정입니다. match 설정을 확인하세요.", + "P_FAILED_FETCH_POLICY": "정책 ID에 해당하는 정책을 가져오는데 실패했습니다.", + "P_FAILED_FETCH_CLUSTER": "정책의 클러스터 정보를 가져오는데 실패했습니다.", + "P_FAILED_FETCH_TEMPLATE": "정책의 템플릿 정보를 가져오는데 실패했습니다.", + "P_CALL_TO_APPLY_KUBERNETES": "쿠버네티스 클러스터 호출에 실패했습니다.", + "P_FAILED_TO_APPLY_KUBERNETES": "쿠버네티스 클러스터 변경사항 적용에 실패했습니다.", + "P_INVALID_POLICY_PARAMETER": "정책 파라미터가 템플릿의 파라미터 스키마에 유효하지 않습니다. 파라미터를 확인하세요.", } func (m ErrorCode) GetText() string { diff --git a/pkg/httpErrors/httpErrors.go b/pkg/httpErrors/httpErrors.go index 6e5b4464..bde4fd68 100644 --- a/pkg/httpErrors/httpErrors.go +++ b/pkg/httpErrors/httpErrors.go @@ -4,7 +4,6 @@ import ( "net/http" "strings" - "github.com/openinfradev/tks-api/pkg/log" "github.com/pkg/errors" ) @@ -71,7 +70,6 @@ func NewRestError(status int, err error, code ErrorCode, text string) IRestError if text != "" { t = text } - log.Info(t) return RestError{ ErrStatus: status, diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go new file mode 100644 index 00000000..34cc90fc --- /dev/null +++ b/pkg/kubernetes/kubernetes.go @@ -0,0 +1,679 @@ +package kubernetes + +import ( + "bytes" + "context" + "fmt" + v1 "k8s.io/api/core/v1" + "os" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/spf13/viper" + + rbacV1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "k8s.io/client-go/discovery" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + + clientcmd "k8s.io/client-go/tools/clientcmd" + + "github.com/openinfradev/tks-api/pkg/log" +) + +type KubeConfigType string + +const ( + KubeconfigForAdmin KubeConfigType = "admin" + KubeconfigForUser KubeConfigType = "user" +) + +func getAdminConfig(ctx context.Context) (*rest.Config, error) { + kubeconfigPath := viper.GetString("kubeconfig-path") + if kubeconfigPath == "" { + log.Info(ctx, "Use in-cluster config") + config, err := rest.InClusterConfig() + if err != nil { + log.Error(ctx, "Failed to load incluster kubeconfig") + return nil, err + } + return config, nil + } else { + config, err := clientcmd.BuildConfigFromFlags("", viper.GetString("kubeconfig-path")) + if err != nil { + log.Error(ctx, "Failed to local kubeconfig") + return nil, err + } + return config, nil + } +} + +func GetClientAdminCluster(ctx context.Context) (*kubernetes.Clientset, error) { + config, err := getAdminConfig(ctx) + if err != nil { + log.Error(ctx, "Failed to load kubeconfig") + return nil, err + } + + // create the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + return clientset, nil +} + +// 쿠버네티스 기본 타입 이외의 타입(예: 정책 템플릿, 정책 등)을 처리하기 위한 dynamic client 생성 +func GetDynamicClientAdminCluster(ctx context.Context) (*dynamic.DynamicClient, error) { + config, err := getAdminConfig(ctx) + if err != nil { + log.Error(ctx, "Failed to load kubeconfig") + return nil, err + } + + // create the dynamic client + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, err + } + return dynamicClient, nil +} + +func GetAwsSecret(ctx context.Context) (awsAccessKeyId string, awsSecretAccessKey string, err error) { + clientset, err := GetClientAdminCluster(ctx) + if err != nil { + return "", "", err + } + + secrets, err := clientset.CoreV1().Secrets("argo").Get(context.TODO(), "awsconfig-secret", metav1.GetOptions{}) + if err != nil { + log.Error(ctx, err) + return "", "", err + } + + strCredentials := string(secrets.Data["credentials"][:]) + arr := strings.Split(strCredentials, "\n") + if len(arr) < 3 { + return "", "", err + } + + fmt.Sscanf(arr[1], "aws_access_key_id = %s", &awsAccessKeyId) + fmt.Sscanf(arr[2], "aws_secret_access_key = %s", &awsSecretAccessKey) + + return +} + +func GetAwsAccountIdSecret(ctx context.Context) (awsAccountId string, err error) { + clientset, err := GetClientAdminCluster(ctx) + if err != nil { + return "", err + } + + secrets, err := clientset.CoreV1().Secrets("argo").Get(context.TODO(), "tks-aws-user", metav1.GetOptions{}) + if err != nil { + log.Error(ctx, err) + return "", err + } + + awsAccountId = string(secrets.Data["account_id"][:]) + return +} + +func GetKubeConfig(ctx context.Context, clusterId string, configType KubeConfigType) ([]byte, error) { + clientset, err := GetClientAdminCluster(ctx) + if err != nil { + return nil, err + } + + var secrets *v1.Secret + + if configType == KubeconfigForAdmin { + secrets, err = clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-kubeconfig", metav1.GetOptions{}) + } else if configType == KubeconfigForUser { + secrets, err = clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-user-kubeconfig", metav1.GetOptions{}) + } + if err != nil { + log.Error(ctx, err) + return nil, err + } + + return secrets.Data["value"], nil +} + +func GetClientFromClusterId(ctx context.Context, clusterId string) (*kubernetes.Clientset, error) { + clientset, err := GetClientAdminCluster(ctx) + if err != nil { + return nil, err + } + + secrets, err := clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-kubeconfig", metav1.GetOptions{}) + if err != nil { + log.Error(ctx, err) + return nil, err + } + + config_user, err := clientcmd.RESTConfigFromKubeConfig(secrets.Data["value"]) + if err != nil { + log.Error(ctx, err) + return nil, err + } + clientset_user, err := kubernetes.NewForConfig(config_user) + if err != nil { + return nil, err + } + + return clientset_user, nil +} + +func GetKubernetesVserionByClusterId(ctx context.Context, clusterId string) (string, error) { + clientset, err := GetClientAdminCluster(ctx) + if err != nil { + return "", err + } + + secrets, err := clientset.CoreV1().Secrets(clusterId).Get(context.TODO(), clusterId+"-tks-kubeconfig", metav1.GetOptions{}) + if err != nil { + log.Error(ctx, err) + return "", err + } + + config_user, err := clientcmd.RESTConfigFromKubeConfig(secrets.Data["value"]) + if err != nil { + log.Error(ctx, err) + return "", err + } + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config_user) + if err != nil { + log.Error(ctx, err) + return "", err + } + + information, err := discoveryClient.ServerVersion() + if err != nil { + log.Error(ctx, "Error while fetching server version information", err) + return "", err + } + + return information.GitVersion, nil +} + +func GetKubernetesVserion(ctx context.Context) (string, error) { + config, err := getAdminConfig(ctx) + if err != nil { + log.Error(ctx, "Failed to load kubeconfig") + return "", err + } + + discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) + if err != nil { + log.Error(ctx, err) + return "", err + } + + information, err := discoveryClient.ServerVersion() + if err != nil { + log.Error(ctx, "Error while fetching server version information", err) + return "", err + } + + return information.GitVersion, nil +} + +func GetResourceApiVersion(ctx context.Context, kubeconfig []byte, kind string) (string, error) { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return "", err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + apiResourceList, err := clientset.Discovery().ServerPreferredResources() + if err != nil { + log.Error(ctx, err) + return "", err + } + + for _, apiResource := range apiResourceList { + for _, resource := range apiResource.APIResources { + if resource.Kind == kind { + return resource.Version, nil + } + } + } + + return "", nil +} + +func EnsureClusterRole(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + // generate clusterrole object + for _, role := range []string{leaderRole, memberRole, viewerRole} { + obj := getClusterRole(role, projectName+"-"+role) + + if _, err := clientset.RbacV1().ClusterRoles().Get(context.Background(), projectName+"-"+role, metav1.GetOptions{}); err != nil { + _, err = clientset.RbacV1().ClusterRoles().Create(context.Background(), obj, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + _, err = clientset.RbacV1().ClusterRoles().Update(context.Background(), obj, metav1.UpdateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } + } + + return nil +} + +func RemoveClusterRole(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + // remove clusterrole object + for _, role := range []string{leaderRole, memberRole, viewerRole} { + if err := clientset.RbacV1().ClusterRoles().Delete(context.Background(), projectName+"-"+role, metav1.DeleteOptions{}); err != nil { + log.Error(ctx, err) + } + } + + return nil +} + +func EnsureClusterRoleBinding(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + obj := generateClusterRoleToClusterRoleBinding(role+"@"+projectName, projectName+"-"+role, projectName+"-"+role) + if _, err = clientset.RbacV1().ClusterRoleBindings().Get(context.Background(), projectName+"-"+role, metav1.GetOptions{}); err != nil { + _, err = clientset.RbacV1().ClusterRoleBindings().Create(context.Background(), obj, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + _, err = clientset.RbacV1().ClusterRoleBindings().Update(context.Background(), obj, metav1.UpdateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } + } + + return nil +} + +func RemoveClusterRoleBinding(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + if err := clientset.RbacV1().ClusterRoleBindings().Delete(context.Background(), projectName+"-"+role, metav1.DeleteOptions{}); err != nil { + log.Error(ctx, err) + } + } + + return nil +} + +func EnsureRoleBinding(ctx context.Context, kubeconfig []byte, projectId string, namespace string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + obj := generateClusterRoleToRoleBinding(role+"@"+projectId, projectId+"-"+role, projectId+"-"+role, namespace) + if _, err = clientset.RbacV1().RoleBindings(namespace).Get(context.Background(), projectId+"-"+role, metav1.GetOptions{}); err != nil { + _, err = clientset.RbacV1().RoleBindings(namespace).Create(context.Background(), obj, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + _, err = clientset.RbacV1().RoleBindings(namespace).Update(context.Background(), obj, metav1.UpdateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } + } + + return nil +} + +func RemoveRoleBinding(ctx context.Context, kubeconfig []byte, projectName string, namespace string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + if err := clientset.RbacV1().RoleBindings(namespace).Delete(context.Background(), projectName+"-"+role, metav1.DeleteOptions{}); err != nil { + log.Error(ctx, err) + } + } + + return nil +} + +const ( + leaderRole = "project-leader" + memberRole = "project-member" + viewerRole = "project-viewer" +) + +func getClusterRole(role, objName string) *rbacV1.ClusterRole { + + clusterRole := rbacV1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: objName, + }, + Rules: []rbacV1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + }, + } + + switch role { + case leaderRole: + clusterRole.Rules[0].Verbs = []string{"*"} + case memberRole: + clusterRole.Rules[0].Verbs = []string{"*"} + case viewerRole: + clusterRole.Rules[0].Verbs = []string{"get", "list", "watch"} + default: + return nil + } + + return &clusterRole +} + +func EnsureNamespace(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + if n, err := clientset.CoreV1().Namespaces().Get(context.Background(), projectName, metav1.GetOptions{}); err != nil { + _, err = clientset.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: projectName, + }, + }, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + log.Info(ctx, "Namespace already exists", n) + } + + return nil +} + +func EnsureCommonClusterRole(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + obj := generateCommonClusterRole(projectName + "-common") + if _, err = clientset.RbacV1().ClusterRoles().Get(context.Background(), projectName+"-common", metav1.GetOptions{}); err != nil { + _, err = clientset.RbacV1().ClusterRoles().Create(context.Background(), obj, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + _, err = clientset.RbacV1().ClusterRoles().Update(context.Background(), obj, metav1.UpdateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } + + return nil +} + +func RemoveCommonClusterRole(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + if err := clientset.RbacV1().ClusterRoles().Delete(context.Background(), projectName+"-common", metav1.DeleteOptions{}); err != nil { + log.Error(ctx, err) + return err + } + + return nil +} + +func EnsureCommonClusterRoleBinding(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + obj := generateClusterRoleToClusterRoleBinding(role+"@"+projectName, projectName+"-common-"+role, projectName+"-common") + if _, err = clientset.RbacV1().ClusterRoleBindings().Get(context.Background(), projectName+"-common"+"-"+role, metav1.GetOptions{}); err != nil { + _, err = clientset.RbacV1().ClusterRoleBindings().Create(context.Background(), obj, metav1.CreateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } else { + _, err = clientset.RbacV1().ClusterRoleBindings().Update(context.Background(), obj, metav1.UpdateOptions{}) + if err != nil { + log.Error(ctx, err) + return err + } + } + } + + return nil +} + +func RemoveCommonClusterRoleBinding(ctx context.Context, kubeconfig []byte, projectName string) error { + config_user, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig) + if err != nil { + log.Error(ctx, err) + return err + } + + clientset := kubernetes.NewForConfigOrDie(config_user) + + for _, role := range []string{leaderRole, memberRole, viewerRole} { + if err := clientset.RbacV1().ClusterRoles().Delete(context.Background(), projectName+"-common-"+role, metav1.DeleteOptions{}); err != nil { + log.Error(ctx, err) + return err + } + } + + return nil +} +func generateCommonClusterRole(objName string) *rbacV1.ClusterRole { + clusterRole := rbacV1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: objName, + }, + Rules: []rbacV1.PolicyRule{ + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{""}, + Resources: []string{"namespaces", "nodes", "storageclasses", "persistentvolumes"}, + }, + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"storage.k8s.io"}, + Resources: []string{"storageclasses"}, + }, + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"apiextensions.k8s.io"}, + Resources: []string{"customresourcedefinitions"}, + }, + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"certificates.k8s.io"}, + Resources: []string{"certificatesigningrequests"}, + }, + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"clusterroles", "clusterrolebindings"}, + }, + }, + } + + return &clusterRole +} + +func generateClusterRoleToClusterRoleBinding(groupName, objName, roleName string) *rbacV1.ClusterRoleBinding { + clusterRoleBinding := rbacV1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: objName, + }, + Subjects: []rbacV1.Subject{ + { + Kind: "Group", + Name: groupName, + }, + }, + RoleRef: rbacV1.RoleRef{ + Kind: "ClusterRole", + Name: roleName, + }, + } + + return &clusterRoleBinding +} + +func generateClusterRoleToRoleBinding(groupName, objName, roleName, namespace string) *rbacV1.RoleBinding { + roleBinding := rbacV1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: objName, + Namespace: namespace, + }, + Subjects: []rbacV1.Subject{ + { + Kind: "Group", + Name: groupName, + }, + }, + RoleRef: rbacV1.RoleRef{ + Kind: "ClusterRole", + Name: roleName, + }, + } + + return &roleBinding +} + +func MergeKubeconfigsWithSingleUser(kubeconfigs []string) (string, error) { + type kubeConfigType struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Clusters []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + } `yaml:"clusters"` + Contexts []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + } `yaml:"contexts"` + + Users []interface{} `yaml:"users,omitempty"` + } + + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + defer encoder.Close() + + encoder.SetIndent(2) + + var config kubeConfigType + var combindConfig kubeConfigType + for _, kc := range kubeconfigs { + err := yaml.Unmarshal([]byte(kc), &config) + if err != nil { + return "", err + } + combindConfig.APIVersion = config.APIVersion + combindConfig.Kind = config.Kind + combindConfig.Clusters = append(combindConfig.Clusters, config.Clusters...) + combindConfig.Contexts = append(combindConfig.Contexts, config.Contexts...) + combindConfig.Users = config.Users + } + + err := encoder.Encode(combindConfig) + if err != nil { + return "", err + } + //modContents, err := yaml.Marshal(combindConfig) + + // write the kubeconfig to a file + err = os.WriteFile("combind-kubeconfig", buf.Bytes(), 0644) + + return buf.String(), err +} diff --git a/pkg/kubernetes/kubernetes_test.go b/pkg/kubernetes/kubernetes_test.go new file mode 100644 index 00000000..647d57a1 --- /dev/null +++ b/pkg/kubernetes/kubernetes_test.go @@ -0,0 +1,427 @@ +package kubernetes_test + +import ( + "context" + "os" + "reflect" + "testing" + + "github.com/openinfradev/tks-api/pkg/kubernetes" + "gopkg.in/yaml.v3" +) + +const path = "/Users/1113433/local_vm_kube/kubeconfig" + +func TestGetResourceApiVersionByClusterId(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + res, err := kubernetes.GetResourceApiVersion(context.Background(), dat, "ClusterRole") + if err != nil { + t.Errorf("GetResourceApiVersion() error = %v", err) + return + } + + t.Logf("GetResourceApiVersion() result = %v", res) + +} + +func TestEnsureClusterRole(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.EnsureClusterRole(context.Background(), dat, "p123") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestEnsureCommonClusterRole(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.EnsureCommonClusterRole(context.Background(), dat, "p123") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestEnsureCommonClusterRoleBinding(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.EnsureCommonClusterRoleBinding(context.Background(), dat, "p123") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestEnsureClusterRoleBinding(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.EnsureClusterRoleBinding(context.Background(), dat, "p123") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestEnsureRoleBinding(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.EnsureRoleBinding(context.Background(), dat, "p123", "test") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestRemoveClusterRoleBinding(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.RemoveClusterRoleBinding(context.Background(), dat, "p123") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestRemoveRoleBinding(t *testing.T) { + dat, err := os.ReadFile(path) + if err != nil { + t.Errorf("ReadFile() error = %v", err) + return + } + + err = kubernetes.RemoveRoleBinding(context.Background(), dat, "p123", "test") + if err != nil { + t.Errorf("EnsureClusterRole() error = %v", err) + return + } +} + +func TestMergeKubeconfigsWithSingleUser(t *testing.T) { + type kubeConfigType struct { + APIVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Clusters []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + } `yaml:"clusters"` + Contexts []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + } `yaml:"contexts"` + + Users []interface{} `yaml:"users,omitempty"` + } + + inputObjs := []kubeConfigType{ + { + APIVersion: "v1", + Kind: "Config", + Clusters: []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + }{ + { + Name: "test-cluster1", + Cluster: struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + }{ + Server: "https://10.0.0.1:6443", + CertificateAuthorityData: "test==", + }, + }, + }, + Contexts: []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + }{ + { + Name: "oidc-user@test-cluster1", + Context: struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + }{ + Cluster: "test-cluster1", + User: "oidc-user", + Namespace: "test-namespaces", + }, + }, + }, + Users: []interface{}{ + map[string]interface{}{ + "name": "oidc-user", + "user": map[string]interface{}{ + "exec": map[string]interface{}{ + "apiVersion": "client.authentication.k8s.io/v1beta1", + "args": []string{ + "oidc-login", + "get-token", + "--oidc-issuer-url=https://idp-domain/auth", + "--oidc-client-id=k8s-api", + "--grant-type=password", + }, + "command": "kubectl", + "env": nil, + "interactiveMode": "IfAvailable", + "provideClusterInfo": false, + }, + }, + }, + }, + }, + { + APIVersion: "v1", + Kind: "Config", + Clusters: []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + }{ + { + Name: "test-cluster2", + Cluster: struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + }{ + Server: "https://10.0.0.2:6443", + CertificateAuthorityData: "test2==", + }, + }, + }, + Contexts: []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + }{ + { + Name: "oidc-user@test-cluster2", + Context: struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + }{ + Cluster: "test-cluster2", + User: "oidc-user", + }, + }, + }, + Users: []interface{}{ + map[string]interface{}{ + "name": "oidc-user", + "user": map[string]interface{}{ + "exec": map[string]interface{}{ + "apiVersion": "client.authentication.k8s.io/v1beta1", + "args": []string{ + "oidc-login", + "get-token", + "--oidc-issuer-url=https://idp-domain/auth", + "--oidc-client-id=k8s-api", + "--grant-type=password", + }, + "command": "kubectl", + "env": nil, + "interactiveMode": "IfAvailable", + "provideClusterInfo": false, + }, + }, + }, + }, + }, + } + + expected := kubeConfigType{ + APIVersion: "v1", + Kind: "Config", + Clusters: []struct { + Name string `yaml:"name"` + Cluster struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + } `yaml:"cluster"` + }{ + { + Name: "test-cluster1", + Cluster: struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + }{ + Server: "https://10.0.0.1:6443", + CertificateAuthorityData: "test==", + }, + }, + { + Name: "test-cluster2", + Cluster: struct { + Server string `yaml:"server"` + CertificateAuthorityData string `yaml:"certificate-authority-data,omitempty"` + }{ + Server: "https://10.0.0.2:6443", + CertificateAuthorityData: "test2==", + }, + }, + }, + Contexts: []struct { + Name string `yaml:"name"` + Context struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + } `yaml:"context"` + }{ + { + Name: "oidc-user@test-cluster1", + Context: struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + }{ + Cluster: "test-cluster1", + User: "oidc-user", + Namespace: "test-namespaces", + }, + }, + { + Name: "oidc-user@test-cluster2", + Context: struct { + Cluster string `yaml:"cluster"` + User string `yaml:"user"` + Namespace string `yaml:"namespace,omitempty"` + }{ + Cluster: "test-cluster2", + User: "oidc-user", + }, + }, + }, + + Users: []interface{}{ + map[string]interface{}{ + "name": "oidc-user2", + "user": map[string]interface{}{ + "exec": map[string]interface{}{ + "apiVersion": "client.authentication.k8s.io/v1beta1", + "args": []string{ + "oidc-login", + "get-token", + "--oidc-issuer-url=https://idp-domain/auth", + "--oidc-client-id=k8s-api", + "--grant-type=password", + }, + "command": "kubectl", + "env": nil, + "interactiveMode": "IfAvailable", + "provideClusterInfo": false, + }, + }, + }, + }, + } + + in := make([]string, len(inputObjs)) + for _, v := range inputObjs { + o, err := yaml.Marshal(&v) + if err != nil { + t.Error(err) + } + in = append(in, string(o)) + } + r, err := kubernetes.MergeKubeconfigsWithSingleUser(in) + t.Log(r) + if err != nil { + t.Error(err) + } + + var result kubeConfigType + if err := yaml.Unmarshal([]byte(r), &result); err != nil { + t.Error(err) + } + + { + if result.APIVersion != expected.APIVersion { + t.Errorf("expected: %s, got: %s", expected.APIVersion, result.APIVersion) + } + if result.Kind != expected.Kind { + t.Errorf("expected: %s, got: %s", expected.Kind, result.Kind) + } + for i, v := range result.Clusters { + if v.Name != expected.Clusters[i].Name { + t.Errorf("expected: %s, got: %s", expected.Clusters[i].Name, v.Name) + } + if v.Cluster.Server != expected.Clusters[i].Cluster.Server { + t.Errorf("expected: %s, got: %s", expected.Clusters[i].Cluster.Server, v.Cluster.Server) + } + if v.Cluster.CertificateAuthorityData != expected.Clusters[i].Cluster.CertificateAuthorityData { + t.Errorf("expected: %s, got: %s", expected.Clusters[i].Cluster.CertificateAuthorityData, v.Cluster.CertificateAuthorityData) + } + } + for i, v := range result.Contexts { + if v.Name != expected.Contexts[i].Name { + t.Errorf("expected: %s, got: %s", expected.Contexts[i].Name, v.Name) + } + if v.Context.Cluster != expected.Contexts[i].Context.Cluster { + t.Errorf("expected: %s, got: %s", expected.Contexts[i].Context.Cluster, v.Context.Cluster) + } + if v.Context.User != expected.Contexts[i].Context.User { + t.Errorf("expected: %s, got: %s", expected.Contexts[i].Context.User, v.Context.User) + } + if v.Context.Namespace != expected.Contexts[i].Context.Namespace { + t.Errorf("expected: %s, got: %s", expected.Contexts[i].Context.Namespace, v.Context.Namespace) + } + } + + //ToDo: This test case down below results in true negative. Need to fix the test case. + if reflect.DeepEqual(result.Users, expected.Users) { + t.Errorf("expected: %v, got: %v", expected.Users, result.Users) + } + } +} diff --git a/pkg/log/log.go b/pkg/log/log.go index 5ad235dd..d48d133e 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -2,8 +2,11 @@ package log import ( "context" + "fmt" "io" "os" + "runtime" + "strconv" "strings" "github.com/openinfradev/tks-api/internal" @@ -18,10 +21,12 @@ var ( func init() { logger = logrus.New() logger.Out = os.Stdout - formatter := new(logrus.TextFormatter) - formatter.FullTimestamp = true - formatter.TimestampFormat = "2006-01-02 15:04:05" - logger.SetFormatter(formatter) + logger.SetFormatter(&CustomFormatter{&logrus.TextFormatter{ + FullTimestamp: true, + TimestampFormat: "2006-01-02 15:04:05", + DisableQuote: true, + }}) + //logger.SetReportCaller(true) logLevel := strings.ToLower(os.Getenv("LOG_LEVEL")) switch logLevel { @@ -40,74 +45,221 @@ func init() { } } -// [TODO] more pretty -func Info(v ...interface{}) { - logger.Info(v...) -} -func Infof(format string, v ...interface{}) { - logger.Infof(format, v...) -} -func InfoWithContext(ctx context.Context, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Info(v...) -} -func InfofWithContext(ctx context.Context, format string, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Infof(format, v...) +type CustomFormatter struct { + logrus.Formatter } -func Warn(v ...interface{}) { - logger.Warn(v...) -} -func Warnf(format string, v ...interface{}) { - logger.Warnf(format, v...) +var colors = map[logrus.Level]string{ + logrus.DebugLevel: "\033[36m", // Cyan + logrus.InfoLevel: "\033[32m", // Green + logrus.WarnLevel: "\033[33m", // Yellow + logrus.ErrorLevel: "\033[31m", // Red + logrus.FatalLevel: "\033[31m", // Red + logrus.PanicLevel: "\033[31m", // Red } -func WarnWithContext(ctx context.Context, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Warn(v...) + +func getColor(level logrus.Level) string { + return colors[level] } -func WarnfWithContext(ctx context.Context, format string, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Warnf(format, v...) + +func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) { + levelColor := getColor(entry.Level) + resetColor := "\033[0m" + levelText := strings.ToUpper(entry.Level.String()) + + requestIDColorStart := "\033[34m" // 파란색 시작 + requestIDColorEnd := "\033[0m" // 색상 리셋 + + requestID := entry.Data[string(internal.ContextKeyRequestID)] + if requestID == nil { + requestID = "Unknown" + } else { + requestID = fmt.Sprintf("%s%v%s", requestIDColorStart, requestID, requestIDColorEnd) + } + file := entry.Data["file"] + if file == nil { + file = "-" + } + var logMessage string + + if file == "-" { + logMessage = fmt.Sprintf("%s%-7s%s %s %sREQUEST_ID=%v%s msg=%s\n", + levelColor, levelText, resetColor, + entry.Time.Format("2006-01-02 15:04:05"), + requestIDColorStart, requestID, requestIDColorEnd, + entry.Message, + ) + } else { + logMessage = fmt.Sprintf("%s%-7s%s %s %sREQUEST_ID=%v%s msg=%s file= %v\n", + levelColor, levelText, resetColor, + entry.Time.Format("2006-01-02 15:04:05"), + requestIDColorStart, requestID, requestIDColorEnd, + entry.Message, + file, + ) + } + + return []byte(logMessage), nil } -func Debug(v ...interface{}) { - logger.Debug(v...) +// [TODO] more pretty +func Info(ctx context.Context, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Info(v...) } -func Debugf(format string, v ...interface{}) { - logger.Debugf(format, v...) +func Infof(ctx context.Context, format string, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Infof(format, v...) } -func DebugWithContext(ctx context.Context, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Debug(v...) + +func Warn(ctx context.Context, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Warn(v...) } -func DebugfWithContext(ctx context.Context, format string, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Debugf(format, v...) +func Warnf(ctx context.Context, format string, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Warnf(format, v...) } -func Error(v ...interface{}) { - logger.Error(v...) +func Debug(ctx context.Context, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Debug(v...) } -func Errorf(format string, v ...interface{}) { - logger.Errorf(format, v...) +func Debugf(ctx context.Context, format string, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Debugf(format, v...) } -func ErrorWithContext(ctx context.Context, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Error(v...) + +func Error(ctx context.Context, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Error(v...) } -func ErrorfWithContext(ctx context.Context, format string, v ...interface{}) { - reqID := ctx.Value(internal.ContextKeyRequestID) - logger.WithField(string(internal.ContextKeyRequestID), reqID).Errorf(format, v...) +func Errorf(ctx context.Context, format string, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Errorf(format, v...) } -func Fatal(v ...interface{}) { - logger.Fatal(v...) +func Fatal(ctx context.Context, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Fatal(v...) } -func Fatalf(format string, v ...interface{}) { - logger.Fatalf(format, v...) +func Fatalf(ctx context.Context, format string, v ...interface{}) { + fields := logrus.Fields{} + + if _, file, line, ok := runtime.Caller(1); ok { + relativePath := getRelativeFilePath(file) + fields["file"] = relativePath + ":" + strconv.Itoa(line) + } + + if ctx != nil { + fields[string(internal.ContextKeyRequestID)] = ctx.Value(internal.ContextKeyRequestID) + } + + logger.WithFields(fields).Fatalf(format, v...) } func Disable() { logger.Out = io.Discard } + +func getRelativeFilePath(absolutePath string) string { + wd, err := os.Getwd() + if err != nil { + return absolutePath + } + + relativePath := strings.TrimPrefix(absolutePath, wd) + relativePath = strings.TrimPrefix(relativePath, "/") + + return relativePath +} diff --git a/pkg/thanos-client/client.go b/pkg/thanos-client/client.go index 1a6994a5..4dcf93e5 100644 --- a/pkg/thanos-client/client.go +++ b/pkg/thanos-client/client.go @@ -1,6 +1,7 @@ package thanos import ( + "context" "encoding/json" "fmt" "io" @@ -13,8 +14,12 @@ import ( ) type ThanosClient interface { - Get(query string) (Metric, error) - FetchRange(query string, start int, end int, step int) (out Metric, err error) + Get(ctx context.Context, query string) (Metric, error) + FetchRange(ctx context.Context, query string, start int, end int, step int) (out Metric, err error) + GetWorkload(ctx context.Context, query string) (WorkloadMetric, error) + FetchPolicyRange(ctx context.Context, query string, start int, end int, step int) (*PolicyMetric, error) + FetchPolicyTemplateRange(ctx context.Context, query string, start int, end int, step int) (*PolicyTemplateMetric, error) + FetchPolicyViolationCountRange(ctx context.Context, query string, start int, end int, step int) (pvcm *PolicyViolationCountMetric, err error) } type ThanosClientImpl struct { @@ -22,12 +27,12 @@ type ThanosClientImpl struct { url string } -// New +// New function func New(host string, port int, ssl bool, token string) (ThanosClient, error) { var baseUrl string if ssl { if token == "" { - return nil, fmt.Errorf("thanos ssl enabled but token is empty.") + return nil, fmt.Errorf("thanos ssl enabled but token is empty") } baseUrl = fmt.Sprintf("%s:%d", host, port) } else { @@ -44,24 +49,24 @@ func New(host string, port int, ssl bool, token string) (ThanosClient, error) { }, nil } -func (c *ThanosClientImpl) Get(query string) (out Metric, err error) { - url := c.url + "/api/v1/query?query=" + url.QueryEscape(query) +func (c *ThanosClientImpl) Get(ctx context.Context, query string) (out Metric, err error) { + reqUrl := c.url + "/api/v1/query?query=" + url.QueryEscape(query) - log.Info("url : ", url) - res, err := c.client.Get(url) + log.Info(ctx, "url : ", reqUrl) + res, err := c.client.Get(reqUrl) if err != nil { return out, err } if res == nil { - return out, fmt.Errorf("Failed to call thanos.") + return out, fmt.Errorf("failed to call thanos") } if res.StatusCode != 200 { - return out, fmt.Errorf("Invalid http status. return code: %d", res.StatusCode) + return out, fmt.Errorf("invalid http status. return code: %d", res.StatusCode) } defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -75,30 +80,51 @@ func (c *ThanosClientImpl) Get(query string) (out Metric, err error) { return out, err } - log.Info(helper.ModelToJson(out)) + log.Info(ctx, helper.ModelToJson(out)) return } -func (c *ThanosClientImpl) FetchRange(query string, start int, end int, step int) (out Metric, err error) { - rangeParam := fmt.Sprintf("&dedup=true&partial_response=false&start=%d&end=%d&step=%d&max_source_resolution=0s", start, end, step) - query = url.QueryEscape(query) + rangeParam - url := c.url + "/api/v1/query_range?query=" + query +func (c *ThanosClientImpl) FetchRange(ctx context.Context, query string, start int, end int, step int) (out Metric, err error) { + body, err := c.fetchRange(ctx, query, start, end, step) + if err != nil { + return out, err + } + + /* + var a interface{} + err = json.Unmarshal(body, &a) + if err != nil { + return out, err + } + log.Info(helper.ModelToJson(a)) + */ - log.Info("url : ", url) - res, err := c.client.Get(url) + err = json.Unmarshal(body, &out) + if err != nil { + return out, err + } + + return +} + +func (c *ThanosClientImpl) GetWorkload(ctx context.Context, query string) (out WorkloadMetric, err error) { + reqUrl := c.url + "/api/v1/query?query=" + url.QueryEscape(query) + + log.Info(ctx, "url : ", reqUrl) + res, err := c.client.Get(reqUrl) if err != nil { return out, err } if res == nil { - return out, fmt.Errorf("Failed to call thanos.") + return out, fmt.Errorf("failed to call thanos") } if res.StatusCode != 200 { - return out, fmt.Errorf("Invalid http status. return code: %d", res.StatusCode) + return out, fmt.Errorf("invalid http status. return code: %d", res.StatusCode) } defer func() { if err := res.Body.Close(); err != nil { - log.Error("error closing http body") + log.Error(ctx, "error closing http body") } }() @@ -107,19 +133,83 @@ func (c *ThanosClientImpl) FetchRange(query string, start int, end int, step int return out, err } - /* - var a interface{} - err = json.Unmarshal(body, &a) - if err != nil { - return out, err - } - log.Info(helper.ModelToJson(a)) - */ - err = json.Unmarshal(body, &out) if err != nil { return out, err } + log.Info(ctx, helper.ModelToJson(out)) return } + +func (c *ThanosClientImpl) FetchPolicyRange(ctx context.Context, query string, start int, end int, step int) (pm *PolicyMetric, err error) { + body, err := c.fetchRange(ctx, query, start, end, step) + if err != nil { + return nil, err + } + + err = json.Unmarshal(body, &pm) + if err != nil { + return nil, err + } + + return pm, nil +} + +func (c *ThanosClientImpl) FetchPolicyTemplateRange(ctx context.Context, query string, start int, end int, step int) (ptm *PolicyTemplateMetric, err error) { + body, err := c.fetchRange(ctx, query, start, end, step) + if err != nil { + return nil, err + } + + err = json.Unmarshal(body, &ptm) + if err != nil { + return nil, err + } + + return ptm, nil +} + +func (c *ThanosClientImpl) FetchPolicyViolationCountRange(ctx context.Context, query string, start int, end int, step int) (pvcm *PolicyViolationCountMetric, err error) { + body, err := c.fetchRange(ctx, query, start, end, step) + if err != nil { + return nil, err + } + + err = json.Unmarshal(body, &pvcm) + if err != nil { + return nil, err + } + + return pvcm, nil +} + +func (c *ThanosClientImpl) fetchRange(ctx context.Context, query string, start int, end int, step int) ([]byte, error) { + rangeParam := fmt.Sprintf("&dedup=true&partial_response=false&start=%d&end=%d&step=%d&max_source_resolution=0s", start, end, step) + query = url.QueryEscape(query) + rangeParam + requestUrl := c.url + "/api/v1/query_range?query=" + query + + res, err := c.client.Get(requestUrl) + if err != nil { + return nil, err + } + if res == nil { + return nil, fmt.Errorf("failed to call thanos") + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("invalid http status. return code: %d", res.StatusCode) + } + + defer func() { + if err := res.Body.Close(); err != nil { + log.Error(ctx, "error closing http body") + } + }() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return body, nil +} diff --git a/pkg/thanos-client/types.go b/pkg/thanos-client/types.go index 6f41cbe3..3d6c7c80 100644 --- a/pkg/thanos-client/types.go +++ b/pkg/thanos-client/types.go @@ -18,5 +18,66 @@ type MetricDataResult struct { type MetricDataResultMetric struct { Name string `json:"__name__"` + Namespace string `json:"namespace,omitempty"` TacoCluster string `json:"taco_cluster"` } + +// PolicyMetric dedicated policy metric struct +type PolicyMetric struct { + Data PolicyMetricData `json:"data"` + Status string `json:"status"` +} + +type PolicyMetricData struct { + Result []PolicyMetricResult `json:"result"` + ResultType string `json:"resultType"` +} + +type PolicyMetricResult struct { + Metric PolicyMetricDataResultMetric `json:"metric"` + Value []interface{} `json:"value"` +} + +type PolicyMetricDataResultMetric struct { + Kind string `json:"kind"` + Name string `json:"name"` + Violation string `json:"violation_enforcement"` +} + +type WorkloadMetric struct { + Status string `json:"status"` + Data struct { + ResultType string `json:"resultType"` + Result []struct { + Metric struct { + } `json:"metric"` + Value []interface{} `json:"value"` + } `json:"result"` + } `json:"data"` +} + +type PolicyTemplateMetric struct { + Status string `json:"status"` + Data struct { + ResultType string `json:"resultType"` + Result []struct { + Metric struct { + Kind string `json:"kind"` + } `json:"metric"` + Value []interface{} `json:"value"` + } `json:"result"` + } `json:"data"` +} + +type PolicyViolationCountMetric struct { + Status string `json:"status"` + Data struct { + ResultType string `json:"resultType"` + Result []struct { + Metric struct { + ViolationEnforcement string `json:"violation_enforcement,omitempty"` + } `json:"metric"` + Value []interface{} `json:"value"` + } `json:"result"` + } `json:"data"` +} diff --git a/scripts/erd_postgres.sql b/scripts/erd_postgres.sql index da611fb8..95b5b5b5 100644 --- a/scripts/erd_postgres.sql +++ b/scripts/erd_postgres.sql @@ -387,4 +387,56 @@ ALTER TABLE IF EXISTS public.users ON UPDATE NO ACTION ON DELETE NO ACTION; + +-- Project table Start +CREATE TABLE IF NOT EXISTS public.projects ( + id text primary key not null, + organization_id text, + name text, + description text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + deleted_at timestamp with time zone +); +CREATE INDEX idx_projects_name ON projects USING btree (name); + +CREATE TABLE IF NOT EXISTS public.project_members ( + id text primary key not null, + project_id text not null, + user_id text, + project_role_id text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + deleted_at timestamp with time zone, + foreign key (project_id) references public.projects (id) + match simple on update no action on delete no action, + foreign key (project_role_id) references public.project_roles (id) + match simple on update no action on delete no action +); + +CREATE TABLE IF NOT EXISTS public.project_namespaces ( + id text primary key not null, + project_id text not null, + stack_id text, + namespace text, + description text, + status text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + deleted_at timestamp with time zone, + foreign key (project_id) references public.projects (id) + match simple on update no action on delete no action +); +CREATE UNIQUE INDEX idx_stackid_namespace ON project_namespaces USING btree (stack_id, namespace); + +CREATE TABLE IF NOT EXISTS public.project_roles ( + id text primary key not null, + name text, + description text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + deleted_at timestamp with time zone +); +-- Project table End + END; \ No newline at end of file diff --git a/scripts/init_postgres.sql b/scripts/init_postgres.sql index 17e33296..df183860 100644 --- a/scripts/init_postgres.sql +++ b/scripts/init_postgres.sql @@ -1,3 +1,6 @@ +ALTER DATABASE tks SET timezone = 'Asia/Seoul'; + +## Roles insert into roles ( id, name, description, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'tks-admin', 'tks-admin', now(), now() ); insert into roles ( id, name, description, created_at, updated_at ) values ( 'b2b689f0-ceeb-46c2-b280-0bc06896acd1', 'admin', 'admin', now(), now() ); insert into roles ( id, name, description, created_at, updated_at ) values ( 'd3015140-2b12-487a-9516-cdeed7c17735', 'project-leader', 'project-leader', now(), now() ); @@ -5,6 +8,7 @@ insert into roles ( id, name, description, created_at, updated_at ) values ( 'f6 insert into roles ( id, name, description, created_at, updated_at ) values ( 'b7ac7e7d-d8bc-470d-b6b2-3e0cc8ba55cc', 'project-viewer', 'project-viewer', now(), now() ); insert into roles ( id, name, description, created_at, updated_at ) values ( 'ff4187a2-f3c1-46b3-8448-03a4b5e132e7', 'user', 'user', now(), now() ); +## Policies insert into policies ( role_id, name, description, c, create_priviledge, u, update_priviledge, r, read_priviledge, d, delete_priviledge, creator, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'organization', 'organization', 't', '', 't', '', 't', '', 't', '', '', now(), now() ); insert into policies ( role_id, name, description, c, create_priviledge, u, update_priviledge, r, read_priviledge, d, delete_priviledge, creator, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'project', 'project', 't', '', 't', '', 't', '', 't', '', '', now(), now() ); insert into policies ( role_id, name, description, c, create_priviledge, u, update_priviledge, r, read_priviledge, d, delete_priviledge, creator, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'user', 'user', 't', '', 't', '', 't', '', 't', '', '', now(), now() ); @@ -12,30 +16,111 @@ insert into policies ( role_id, name, description, c, create_priviledge, u, upda insert into policies ( role_id, name, description, c, create_priviledge, u, update_priviledge, r, read_priviledge, d, delete_priviledge, creator, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'service', 'service', 't', '', 't', '', 't', '', 't', '', '', now(), now() ); insert into policies ( role_id, name, description, c, create_priviledge, u, update_priviledge, r, read_priviledge, d, delete_priviledge, creator, created_at, updated_at ) values ( '2ea4415c-9748-493f-91ba-4a64506b7be8', 'k8s_resources', 'resources of k8s', 'f', '', 'f', '', 'f', '', 'f', '', '', now(), now() ); +## Organizations insert into organizations ( id, name, description, created_at, updated_at ) values ( 'master', 'master', 'tks', now(), now() ); -insert into users ( id, account_id, name, password, organization_id, role_id, created_at, updated_at ) values ( 'bf67de40-ce15-4dc0-b6c2-17f053ca504f', 'admin', 'admin', '$2a$10$Akf03nbLHk93sTtozm35XuINXkJeNX7A1T9o/Pxpg9R2B2PToBPOO', 'master', 'b2b689f0-ceeb-46c2-b280-0bc06896acd1', now(), now() ); - -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '49901092-be76-4d4f-94e9-b84525f560b5', 'master', 'AWS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'aws-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '44d5e76b-63db-4dd0-a16e-11bd3f6054cf', 'master', 'AWS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'aws-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( 'fe1d97e0-7428-4be6-9c69-310a88b4ff46', 'master', 'AWS Standard (arm)', 'included LMA', 'v2', 'AWS', 'arm', 'aws-arm-reference', 'STANDARD', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '3696cb38-4da0-4235-97eb-b6eb15962bd1', 'master', 'AWS Standard (arm)', 'included LMA, SERVICE_MESH', 'v2', 'AWS', 'arm', 'aws-arm-msa-reference', 'MSA', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( 'c8a4658d-d5a6-4191-8a91-e26f6aee007f', 'master', 'EKS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '39f18a09-5b94-4772-bdba-e4c32ee002f7', 'master', 'EKS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '5678bf11-256f-4d2c-a673-f2fedb82de5b', 'master', 'BYOH Standard', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '92f5e5ce-7ffd-4c3e-aff6-9b7fb03dd881', 'master', 'BYOH MSA Standard', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); +## Users +insert into users ( id, account_id, name, organization_id, created_at, updated_at ) values ( 'bf67de40-ce15-4dc0-b6c2-17f053ca504f', 'admin', 'admin', 'master', now(), now() ); + +## StackTemplates +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '49901092-be76-4d4f-94e9-b84525f560b5', 'AWS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'aws-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '44d5e76b-63db-4dd0-a16e-11bd3f6054cf', 'AWS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'aws-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'fe1d97e0-7428-4be6-9c69-310a88b4ff46', 'AWS Standard (arm)', 'included LMA', 'v2', 'AWS', 'arm', 'aws-arm-reference', 'STANDARD', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '3696cb38-4da0-4235-97eb-b6eb15962bd1', 'AWS Standard (arm)', 'included LMA, SERVICE_MESH', 'v2', 'AWS', 'arm', 'aws-arm-msa-reference', 'MSA', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}, {"name": "MSA", "type": "SERVICE_MESH", "applications": [{"name": "istio", "version": "v1.13.1", "description": "MSA 플랫폼"}, {"name": "jagger", "version": "v2.27.1", "description": "분산 서비스간 트랜잭션 추적을 위한 로깅 플랫폼"}, {"name": "kiali", "version": "v1.45.1", "description": "MSA 통합 모니터링포탈"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'c8a4658d-d5a6-4191-8a91-e26f6aee007f', 'EKS Standard (x86)', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '39f18a09-5b94-4772-bdba-e4c32ee002f7', 'EKS MSA Standard (x86)', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '5678bf11-256f-4d2c-a673-f2fedb82de5b', 'BYOH Standard', 'included LMA', 'v1', 'AWS', 'x86', 'eks-reference', 'STANDARD', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '92f5e5ce-7ffd-4c3e-aff6-9b7fb03dd881', 'BYOH MSA Standard', 'included LMA, SERVICE MESH', 'v1', 'AWS', 'x86', 'eks-msa-reference', 'MSA', 'v1.25', 'AWS', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]},{"name":"MSA","type":"SERVICE_MESH","applications":[{"name":"istio","version":"v1.17.2","description":"MSA 플랫폼"},{"name":"jagger","version":"1.35.0","description":"분산 서비스간 트랜잭션 추적을 위한 플랫폼"},{"name":"kiali","version":"v1.63.0","description":"MSA 구조 및 성능을 볼 수 있는 Dashboard"},{"name":"k8ssandra","version":"1.6.0","description":"분산 서비스간 호출 로그를 저장하는 스토리지"}]}]' ); # BTV -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( '2526ec49-28a2-4be9-8d18-2c39fc0993fd', 'master', 'BYOH Admin Standard (BTV)', 'included LMA', 'v1', 'BYOH', 'x86', 'tks-admin', 'STANDARD', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( 'a76b5c97-7d55-46d8-9248-9952bfaff62c', 'master', 'BYOH Standard (BTV SSU)', 'included LMA', 'v1', 'BYOH', 'x86', 'byoh-ssu-reference', 'MSA', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); -insert into stack_templates ( id, organization_id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) -values ( 'b5bbd6ea-5bf3-4d88-bb06-4a4c64c73c15', 'master', 'BYOH Standard (BTV SUY)', 'included LMA', 'v1', 'BYOH', 'x86', 'byoh-suy-reference', 'MSA', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '2526ec49-28a2-4be9-8d18-2c39fc0993fd', 'BYOH Admin Standard (BTV)', 'included LMA', 'v1', 'BYOH', 'x86', 'tks-admin', 'STANDARD', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'a76b5c97-7d55-46d8-9248-9952bfaff62c', 'BYOH MSA Standard (BTV SSU)', 'included LMA', 'v1', 'BYOH', 'x86', 'byoh-ssu-reference', 'MSA', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'b5bbd6ea-5bf3-4d88-bb06-4a4c64c73c15', 'BYOH MSA Standard (BTV SUY)', 'included LMA', 'v1', 'BYOH', 'x86', 'byoh-suy-reference', 'MSA', 'v1.25', 'BYOH', now(), now(), '[{"name":"Logging,Monitoring,Alerting","type":"LMA","applications":[{"name":"thanos","version":"0.30.2","description":"다중클러스터의 모니터링 데이터 통합 질의처리"},{"name":"prometheus-stack","version":"v0.66.0","description":"모니터링 데이터 수집/저장 및 질의처리"},{"name":"alertmanager","version":"v0.25.0","description":"알람 처리를 위한 노티피케이션 서비스"},{"name":"loki","version":"2.6.1","description":"로그데이터 저장 및 질의처리"},{"name":"grafana","version":"8.3.3","description":"모니터링/로그 통합대시보드"}]}]' ); +# PSNM +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( 'c3396c68-03ec-4d41-991c-69e4a2ac16aa', 'psnm-backend-reference', 'included LMA', 'v1', 'AWS', 'x86', 'psnm-backend-reference', 'STANDARD', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); +insert into stack_templates ( id, name, description, version, cloud_service, platform, template, template_type, kube_version, kube_type, created_at, updated_at, services ) +values ( '23b07a65-1cb3-4609-9bba-e88c15e2e192', 'psnm-frontend-reference', 'included LMA', 'v1', 'AWS', 'x86', 'psnm-frontend-reference', 'STANDARD', 'v1.25', 'EKS', now(), now(), '[{"name": "Logging,Monitoring,Alerting", "type": "LMA", "applications": [{"name": "prometheus-stack", "version": "v.44.3.1", "description": "통계데이터 제공을 위한 backend 플랫폼"}, {"name": "elastic-system", "version": "v1.8.0", "description": "로그 데이터 적재를 위한 Storage"}, {"name": "alertmanager", "version": "v0.23.0", "description": "Alert 관리를 위한 backend 서비스"}, {"name": "grafana", "version": "v6.50.7", "description": "모니터링 통합 포탈"}]}]' ); + +## Projects +insert into project_roles ( id, name, description, created_at, updated_at ) values ( 'f4358b4e-adc3-447a-8ad9-c111c4b9a974', 'project-leader', 'project-leader', now(), now() ); +insert into project_roles ( id, name, description, created_at, updated_at ) values ( '2071bd6f-26b3-4c1a-a3ab-439bc89f0011', 'project-member', 'project-member', now(), now() ); +insert into project_roles ( id, name, description, created_at, updated_at ) values ( 'f62c16e1-316c-4d7f-9cfa-dbe4ed7dfa17', 'project-viewer', 'project-viewer', now(), now() ); + +## SystemNotificationTemplates +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('d42d716f-dd2e-429b-897d-b602f6382790', 'node-cpu-high-load', 'node-cpu-high-load', true, 'SYSTEM_NOTIFICATION', '(avg by (taco_cluster, instance) (rate(node_cpu_seconds_total{mode="idle"}[60s])))', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('f11eefa4-5a16-44fc-8dae-4662e7fba023', 'node-memory-high-utilization', 'node-memory-high-utilization', true, 'SYSTEM_NOTIFICATION', '(node_memory_MemAvailable_bytes/node_memory_MemTotal_bytes)', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('1ec08b58-2fe1-49c5-bbab-3544ec8ce330', 'node-disk-full', 'node-disk-full', true, 'SYSTEM_NOTIFICATION', 'predict_linear(node_filesystem_free_bytes{mountpoint="/"}[6h], 24*3600)', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('68dcb92d-91cc-47d0-9b2f-2285d74f157f', 'pvc-full', 'pvc-full', true, 'SYSTEM_NOTIFICATION','predict_linear(kubelet_volume_stats_available_bytes[6h], 24*3600)', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('46e9e216-364a-4a3f-9182-85b2c4c34f77', 'pod-restart-frequently', 'pod-restart-frequently', true, 'SYSTEM_NOTIFICATION','increase(kube_pod_container_status_restarts_total{namespace!="kube-system"}[60m:])', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('7355d0f9-7c14-4f70-92ea-a9868624ff82', 'policy-audited', 'policy-audited', true, 'POLICY_NOTIFICATION', 'opa_scorecard_constraint_violations{namespace!="kube-system|taco-system|gatekeeper-system", violation_enforcement="warn"}', null, null, now(), now() ); +insert into system_notification_templates ( id, name, description, is_system, notification_type, metric_query, creator_id, updator_id, created_at, updated_at ) +values ('792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'policy-blocked', 'policy-blocked', true, 'POLICY_NOTIFICATION', 'opa_scorecard_constraint_violations{namespace!="kube-system|taco-system|gatekeeper-system",violation_enforcement=""}', null, null, now(), now() ); + +## SystemNotificationTemplates -> SystemNotificationMetricParameters +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, 'd42d716f-dd2e-429b-897d-b602f6382790', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, 'd42d716f-dd2e-429b-897d-b602f6382790', 'INSTANCE', '$labels.instance', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, 'f11eefa4-5a16-44fc-8dae-4662e7fba023', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, 'f11eefa4-5a16-44fc-8dae-4662e7fba023', 'INSTANCE', '$labels.instance', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, '1ec08b58-2fe1-49c5-bbab-3544ec8ce330', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, '1ec08b58-2fe1-49c5-bbab-3544ec8ce330', 'INSTANCE', '$labels.instance', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, '68dcb92d-91cc-47d0-9b2f-2285d74f157f', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, '68dcb92d-91cc-47d0-9b2f-2285d74f157f', 'PVC', '$labels.persistentvolumeclaim', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, '46e9e216-364a-4a3f-9182-85b2c4c34f77', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, '46e9e216-364a-4a3f-9182-85b2c4c34f77', 'POD', '$labels.pod', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 2, '46e9e216-364a-4a3f-9182-85b2c4c34f77', 'NAMESPACE', '$labels.namespace', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'NAME', '$labels.name', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 2, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'KIND', '$labels.kind', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 3, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'VIOLATING_KIND', '$labels.violating_kind', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 4, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'VIOLATING_NAMESPACE', '$labels.violating_namespace', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 5, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'VIOLATING_NAME', '$labels.violating_name', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 6, '7355d0f9-7c14-4f70-92ea-a9868624ff82', 'VIOLATION_MSG', '$labels.violation_msg', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 0, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'STACK', '$labels.taco_cluster', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 1, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'NAME', '$labels.name', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 2, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'KIND', '$labels.kind', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 3, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'VIOLATING_KIND', '$labels.violating_kind', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 4, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'VIOLATING_NAMESPACE', '$labels.violating_namespace', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 5, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'VIOLATING_NAME', '$labels.violating_name', now(), now() ); +insert into system_notification_metric_parameters ( "order", system_notification_template_id, key, value, created_at, updated_at ) +values ( 6, '792ca0c6-b98f-4493-aa17-548de9eb9a4e', 'VIOLATION_MSG', '$labels.violation_msg', now(), now() ); diff --git a/scripts/patch_postgres.sql b/scripts/patch_postgres.sql new file mode 100644 index 00000000..00cdab42 --- /dev/null +++ b/scripts/patch_postgres.sql @@ -0,0 +1,7 @@ +# organization 에 신규로 추가된 admin_id 를 일괄 업데이트 하는 쿼리 +# v.4.0.0 deploy 후 아래 쿼리를 실행할 것 + +UPDATE organizations AS a +SET admin_id = b.id +FROM users b +WHERE b.account_id = 'admin' AND a.id = b.organization_id \ No newline at end of file