diff --git a/api/swagger/docs.go b/api/swagger/docs.go index 3ce6bb87..6980cdfc 100644 --- a/api/swagger/docs.go +++ b/api/swagger/docs.go @@ -526,7 +526,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" } } } @@ -3224,6 +3224,84 @@ const docTemplate = `{ } } }, + "/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" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", + "parameters": [ + { + "type": "string", + "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": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organizationId}/my-profile": { "get": { "security": [ @@ -3423,6 +3501,346 @@ const docTemplate = `{ } } }, + "/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": "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" + }, + "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/{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}/primary-cluster": { "patch": { "security": [ @@ -8735,21 +9153,78 @@ const docTemplate = `{ "name" ], "properties": { - "adminAccountId": { + "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", + "properties": { + "description": { "type": "string" }, - "adminEmail": { + "enforcementAction": { "type": "string" }, - "adminName": { - "type": "string" + "mandatory": { + "type": "boolean" }, - "description": { + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { "type": "string", - "maxLength": 100, - "minLength": 0 + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" }, - "name": { + "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" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { "type": "string" } } @@ -9157,6 +9632,7 @@ const docTemplate = `{ "name": { "type": "string" }, + "organizationIds": { "type": "array", "items": { @@ -9392,9 +9868,6 @@ const docTemplate = `{ } } }, - "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { - "type": "object" - }, "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { "type": "object", "properties": { @@ -9691,6 +10164,17 @@ const docTemplate = `{ } } }, + "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": { @@ -9730,6 +10214,14 @@ const docTemplate = `{ } } }, + "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": { @@ -10156,6 +10648,20 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { "type": "object", "properties": { @@ -10319,6 +10825,104 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "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.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + }, + "is_allowed": { + "type": "boolean" + }, + "key": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse": { + "type": "object", + "properties": { + "configuration": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "dashboard": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "notification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "project_management": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { "type": "object", "properties": { @@ -10560,6 +11164,73 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "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" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { "type": "object", "properties": { @@ -11091,6 +11762,17 @@ const docTemplate = `{ } } }, + "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": { @@ -11945,6 +12627,75 @@ const docTemplate = `{ } } }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { + "type": "object", + "properties": { + "currentTargetClusterIds": { + "type": "array", + "items": { + "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" + ] + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "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" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", "properties": { diff --git a/api/swagger/swagger.json b/api/swagger/swagger.json index 2da9eb49..fa4311e5 100644 --- a/api/swagger/swagger.json +++ b/api/swagger/swagger.json @@ -520,7 +520,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse" + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse" } } } @@ -3218,6 +3218,84 @@ } } }, + "/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" + } + } + } + }, + "patch": { + "security": [ + { + "JWT": [] + } + ], + "description": "템플릿, 정책이 필수 인지 여부를 설정한다.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Policy" + ], + "summary": "[SetMandatoryPolicies] 필수 정책 템플릿, 정책을 설정", + "parameters": [ + { + "type": "string", + "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": { + "200": { + "description": "OK" + } + } + } + }, "/organizations/{organizationId}/my-profile": { "get": { "security": [ @@ -3417,6 +3495,346 @@ } } }, + "/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": "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" + }, + "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/{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}/primary-cluster": { "patch": { "security": [ @@ -8729,21 +9147,78 @@ "name" ], "properties": { - "adminAccountId": { + "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", + "properties": { + "description": { "type": "string" }, - "adminEmail": { + "enforcementAction": { "type": "string" }, - "adminName": { - "type": "string" + "mandatory": { + "type": "boolean" }, - "description": { + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { "type": "string", - "maxLength": 100, - "minLength": 0 + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" }, - "name": { + "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" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.CreatePolicyResponse": { + "type": "object", + "properties": { + "id": { "type": "string" } } @@ -9386,9 +9861,6 @@ } } }, - "github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse": { - "type": "object" - }, "github_com_openinfradev_tks-api_pkg_domain.FilterResponse": { "type": "object", "properties": { @@ -9685,6 +10157,17 @@ } } }, + "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": { @@ -9724,6 +10207,14 @@ } } }, + "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": { @@ -10150,6 +10641,20 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.ListPolicyResponse": { + "type": "object", + "properties": { + "pagination": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PaginationResponse" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.PolicyResponse" + } + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.ListPolicyTemplateResponse": { "type": "object", "properties": { @@ -10313,6 +10818,104 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo": { + "type": "object", + "properties": { + "description": { + "type": "string", + "example": "org 레이블 설정 여부 검사" + }, + "mandatory": { + "type": "boolean" + }, + "policyId": { + "type": "string", + "example": "0091fe9b-e44b-423d-9562-ac2b73089593" + }, + "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.MergePermissionResponse": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + }, + "is_allowed": { + "type": "boolean" + }, + "key": { + "type": "string" + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.MergedPermissionSetResponse": { + "type": "object", + "properties": { + "configuration": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "dashboard": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "notification": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "policy": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "project_management": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + }, + "stack": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.MergePermissionResponse": { "type": "object", "properties": { @@ -10554,6 +11157,73 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.PolicyResponse": { + "type": "object", + "properties": { + "createdAt": { + "type": "string", + "format": "date-time" + }, + "creator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + }, + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "id": { + "type": "string", + "example": "d98ef5f1-4a68-4047-a446-2207787ce3ff" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "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" + }, + "templateName": { + "type": "string", + "example": "필수 Label 검사" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "updator": { + "$ref": "#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse": { "type": "object", "properties": { @@ -11085,6 +11755,17 @@ } } }, + "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": { @@ -11939,6 +12620,75 @@ } } }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyClustersRequest": { + "type": "object", + "properties": { + "currentTargetClusterIds": { + "type": "array", + "items": { + "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" + ] + } + } + }, + "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyRequest": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "enforcementAction": { + "type": "string" + }, + "mandatory": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "example": { + "refer": "match.Match" + } + }, + "parameters": { + "type": "string", + "example": "\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\"" + }, + "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" + } + } + }, "github_com_openinfradev_tks-api_pkg_domain.UpdatePolicyTemplateRequest": { "type": "object", "properties": { diff --git a/api/swagger/swagger.yaml b/api/swagger/swagger.yaml index 527db22c..f22662d2 100644 --- a/api/swagger/swagger.yaml +++ b/api/swagger/swagger.yaml @@ -846,6 +846,45 @@ definitions: - adminEmail - name type: object + github_com_openinfradev_tks-api_pkg_domain.CreatePolicyRequest: + properties: + description: + type: string + enforcementAction: + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + 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 + templateName: + example: 필수 Label 검사 + type: string + 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: @@ -1475,6 +1514,13 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.DashboardStackResponse' type: array 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 github_com_openinfradev_tks-api_pkg_domain.GetMyProfileResponse: properties: user: @@ -1500,6 +1546,11 @@ definitions: organization: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.OrganizationResponse' 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: @@ -1773,6 +1824,15 @@ definitions: 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: @@ -1880,6 +1940,46 @@ definitions: type: array type: object type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyInfo: + properties: + description: + example: org 레이블 설정 여부 검사 + type: string + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + policyName: + example: org 레이블 요구 + type: string + type: object + github_com_openinfradev_tks-api_pkg_domain.MandatoryPolicyPatchInfo: + properties: + mandatory: + type: boolean + policyId: + example: 0091fe9b-e44b-423d-9562-ac2b73089593 + type: string + 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.MergePermissionResponse: properties: children: @@ -2038,6 +2138,53 @@ definitions: value: type: integer type: object + github_com_openinfradev_tks-api_pkg_domain.PolicyResponse: + properties: + createdAt: + format: date-time + type: string + creator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + description: + type: string + enforcementAction: + type: string + id: + example: d98ef5f1-4a68-4047-a446-2207787ce3ff + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + 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 + templateName: + example: 필수 Label 검사 + type: string + updatedAt: + format: date-time + type: string + updator: + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.SimpleUserResponse' + type: object github_com_openinfradev_tks-api_pkg_domain.PolicyTemplateResponse: properties: createdAt: @@ -2394,6 +2541,13 @@ definitions: taskId: type: string type: object + 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: @@ -2960,6 +3114,53 @@ definitions: $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.PermissionResponse' 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: + type: string + mandatory: + type: boolean + match: + additionalProperties: + type: string + example: + refer: match.Match + type: object + parameters: + example: '"labels":{"key":"owner","allowedRegex:^[a-zA-Z]+.agilebank.demo$}"' + 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: @@ -3877,7 +4078,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.ExistsPolicyTemplateKindResponse' + $ref: '#/definitions/github_com_openinfradev_tks-api_pkg_domain.CheckExistedResponse' security: - JWT: [] summary: '[ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인' @@ -5350,6 +5551,55 @@ paths: summary: Update dashboard tags: - Dashboards + /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: @@ -5475,6 +5725,226 @@ paths: 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: 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 + - 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/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}/primary-cluster: patch: consumes: diff --git a/internal/database/database.go b/internal/database/database.go index ebd5639a..4a480a4e 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -176,6 +176,10 @@ func migrateSchema(db *gorm.DB) error { return err } + if err := db.AutoMigrate(&model.Policy{}); err != nil { + return err + } + if err := db.AutoMigrate(&model.Dashboard{}); err != nil { return err } diff --git a/internal/delivery/api/endpoint.go b/internal/delivery/api/endpoint.go index df9fe7da..e740b404 100644 --- a/internal/delivery/api/endpoint.go +++ b/internal/delivery/api/endpoint.go @@ -211,19 +211,19 @@ const ( Admin_GetProjects // PolicyTemplate - ListPolicyTemplate - CreatePolicyTemplate - DeletePolicyTemplate - GetPolicyTemplate - UpdatePolicyTemplate - GetPolicyTemplateDeploy - ListPolicyTemplateStatistics - ListPolicyTemplateVersions - CreatePolicyTemplateVersion - DeletePolicyTemplateVersion - GetPolicyTemplateVersion - ExistsPolicyTemplateKind - ExistsPolicyTemplateName + 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 // ClusterPolicyStatus ListClusterPolicyStatus diff --git a/internal/delivery/api/generated_endpoints.go.go b/internal/delivery/api/generated_endpoints.go.go index 88eb4d7e..9e2c008d 100644 --- a/internal/delivery/api/generated_endpoints.go.go +++ b/internal/delivery/api/generated_endpoints.go.go @@ -635,56 +635,56 @@ var ApiMap = map[Endpoint]EndpointInfo{ Name: "Admin_GetProjects", Group: "Admin Project", }, - ListPolicyTemplate: { - Name: "ListPolicyTemplate", + Admin_ListPolicyTemplate: { + Name: "Admin_ListPolicyTemplate", Group: "PolicyTemplate", }, - CreatePolicyTemplate: { - Name: "CreatePolicyTemplate", + Admin_CreatePolicyTemplate: { + Name: "Admin_CreatePolicyTemplate", Group: "PolicyTemplate", }, - DeletePolicyTemplate: { - Name: "DeletePolicyTemplate", + Admin_DeletePolicyTemplate: { + Name: "Admin_DeletePolicyTemplate", Group: "PolicyTemplate", }, - GetPolicyTemplate: { - Name: "GetPolicyTemplate", + Admin_GetPolicyTemplate: { + Name: "Admin_GetPolicyTemplate", Group: "PolicyTemplate", }, - UpdatePolicyTemplate: { - Name: "UpdatePolicyTemplate", + Admin_UpdatePolicyTemplate: { + Name: "Admin_UpdatePolicyTemplate", Group: "PolicyTemplate", }, - GetPolicyTemplateDeploy: { - Name: "GetPolicyTemplateDeploy", + Admin_GetPolicyTemplateDeploy: { + Name: "Admin_GetPolicyTemplateDeploy", Group: "PolicyTemplate", }, - ListPolicyTemplateStatistics: { - Name: "ListPolicyTemplateStatistics", + Admin_ListPolicyTemplateStatistics: { + Name: "Admin_ListPolicyTemplateStatistics", Group: "PolicyTemplate", }, - ListPolicyTemplateVersions: { - Name: "ListPolicyTemplateVersions", + Admin_ListPolicyTemplateVersions: { + Name: "Admin_ListPolicyTemplateVersions", Group: "PolicyTemplate", }, - CreatePolicyTemplateVersion: { - Name: "CreatePolicyTemplateVersion", + Admin_CreatePolicyTemplateVersion: { + Name: "Admin_CreatePolicyTemplateVersion", Group: "PolicyTemplate", }, - DeletePolicyTemplateVersion: { - Name: "DeletePolicyTemplateVersion", + Admin_DeletePolicyTemplateVersion: { + Name: "Admin_DeletePolicyTemplateVersion", Group: "PolicyTemplate", }, - GetPolicyTemplateVersion: { - Name: "GetPolicyTemplateVersion", + Admin_GetPolicyTemplateVersion: { + Name: "Admin_GetPolicyTemplateVersion", Group: "PolicyTemplate", }, - ExistsPolicyTemplateKind: { - Name: "ExistsPolicyTemplateKind", + Admin_ExistsPolicyTemplateKind: { + Name: "Admin_ExistsPolicyTemplateKind", Group: "PolicyTemplate", }, - ExistsPolicyTemplateName: { - Name: "ExistsPolicyTemplateName", + Admin_ExistsPolicyTemplateName: { + Name: "Admin_ExistsPolicyTemplateName", Group: "PolicyTemplate", }, ListClusterPolicyStatus: { @@ -1126,32 +1126,32 @@ func (e Endpoint) String() string { return "Admin_GetTksRole" case Admin_GetProjects: return "Admin_GetProjects" - 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 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 ListClusterPolicyStatus: return "ListClusterPolicyStatus" case GetClusterPolicyTemplateStatus: @@ -1534,32 +1534,32 @@ func GetEndpoint(name string) Endpoint { return Admin_GetTksRole case "Admin_GetProjects": return Admin_GetProjects - 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 "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 "ListClusterPolicyStatus": return ListClusterPolicyStatus case "GetClusterPolicyTemplateStatus": diff --git a/internal/delivery/http/policy-template.go b/internal/delivery/http/policy-template.go index 5b0dfa3e..bc66d83b 100644 --- a/internal/delivery/http/policy-template.go +++ b/internal/delivery/http/policy-template.go @@ -24,19 +24,19 @@ type PolicyTemplateHandler struct { } type IPolicyTemplateHandler interface { - 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) + 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) RegoCompile(w http.ResponseWriter, r *http.Request) } @@ -46,7 +46,7 @@ func NewPolicyTemplateHandler(u usecase.Usecase) IPolicyTemplateHandler { } } -// CreatePolicyTemplate godoc +// Admin_CreatePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [CreatePolicyTemplate] 정책 템플릿 신규 생성 @@ -57,7 +57,7 @@ func NewPolicyTemplateHandler(u usecase.Usecase) IPolicyTemplateHandler { // @Success 200 {object} domain.CreatePolicyTemplateReponse // @Router /admin/policy-templates [post] // @Security JWT -func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplate(w http.ResponseWriter, r *http.Request) { input := domain.CreatePolicyTemplateRequest{} err := UnmarshalRequestInput(r, &input) @@ -84,7 +84,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, out) } -// UpdatePolicyTemplate godoc +// Admin_UpdatePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [UpdatePolicyTemplate] 정책 템플릿 업데이트 @@ -96,7 +96,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId} [patch] // @Security JWT -func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_UpdatePolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -137,7 +137,7 @@ func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, nil) } -// DeletePolicyTemplate godoc +// Admin_DeletePolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [DeletePolicyTemplate] 정책 템플릿 삭제 @@ -148,7 +148,7 @@ func (h *PolicyTemplateHandler) UpdatePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId} [delete] // @Security JWT -func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -184,7 +184,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *h ResponseJSON(w, r, http.StatusOK, "") } -// GetPolicyTemplate godoc +// Admin_GetPolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplate] 정책 템플릿 조회(최신 버전) @@ -195,7 +195,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplate(w http.ResponseWriter, r *h // @Success 200 {object} domain.GetPolicyTemplateResponse // @Router /admin/policy-templates/{policyTemplateId} [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplate(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -244,7 +244,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplate godoc +// Admin_ListPolicyTemplate godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplate] 정책 템플릿 목록 조회 @@ -259,7 +259,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplate(w http.ResponseWriter, r *http // @Success 200 {object} domain.ListPolicyTemplateResponse // @Router /admin/policy-templates [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplate(w http.ResponseWriter, r *http.Request) { urlParams := r.URL.Query() pg := pagination.NewPagination(&urlParams) @@ -290,7 +290,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *htt ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplateVersions godoc +// Admin_ListPolicyTemplateVersions godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplateVersions] 정책 템플릿 버전목록 조회 @@ -301,7 +301,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplate(w http.ResponseWriter, r *htt // @Success 200 {object} domain.ListPolicyTemplateVersionsResponse // @Router /admin/policy-templates/{policyTemplateId}/versions [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateVersions(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] @@ -338,7 +338,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter ResponseJSON(w, r, http.StatusOK, out) } -// ListPolicyTemplateStatistics godoc +// Admin_ListPolicyTemplateStatistics godoc // // @Tags PolicyTemplate // @Summary [ListPolicyTemplateStatistics] 정책 템플릿 사용 카운트 조회 @@ -349,7 +349,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateVersions(w http.ResponseWriter // @Success 200 {object} domain.ListPolicyTemplateStatisticsResponse // @Router /admin/policy-templates/{policyTemplateId}/statistics [get] // @Security JWT -func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ListPolicyTemplateStatistics(w http.ResponseWriter, r *http.Request) { // result := domain.ListPolicyTemplateStatisticsResponse{ // PolicyTemplateStatistics: []domain.PolicyTemplateStatistics{ // { @@ -367,7 +367,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWrit // util.JsonResponse(w, result) } -// GetPolicyTemplateDeploy godoc +// Admin_GetPolicyTemplateDeploy godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplateDeploy] 정책 템플릿 클러스터 별 설치 버전 조회 @@ -378,7 +378,7 @@ func (h *PolicyTemplateHandler) ListPolicyTemplateStatistics(w http.ResponseWrit // @Success 200 {object} domain.GetPolicyTemplateDeployResponse // @Router /admin/policy-templates/{policyTemplateId}/deploy [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateDeploy(w http.ResponseWriter, r *http.Request) { // c1 := util.UUIDGen() // c2 := util.UUIDGen() // c3 := util.UUIDGen() @@ -393,7 +393,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r // util.JsonResponse(w, result) } -// GetPolicyTemplateVersion godoc +// Admin_GetPolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [GetPolicyTemplateVersion] 정책 템플릿 특정 버전 조회 @@ -405,7 +405,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateDeploy(w http.ResponseWriter, r // @Success 200 {object} domain.GetPolicyTemplateVersionResponse // @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [get] // @Security JWT -func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_GetPolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -450,7 +450,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, ResponseJSON(w, r, http.StatusOK, out) } -// CreatePolicyTemplateVersion godoc +// Admin_CreatePolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [CreatePolicyTemplateVersion] 정책 템플릿 특정 버전 저장 @@ -462,7 +462,7 @@ func (h *PolicyTemplateHandler) GetPolicyTemplateVersion(w http.ResponseWriter, // @Success 200 {object} domain.CreatePolicyTemplateVersionResponse // @Router /admin/policy-templates/{policyTemplateId}/versions [post] // @Security JWT -func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_CreatePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -526,7 +526,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite ResponseJSON(w, r, http.StatusOK, out) } -// DeletePolicyTemplateVersion godoc +// Admin_DeletePolicyTemplateVersion godoc // // @Tags PolicyTemplate // @Summary [DeletePolicyTemplateVersion] 정책 템플릿 특정 버전 삭제 @@ -538,7 +538,7 @@ func (h *PolicyTemplateHandler) CreatePolicyTemplateVersion(w http.ResponseWrite // @Success 200 {object} nil // @Router /admin/policy-templates/{policyTemplateId}/versions/{version} [delete] // @Security JWT -func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_DeletePolicyTemplateVersion(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateId, ok := vars["policyTemplateId"] if !ok { @@ -575,7 +575,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite ResponseJSON(w, r, http.StatusOK, "") } -// ExistsPolicyTemplateName godoc +// Admin_ExistsPolicyTemplateName godoc // // @Tags PolicyTemplate // @Summary [ExistsPolicyTemplateName] 정책 템플릿 아름 존재 여부 확인 @@ -586,7 +586,7 @@ func (h *PolicyTemplateHandler) DeletePolicyTemplateVersion(w http.ResponseWrite // @Success 200 {object} domain.CheckExistedResponse // @Router /admin/policy-templates/name/{policyTemplateName}/existence [get] // @Security JWT -func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateName(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateName, ok := vars["policyTemplateName"] if !ok { @@ -607,7 +607,7 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, ResponseJSON(w, r, http.StatusOK, out) } -// ExistsPolicyTemplateKind godoc +// Admin_ExistsPolicyTemplateKind godoc // // @Tags PolicyTemplate // @Summary [ExistsPolicyTemplateKind] 정책 템플릿 유형 존재 여부 확인 @@ -615,10 +615,10 @@ func (h *PolicyTemplateHandler) ExistsPolicyTemplateName(w http.ResponseWriter, // @Accept json // @Produce json // @Param policyTemplateKind path string true "정책 템플릿 이름" -// @Success 200 {object} domain.ExistsPolicyTemplateKindResponse +// @Success 200 {object} domain.CheckExistedResponse // @Router /admin/policy-templates/kind/{policyTemplateKind}/existence [get] // @Security JWT -func (h *PolicyTemplateHandler) ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { +func (h *PolicyTemplateHandler) Admin_ExistsPolicyTemplateKind(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) policyTemplateKind, ok := vars["policyTemplateKind"] if !ok { diff --git a/internal/delivery/http/policy.go b/internal/delivery/http/policy.go new file mode 100644 index 00000000..972648c8 --- /dev/null +++ b/internal/delivery/http/policy.go @@ -0,0 +1,534 @@ +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" +) + +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) +} + +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 + } + + 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 + } + + 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.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) + } + + ResponseJSON(w, r, http.StatusOK, out) +} + +// ListPolicy godoc +// +// @Tags Policy +// @Summary [ListPolicy] 정책 목록 조회 +// @Description 정책 목록을 조회한다. +// @Accept json +// @Produce json +// @Param organizationId path string true "조직 식별자(o로 시작)" +// @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.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) + + policies, err := h.usecase.Fetch(r.Context(), organizationId, pg) + 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 + } + } + + 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("policyTemplateName 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) +} diff --git a/internal/kubernetes/kubernetes.go b/internal/kubernetes/kubernetes.go index 7559ea26..00555cad 100644 --- a/internal/kubernetes/kubernetes.go +++ b/internal/kubernetes/kubernetes.go @@ -4,16 +4,18 @@ import ( "bytes" "context" "fmt" - "gopkg.in/yaml.v3" "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" @@ -57,6 +59,22 @@ func GetClientAdminCluster(ctx context.Context) (*kubernetes.Clientset, error) { 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 { diff --git a/internal/middleware/audit/audit-map.go b/internal/middleware/audit/audit-map.go index d7c70439..9029b11b 100644 --- a/internal/middleware/audit/audit-map.go +++ b/internal/middleware/audit/audit-map.go @@ -116,7 +116,7 @@ var auditMap = map[internalApi.Endpoint]fnAudit{ } else { return fmt.Sprintf("어드민 [%s]을 생성하는데 실패하였습니다.", input.Name), errorText(ctx, out) } - }, internalApi.CreatePolicyTemplate: func(ctx context.Context, out *bytes.Buffer, in []byte, statusCode int) (message string, description string) { + }, internalApi.Admin_CreatePolicyTemplate: func(ctx context.Context, out *bytes.Buffer, in []byte, statusCode int) (message string, description string) { input := domain.CreatePolicyTemplateRequest{} if err := json.Unmarshal(in, &input); err != nil { log.Error(ctx, err) diff --git a/internal/model/permission.go b/internal/model/permission.go index 87e06b6e..7fafc931 100644 --- a/internal/model/permission.go +++ b/internal/model/permission.go @@ -276,14 +276,14 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.ListPolicyTemplate, - api.GetPolicyTemplate, - api.GetPolicyTemplateDeploy, - api.ListPolicyTemplateStatistics, - api.ListPolicyTemplateVersions, - api.GetPolicyTemplateVersion, - api.ExistsPolicyTemplateName, - api.ExistsPolicyTemplateKind, + api.Admin_ListPolicyTemplate, + api.Admin_GetPolicyTemplate, + api.Admin_GetPolicyTemplateDeploy, + api.Admin_ListPolicyTemplateStatistics, + api.Admin_ListPolicyTemplateVersions, + api.Admin_GetPolicyTemplateVersion, + api.Admin_ExistsPolicyTemplateName, + api.Admin_ExistsPolicyTemplateKind, // ClusterPolicyStatus api.ListClusterPolicyStatus, @@ -317,8 +317,8 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.CreatePolicyTemplate, - api.CreatePolicyTemplateVersion, + api.Admin_CreatePolicyTemplate, + api.Admin_CreatePolicyTemplateVersion, // Policy api.SetMandatoryPolicies, @@ -336,7 +336,7 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.UpdatePolicyTemplate, + api.Admin_UpdatePolicyTemplate, // ClusterPolicyStatus api.UpdateClusterPolicyTemplateStatus, @@ -359,8 +359,8 @@ func newPolicy() *Permission { IsAllowed: helper.BoolP(false), Endpoints: endpointObjects( // PolicyTemplate - api.DeletePolicyTemplate, - api.DeletePolicyTemplateVersion, + api.Admin_DeletePolicyTemplate, + api.Admin_DeletePolicyTemplateVersion, // Policy api.DeletePolicy, diff --git a/internal/model/policy.go b/internal/model/policy.go new file mode 100644 index 00000000..1a05c5ef --- /dev/null +++ b/internal/model/policy.go @@ -0,0 +1,77 @@ +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 + 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"` + 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) { + 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"` +} diff --git a/internal/policy-template/paramdef-util.go b/internal/policy-template/paramdef-util.go new file mode 100644 index 00000000..04ca850c --- /dev/null +++ b/internal/policy-template/paramdef-util.go @@ -0,0 +1,85 @@ +package policytemplate + +import ( + "strings" + + "github.com/openinfradev/tks-api/pkg/domain" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +func ParamDefsToJSONSchemaProeprties(paramdefs []*domain.ParameterDef) *apiextensionsv1.JSONSchemaProps { + if paramdefs == nil { + return nil + } + + result := apiextensionsv1.JSONSchemaProps{Type: "object", Properties: convert(paramdefs)} + + return &result +} + +func convert(paramdefs []*domain.ParameterDef) 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), + }, + } + case isArary: + result[paramdef.Key] = apiextensionsv1.JSONSchemaProps{ + Type: "array", + Items: &apiextensionsv1.JSONSchemaPropsOrArray{ + Schema: &apiextensionsv1.JSONSchemaProps{Type: strings.TrimSuffix(paramdef.Type, "[]")}, + }, + } + case isObject: + result[paramdef.Key] = *ParamDefsToJSONSchemaProeprties(paramdef.Children) + _: + 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{}} + } +} diff --git a/internal/repository/policy.go b/internal/repository/policy.go new file mode 100644 index 00000000..9ceda1aa --- /dev/null +++ b/internal/repository/policy.go @@ -0,0 +1,213 @@ +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/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) + ExistByName(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) +} + +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).Create(&dto).Error + + 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).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("organization_id = ?", organizationId), &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) 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 { + if errors.Is(res.Error, gorm.ErrRecordNotFound) { + log.Infof(ctx, "Not found policy %s='%v'", key, value) + return nil, nil + } else { + 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 err = tx.Model(&model.Policy{}). + Where("organization_id = ? id in ?", organizationId, mandatoryPolicyIds). + Update("mandatory", true).Error; err != nil { + return err + } + + if err = tx.Model(&model.Policy{}). + Where("organization_id = ? id in ?", organizationId, nonMandatoryPolicyIds). + Update("mandatory", false).Error; err != nil { + return err + } + + return nil + }) +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 0114cf59..bee291a5 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -21,6 +21,7 @@ type Repository struct { Project IProjectRepository Audit IAuditRepository PolicyTemplate IPolicyTemplateRepository + Policy IPolicyRepository SystemNotification ISystemNotificationRepository SystemNotificationTemplate ISystemNotificationTemplateRepository SystemNotificationRule ISystemNotificationRuleRepository diff --git a/internal/route/route.go b/internal/route/route.go index bb922dea..36a6fbd4 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -59,6 +59,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Endpoint: repository.NewEndpointRepository(db), Audit: repository.NewAuditRepository(db), PolicyTemplate: repository.NewPolicyTemplateRepository(db), + Policy: repository.NewPolicyRepository(db), Dashboard: repository.NewDashboardRepository(db), } @@ -81,6 +82,7 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa Role: usecase.NewRoleUsecase(repoFactory), Permission: usecase.NewPermissionUsecase(repoFactory), PolicyTemplate: usecase.NewPolicyTemplateUsecase(repoFactory), + Policy: usecase.NewPolicyUsecase(repoFactory), } customMiddleware := internalMiddleware.NewMiddleware( @@ -295,21 +297,32 @@ func SetupRouter(db *gorm.DB, argoClient argowf.ArgoClient, kc keycloak.IKeycloa 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.ListPolicyTemplate, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplate))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates", customMiddleware.Handle(internalApi.CreatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplate))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.DeletePolicyTemplate, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplate))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.GetPolicyTemplate, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplate))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}", customMiddleware.Handle(internalApi.UpdatePolicyTemplate, http.HandlerFunc(policyTemplateHandler.UpdatePolicyTemplate))).Methods(http.MethodPatch) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/deploy ", customMiddleware.Handle(internalApi.GetPolicyTemplateDeploy, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateDeploy))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/statistics", customMiddleware.Handle(internalApi.ListPolicyTemplateStatistics, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateStatistics))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.ListPolicyTemplateVersions, http.HandlerFunc(policyTemplateHandler.ListPolicyTemplateVersions))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions", customMiddleware.Handle(internalApi.CreatePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.CreatePolicyTemplateVersion))).Methods(http.MethodPost) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.DeletePolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.DeletePolicyTemplateVersion))).Methods(http.MethodDelete) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/{policyTemplateId}/versions/{version}", customMiddleware.Handle(internalApi.GetPolicyTemplateVersion, http.HandlerFunc(policyTemplateHandler.GetPolicyTemplateVersion))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/kind/{policyTemplateKind}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateKind, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateKind))).Methods(http.MethodGet) - r.Handle(API_PREFIX+API_VERSION+ADMINAPI_PREFIX+"/policy-templates/name/{policyTemplateName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyTemplateName, http.HandlerFunc(policyTemplateHandler.ExistsPolicyTemplateName))).Methods(http.MethodGet) + 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/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+"/policy-templates/rego-compile", customMiddleware.Handle(internalApi.CompileRego, http.HandlerFunc(policyTemplateHandler.RegoCompile))).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}/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}", 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/{policyId}/clusters", customMiddleware.Handle(internalApi.UpdatePolicyTargetClusters, http.HandlerFunc(policyHandler.UpdatePolicyTargetClusters))).Methods(http.MethodPatch) + r.Handle(API_PREFIX+API_VERSION+"/organizations/{organizationId}/policies/{policyName}/existence", customMiddleware.Handle(internalApi.ExistsPolicyName, http.HandlerFunc(policyHandler.ExistsPolicyName))).Methods(http.MethodGet) + // assets r.PathPrefix("/api/").HandlerFunc(http.NotFound) r.PathPrefix("/").Handler(httpSwagger.WrapHandler).Methods(http.MethodGet) diff --git a/internal/usecase/policy.go b/internal/usecase/policy.go new file mode 100644 index 00000000..4f7da889 --- /dev/null +++ b/internal/usecase/policy.go @@ -0,0 +1,258 @@ +package usecase + +import ( + "context" + "fmt" + + 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" + "github.com/openinfradev/tks-api/internal/repository" + "github.com/openinfradev/tks-api/pkg/domain" + "github.com/openinfradev/tks-api/pkg/httpErrors" +) + +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, 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) + Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]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) + 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) +} + +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 (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.TemplateName) + if err != nil { + return uuid.Nil, err + } + + if exists { + return uuid.Nil, httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "PT_CREATE_ALREADY_EXISTED_NAME", "policy template 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 { + return uuid.Nil, httpErrors.NewBadRequestError(fmt.Errorf("invalid organizationId"), "C_INVALID_ORGANIZATION_ID", "") + } + dto.TargetClusters[i] = cluster + } + + userId := user.GetUserId() + dto.CreatorId = &userId + + id, err := u.repo.Create(ctx, dto) + + if err != nil { + return uuid.Nil, err + } + + return id, 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, targetClusterIds *[]string) (err error) { + + user, ok := request.UserFrom(ctx) + if !ok { + return httpErrors.NewBadRequestError(fmt.Errorf("invalid token"), "A_INVALID_TOKEN", "") + } + + _, err = u.repo.GetByID(ctx, organizationId, policyId) + if err != nil { + return httpErrors.NewNotFoundError(err, "P_FAILED_FETCH_POLICY_TEMPLATE", "") + } + + updateMap := make(map[string]interface{}) + + if mandatory != nil { + updateMap["mandatory"] = mandatory + } + + if policyName != nil { + exists, err := u.repo.ExistByName(ctx, organizationId, *policyName) + if err == nil && exists { + return httpErrors.NewBadRequestError(httpErrors.DuplicateResource, "P_INVALID_POLICY__NAME", "policy template name already exists") + } + updateMap["policy_name"] = policyName + } + + if description != nil { + updateMap["description"] = description + } + + if templateId != nil { + updateMap["template_id"] = templateId + } + + if enforcementAction != nil { + updateMap["enforcement_action"] = enforcementAction + } + + if parameters != nil { + updateMap["parameters"] = parameters + } + + if parameters != nil { + updateMap["policy_match"] = match.JSON() + } + + 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 + } + + return nil +} + +func (u *PolicyUsecase) Delete(ctx context.Context, organizationId string, policyId uuid.UUID) (err error) { + 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) Fetch(ctx context.Context, organizationId string, pg *pagination.Pagination) (*[]model.Policy, error) { + return u.repo.Fetch(ctx, organizationId, pg) +} + +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) 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 + } + + return u.repo.UpdatePolicyTargetClusters(ctx, organizationId, policyId, currentClusterIds, targetClusters) +} + +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 +} diff --git a/internal/usecase/usecase.go b/internal/usecase/usecase.go index 181d1f31..46302b69 100644 --- a/internal/usecase/usecase.go +++ b/internal/usecase/usecase.go @@ -19,4 +19,5 @@ type Usecase struct { Permission IPermissionUsecase Audit IAuditUsecase PolicyTemplate IPolicyTemplateUsecase + Policy IPolicyUsecase } diff --git a/pkg/domain/policy.go b/pkg/domain/policy.go new file mode 100644 index 00000000..e2ccf0e0 --- /dev/null +++ b/pkg/domain/policy.go @@ -0,0 +1,120 @@ +package domain + +import ( + "encoding/json" + "time" +) + +type Kinds struct { + APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"` + Kinds []string `json:"kinds,omitempty"` +} + +type Match struct { + Namespaces []string `json:"namespaces,omitempty"` + ExcludedNamespaces []string `json:"excludedNamespaces,omitempty"` + Kinds []Kinds `json:"kinds,omitempty"` +} + +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"` + Mandatory bool `json:"mandatory"` + + PolicyName string `json:"policyName" example:"label 정책"` + 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"` + Parameters string `json:"parameters" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //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" example:"label 정책"` + 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"` + Parameters string `json:"parameters" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //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" example:"label 정책"` + Description string `json:"description"` + TemplateId *string `json:"templateId,omitempty" example:"d98ef5f1-4a68-4047-a446-2207787ce3ff"` + EnforcementAction *string `json:"enforcementAction,omitempty" enum:"warn,deny,dryrun"` + Parameters *string `json:"parameters,omitempty" example:"\"labels\":{\"key\":\"owner\",\"allowedRegex:^[a-zA-Z]+.agilebank.demo$}\""` + Match *Match `json:"match,omitempty" swaggertype:"object,string" example:"refer:match.Match"` + //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"` +} diff --git a/pkg/httpErrors/errorCode.go b/pkg/httpErrors/errorCode.go index 2db07ce1..63fe9dfd 100644 --- a/pkg/httpErrors/errorCode.go +++ b/pkg/httpErrors/errorCode.go @@ -19,6 +19,7 @@ var errorMap = map[ErrorCode]string{ "C_INVALID_CLOUD_SERVICE": "유효하지 않은 클라우드서비스입니다.", "C_INVALID_AUDIT_ID": "유효하지 않은 로그 아이디입니다. 로그 아이디를 확인하세요.", "C_INVALID_POLICY_TEMPLATE_ID": "유효하지 않은 정책 템플릿 아이디입니다. 정책 템플릿 아이디를 확인하세요.", + "C_INVALID_POLICY_ID": "유효하지 않은 정책 아이디입니다. 정책 아이디를 확인하세요.", "C_FAILED_TO_CALL_WORKFLOW": "워크플로우 호출에 실패했습니다.", // Auth @@ -122,6 +123,11 @@ var errorMap = map[ErrorCode]string{ "PT_INVALID_POLICY_TEMPLATE_NAME": "유효하지 않은 정책 템플릿 이름입니다. 정책 템플릿 이름을 확인하세요.", "PT_INVALID_POLICY_TEMPLATE_KIND": "유효하지 않은 정책 템플릿 유형입니다. 정책 템플릿 유형을 확인하세요.", "PT_INVALID_REGO_PARSEPARAMETER": "유효하지 않은 Rego 파싱 설정입니다. Rego 파싱 설정을 확인하세요.", + + // Policy + "P_CREATE_ALREADY_EXISTED_NAME": "정첵에 이미 존재하는 이름입니다.", + "P_NOT_FOUND_POLICY": "정책이 존재하지 않습니다.", + "P_INVALID_POLICY_NAME": "유효하지 않은 정책 이름입니다. 정책 이름을 확인하세요.", } func (m ErrorCode) GetText() string {