diff --git a/docs/apidoc/swagger.json b/docs/apidoc/swagger.json index cc9f77b13..a9493a2f1 100644 --- a/docs/apidoc/swagger.json +++ b/docs/apidoc/swagger.json @@ -321,8 +321,8 @@ "tags": [ "application" ], - "summary": "list all applications", - "operationId": "listApplications", + "summary": "list all application plans", + "operationId": "listApplicationPlans", "parameters": [ { "type": "string", @@ -368,8 +368,8 @@ "tags": [ "application" ], - "summary": "create one application", - "operationId": "createApplication", + "summary": "create one application plan", + "operationId": "createApplicationPlan", "parameters": [ { "name": "body", @@ -407,12 +407,12 @@ "tags": [ "application" ], - "summary": "detail one application", - "operationId": "detailApplication", + "summary": "detail one application plan", + "operationId": "detailApplicationPlan", "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -431,6 +431,50 @@ } } }, + "put": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "application" + ], + "summary": "update one application plan", + "operationId": "updateApplicationPlan", + "parameters": [ + { + "type": "string", + "description": "identifier of the application plan", + "name": "name", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.UpdateApplicationPlanRequest" + } + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.ApplicationPlanBase" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + }, "delete": { "consumes": [ "application/xml", @@ -444,11 +488,11 @@ "application" ], "summary": "delete one application", - "operationId": "deleteApplication", + "operationId": "deleteApplicationPlan", "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -486,7 +530,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -528,7 +572,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -574,7 +618,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -612,7 +656,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -632,6 +676,148 @@ } } }, + "/api/v1/applicationplans/{name}/envs": { + "post": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "application" + ], + "summary": "creating an application environment plan", + "operationId": "createApplicationEnv", + "parameters": [ + { + "type": "string", + "description": "identifier of the application plan", + "name": "name", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.CreateApplicationEnvPlanRequest" + } + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.EnvBind" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, + "/api/v1/applicationplans/{name}/envs/{envName}": { + "put": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "application" + ], + "summary": "set application plan differences in the specified environment", + "operationId": "updateApplicationEnvBinding", + "parameters": [ + { + "type": "string", + "description": "identifier of the application plan", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "identifier of the application plan", + "name": "envName", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.PutApplicationPlanEnvRequest" + } + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.EnvBind" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + }, + "delete": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "application" + ], + "summary": "delete an application environment plan", + "operationId": "deleteApplicationEnv", + "parameters": [ + { + "type": "string", + "description": "identifier of the application plan", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "identifier of the application plan", + "name": "envName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.EmptyResponse" + } + }, + "404": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, "/api/v1/applicationplans/{name}/policies": { "get": { "consumes": [ @@ -650,7 +836,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -871,7 +1057,7 @@ "parameters": [ { "type": "string", - "description": "identifier of the application", + "description": "identifier of the application plan", "name": "name", "in": "path", "required": true @@ -1097,6 +1283,178 @@ } } }, + "/api/v1/clusters/cloud-clusters/{provider}/create": { + "post": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "cluster" + ], + "summary": "create cloud cluster", + "operationId": "createCloudCluster", + "parameters": [ + { + "type": "string", + "description": "identifier of the cloud provider", + "name": "provider", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/*v1.CreateCloudClusterRequest" + } + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.CreateCloudClusterResponse" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, + "/api/v1/clusters/cloud-clusters/{provider}/creation": { + "get": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "cluster" + ], + "summary": "list cloud cluster creation", + "operationId": "listCloudClusterCreation", + "parameters": [ + { + "type": "string", + "description": "identifier of the cloud provider", + "name": "provider", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.ListCloudClusterCreationResponse" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, + "/api/v1/clusters/cloud-clusters/{provider}/creation/{cloudClusterName}": { + "get": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "cluster" + ], + "summary": "check cloud cluster create status", + "operationId": "getCloudClusterCreationStatus", + "parameters": [ + { + "type": "string", + "description": "identifier of the cloud provider", + "name": "provider", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "identifier for cloud cluster which is creating", + "name": "cloudClusterName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.CreateCloudClusterResponse" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + }, + "delete": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "cluster" + ], + "summary": "delete cloud cluster creation", + "operationId": "deleteCloudClusterCreation", + "parameters": [ + { + "type": "string", + "description": "identifier of the cloud provider", + "name": "provider", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "identifier for cloud cluster which is creating", + "name": "cloudClusterName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "schema": { + "$ref": "#/definitions/v1.CreateCloudClusterResponse" + } + }, + "400": { + "schema": { + "$ref": "#/definitions/bcode.Bcode" + } + } + } + } + }, "/api/v1/clusters/{clusterName}": { "get": { "consumes": [ @@ -1215,7 +1573,7 @@ } } }, - "/api/v1/componentdefinitions": { + "/api/v1/definitions": { "get": { "consumes": [ "application/xml", @@ -1226,23 +1584,76 @@ "application/xml" ], "tags": [ - "componentdefinition" + "definition" ], - "summary": "list all componentdefinition", - "operationId": "listComponentDefinition", + "summary": "list all definitions", + "operationId": "listDefinitions", "parameters": [ { "type": "string", - "description": "if specified, query the componentdefinition supported by the env.", + "description": "query the definition type", + "name": "type", + "in": "query" + }, + { + "type": "string", + "description": "if specified, query the definition supported by the env.", "name": "envName", "in": "query" } ], "responses": { "200": { + "description": "OK", "schema": { - "$ref": "#/definitions/v1.ListComponentDefinitionResponse" + "$ref": "#/definitions/map[string]string" } + }, + "500": { + "description": "Bummer, something went wrong" + } + } + } + }, + "/api/v1/definitions/{name}": { + "get": { + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "tags": [ + "definition" + ], + "summary": "detail definition", + "operationId": "detailDefinition", + "parameters": [ + { + "type": "string", + "description": "identifier of the definition", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "query the definition type", + "name": "type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/map[string]string" + } + }, + "500": { + "description": "Bummer, something went wrong" } } } @@ -1834,9 +2245,9 @@ }, "common.AppRolloutStatus": { "required": [ + "rollingState", "batchRollingState", "upgradedReplicas", - "rollingState", "currentBatch", "upgradedReadyReplicas", "lastTargetAppRevision" @@ -2975,24 +3386,6 @@ } } }, - "v1.ComponentDefinitionBase": { - "required": [ - "name", - "description", - "icon" - ], - "properties": { - "description": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, "v1.ComponentPlanBase": { "required": [ "name", @@ -3068,6 +3461,19 @@ } } }, + "v1.ComponentSelector": { + "required": [ + "components" + ], + "properties": { + "components": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "v1.ConnectCloudClusterRequest": { "required": [ "accessKeyID", @@ -3120,6 +3526,31 @@ } } }, + "v1.CreateApplicationEnvPlanRequest": { + "required": [ + "componentSelector", + "name", + "alias", + "clusterSelector" + ], + "properties": { + "alias": { + "type": "string" + }, + "clusterSelector": { + "$ref": "#/definitions/v1.ClusterSelector" + }, + "componentSelector": { + "$ref": "#/definitions/v1.ComponentSelector" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, "v1.CreateApplicationPlanRequest": { "required": [ "name", @@ -3182,6 +3613,57 @@ } } }, + "v1.CreateCloudClusterRequest": { + "required": [ + "accessKeyID", + "accessKeySecret", + "name", + "zone", + "workerNumber", + "cpuCoresPerWorker", + "memoryPerWorker" + ], + "properties": { + "accessKeyID": { + "type": "string" + }, + "accessKeySecret": { + "type": "string" + }, + "cpuCoresPerWorker": { + "type": "integer", + "format": "int64" + }, + "memoryPerWorker": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "workerNumber": { + "type": "integer", + "format": "int32" + }, + "zone": { + "type": "string" + } + } + }, + "v1.CreateCloudClusterResponse": { + "required": [ + "clusterID", + "status" + ], + "properties": { + "clusterID": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "v1.CreateClusterRequest": { "required": [ "name", @@ -3338,13 +3820,77 @@ } } }, + "v1.DefinitionBase": { + "required": [ + "name", + "description", + "icon" + ], + "properties": { + "description": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "v1.DefinitionProperties": { + "required": [ + "default", + "description", + "title", + "type" + ], + "properties": { + "default": { + "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "v1.DefinitionSchema": { + "required": [ + "properties", + "required", + "type" + ], + "properties": { + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1.DefinitionProperties" + } + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string" + } + } + }, "v1.DetailAddonResponse": { "required": [ + "name", "version", "description", "icon", - "tags", - "name" + "tags" ], "properties": { "deploy_data": { @@ -3375,15 +3921,15 @@ }, "v1.DetailApplicationPlanResponse": { "required": [ - "icon", - "status", - "gatewayRule", + "name", "namespace", + "icon", + "gatewayRule", + "alias", "description", "createTime", "updateTime", - "name", - "alias", + "status", "policies", "status", "resourceInfo", @@ -3453,20 +3999,20 @@ }, "v1.DetailClusterResponse": { "required": [ - "createTime", - "description", - "status", "reason", - "kubeConfig", - "kubeConfigSecret", - "name", - "alias", "provider", - "labels", - "dashboardURL", "updateTime", + "description", + "dashboardURL", + "kubeConfigSecret", + "createTime", + "name", "icon", "apiServerURL", + "alias", + "labels", + "status", + "kubeConfig", "resourceInfo" ], "properties": { @@ -3524,13 +4070,13 @@ }, "v1.DetailComponentPlanResponse": { "required": [ - "appPrimaryKey", - "alias", - "type", + "createTime", "updateTime", + "alias", + "appPrimaryKey", "creator", "name", - "createTime" + "type" ], "properties": { "alias": { @@ -3606,15 +4152,25 @@ } } }, + "v1.DetailDefinitionResponse": { + "required": [ + "schema" + ], + "properties": { + "schema": { + "$ref": "#/definitions/v1.DefinitionSchema" + } + } + }, "v1.DetailPolicyResponse": { "required": [ - "description", - "creator", "properties", "createTime", "updateTime", "name", - "type" + "type", + "description", + "creator" ], "properties": { "createTime": { @@ -3757,12 +4313,20 @@ "v1.EnvBind": { "required": [ "name", - "clusterSelector" + "alias", + "clusterSelector", + "componentSelector" ], "properties": { + "alias": { + "type": "string" + }, "clusterSelector": { "$ref": "#/definitions/v1.ClusterSelector" }, + "componentSelector": { + "$ref": "#/definitions/v1.ComponentSelector" + }, "description": { "type": "string" }, @@ -3850,6 +4414,19 @@ } } }, + "v1.ListCloudClusterCreationResponse": { + "required": [ + "creations" + ], + "properties": { + "creations": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "v1.ListCloudClusterResponse": { "required": [ "clusters", @@ -3881,15 +4458,15 @@ } } }, - "v1.ListComponentDefinitionResponse": { + "v1.ListDefinitionResponse": { "required": [ - "componentDefinitions" + "definitions" ], "properties": { - "componentDefinitions": { + "definitions": { "type": "array", "items": { - "$ref": "#/definitions/v1.ComponentDefinitionBase" + "$ref": "#/definitions/v1.DefinitionBase" } } } @@ -4089,6 +4666,46 @@ } } }, + "v1.PutApplicationPlanEnvRequest": { + "properties": { + "alias": { + "type": "string" + }, + "clusterSelector": { + "$ref": "#/definitions/v1.ClusterSelector" + }, + "componentSelector": { + "$ref": "#/definitions/v1.ComponentSelector" + }, + "description": { + "type": "string" + } + } + }, + "v1.UpdateApplicationPlanRequest": { + "required": [ + "alias", + "description", + "icon" + ], + "properties": { + "alias": { + "type": "string" + }, + "description": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "labels": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "v1.UpdatePolicyRequest": { "required": [ "description", diff --git a/pkg/apiserver/model/application.go b/pkg/apiserver/model/application.go index 5095f1cc7..397660bf7 100644 --- a/pkg/apiserver/model/application.go +++ b/pkg/apiserver/model/application.go @@ -62,18 +62,25 @@ func (a *ApplicationPlan) Index() map[string]string { // EnvBind application env bind type EnvBind struct { - Name string `json:"name" validate:"checkname"` - Description string `json:"description,omitempty"` - ClusterSelector *ClusterSelector `json:"clusterSelector"` + Name string `json:"name"` + Alias string `json:"alias"` + Description string `json:"description,omitempty"` + ClusterSelector ClusterSelector `json:"clusterSelector"` + ComponentSelector *ComponentSelector `json:"componentSelector"` } // ClusterSelector cluster selector type ClusterSelector struct { - Name string `json:"name" validate:"checkname"` + Name string `json:"name"` // Adapt to a scenario where only one Namespace is available or a user-defined Namespace is available. Namespace string `json:"namespace,omitempty"` } +// ComponentSelector component selector +type ComponentSelector struct { + Components []string `json:"components"` +} + // ApplicationComponentPlan component database model type ApplicationComponentPlan struct { Model diff --git a/pkg/apiserver/rest/apis/v1/types.go b/pkg/apiserver/rest/apis/v1/types.go index 92fa93c36..b970e6a50 100644 --- a/pkg/apiserver/rest/apis/v1/types.go +++ b/pkg/apiserver/rest/apis/v1/types.go @@ -29,11 +29,12 @@ import ( var ( // CtxKeyApplication request context key of application CtxKeyApplication = "application" + // CtxKeyWorkflow request context key of workflow + CtxKeyWorkflow = "workflow" + // CtxKeyApplicationEnvBinding request context key of env binding + CtxKeyApplicationEnvBinding = "envbinding-policy" ) -// CtxKeyWorkflow request context key of workflow -var CtxKeyWorkflow = "workflow" - // AddonPhase defines the phase of an addon type AddonPhase string @@ -228,7 +229,7 @@ type EnvBindList []*EnvBind // ContainCluster contain cluster name func (e EnvBindList) ContainCluster(name string) bool { for _, eb := range e { - if eb.ClusterSelector != nil && eb.ClusterSelector.Name == name { + if eb.ClusterSelector.Name == name { return true } } @@ -283,11 +284,21 @@ type CreateApplicationPlanRequest struct { Deploy bool `json:"deploy,omitempty"` } +// UpdateApplicationPlanRequest update application plan base config +type UpdateApplicationPlanRequest struct { + Alias string `json:"alias" validate:"checkalias"` + Description string `json:"description"` + Icon string `json:"icon"` + Labels map[string]string `json:"labels,omitempty"` +} + // EnvBind application env bind type EnvBind struct { - Name string `json:"name" validate:"checkname"` - Description string `json:"description,omitempty"` - ClusterSelector *ClusterSelector `json:"clusterSelector"` + Name string `json:"name" validate:"checkname"` + Alias string `json:"alias" validate:"checkalias"` + Description string `json:"description,omitempty"` + ClusterSelector ClusterSelector `json:"clusterSelector"` + ComponentSelector *ComponentSelector `json:"componentSelector"` } // ClusterSelector cluster selector @@ -297,6 +308,11 @@ type ClusterSelector struct { Namespace string `json:"namespace,omitempty"` } +// ComponentSelector component selector +type ComponentSelector struct { + Components []string `json:"components"` +} + // DetailApplicationPlanResponse application plan detail type DetailApplicationPlanResponse struct { ApplicationPlanBase @@ -359,6 +375,11 @@ type DetailComponentPlanResponse struct { //TODO: Status } +// ListApplicationComponentOptions list app plan component list +type ListApplicationComponentOptions struct { + EnvName string `json:"envName"` +} + // CreateApplicationTemplateRequest create app template request model type CreateApplicationTemplateRequest struct { TemplateName string `json:"templateName" validate:"checkname"` @@ -417,12 +438,14 @@ type DetailDefinitionResponse struct { Schema *DefinitionSchema `json:"schema"` } +// DefinitionSchema definition schema info type DefinitionSchema struct { Properties map[string]*DefinitionProperties `json:"properties"` Required []string `json:"required"` Type string `json:"type"` } +// DefinitionProperties definition properties type DefinitionProperties struct { Items *DefinitionSchema `json:"items,omitempty"` Enum []interface{} `json:"enum,omitempty"` @@ -611,3 +634,16 @@ type ApplicationDeployResponse struct { // VelaQLViewResponse query response type VelaQLViewResponse map[string]interface{} + +// PutApplicationPlanEnvRequest set diff request +type PutApplicationPlanEnvRequest struct { + ComponentSelector *ComponentSelector `json:"componentSelector,omitempty"` + Alias *string `json:"alias,omitempty" validate:"checkalias"` + Description *string `json:"description,omitempty"` + ClusterSelector *ClusterSelector `json:"clusterSelector,omitempty"` +} + +// CreateApplicationEnvPlanRequest new application env plan +type CreateApplicationEnvPlanRequest struct { + EnvBind +} diff --git a/pkg/apiserver/rest/usecase/application.go b/pkg/apiserver/rest/usecase/applicationplan.go similarity index 65% rename from pkg/apiserver/rest/usecase/application.go rename to pkg/apiserver/rest/usecase/applicationplan.go index 64a6f4b33..a22465bc0 100644 --- a/pkg/apiserver/rest/usecase/application.go +++ b/pkg/apiserver/rest/usecase/applicationplan.go @@ -20,6 +20,8 @@ import ( "context" "encoding/json" "errors" + "fmt" + "sort" "strings" corev1 "k8s.io/api/core/v1" @@ -49,18 +51,22 @@ type PolicyType string const ( // EnvBindPolicy Multiple environment distribution policy EnvBindPolicy PolicyType = "env-binding" + + // EnvBindPolicyDefaultName default policy name + EnvBindPolicyDefaultName string = "env-bindings" ) // ApplicationUsecase application usecase type ApplicationUsecase interface { - ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioPlanOptions) ([]*apisv1.ApplicationPlanBase, error) - GetApplication(ctx context.Context, appName string) (*model.ApplicationPlan, error) - DetailApplication(ctx context.Context, app *model.ApplicationPlan) (*apisv1.DetailApplicationPlanResponse, error) + ListApplicationPlans(ctx context.Context, listOptions apisv1.ListApplicatioPlanOptions) ([]*apisv1.ApplicationPlanBase, error) + GetApplicationPlan(ctx context.Context, appName string) (*model.ApplicationPlan, error) + DetailApplicationPlan(ctx context.Context, app *model.ApplicationPlan) (*apisv1.DetailApplicationPlanResponse, error) PublishApplicationTemplate(ctx context.Context, app *model.ApplicationPlan) (*apisv1.ApplicationTemplateBase, error) - CreateApplication(context.Context, apisv1.CreateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) - DeleteApplication(ctx context.Context, app *model.ApplicationPlan) error + CreateApplicationPlan(context.Context, apisv1.CreateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) + UpdateApplicationPlan(context.Context, *model.ApplicationPlan, apisv1.UpdateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) + DeleteApplicationPlan(ctx context.Context, app *model.ApplicationPlan) error Deploy(ctx context.Context, app *model.ApplicationPlan, req apisv1.ApplicationDeployRequest) (*apisv1.ApplicationDeployResponse, error) - ListComponents(ctx context.Context, app *model.ApplicationPlan) ([]*apisv1.ComponentPlanBase, error) + ListComponents(ctx context.Context, app *model.ApplicationPlan, op apisv1.ListApplicationComponentOptions) ([]*apisv1.ComponentPlanBase, error) AddComponent(ctx context.Context, app *model.ApplicationPlan, com apisv1.CreateComponentPlanRequest) (*apisv1.ComponentPlanBase, error) DetailComponent(ctx context.Context, app *model.ApplicationPlan, componentName string) (*apisv1.DetailComponentPlanResponse, error) DeleteComponent(ctx context.Context, app *model.ApplicationPlan, componentName string) error @@ -69,6 +75,10 @@ type ApplicationUsecase interface { DetailPolicy(ctx context.Context, app *model.ApplicationPlan, policyName string) (*apisv1.DetailPolicyResponse, error) DeletePolicy(ctx context.Context, app *model.ApplicationPlan, policyName string) error UpdatePolicy(ctx context.Context, app *model.ApplicationPlan, policyName string, policy apisv1.UpdatePolicyRequest) (*apisv1.DetailPolicyResponse, error) + GetApplicationPlanEnvBindingPolicy(ctx context.Context, app *model.ApplicationPlan) (*v1alpha1.EnvBindingSpec, error) + UpdateApplicationEnvBindingPlan(ctx context.Context, app *model.ApplicationPlan, envName string, diff apisv1.PutApplicationPlanEnvRequest) (*apisv1.EnvBind, error) + CreateApplicationEnvBindingPlan(ctx context.Context, app *model.ApplicationPlan, env apisv1.CreateApplicationEnvPlanRequest) (*apisv1.EnvBind, error) + DeleteApplicationEnvBindingPlan(ctx context.Context, app *model.ApplicationPlan, envName string) error } type applicationUsecaseImpl struct { @@ -92,8 +102,8 @@ func NewApplicationUsecase(ds datastore.DataStore, workflowUsecase WorkflowUseca } } -// ListApplications list applications -func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioPlanOptions) ([]*apisv1.ApplicationPlanBase, error) { +// ListApplicationPlans list applications +func (c *applicationUsecaseImpl) ListApplicationPlans(ctx context.Context, listOptions apisv1.ListApplicatioPlanOptions) ([]*apisv1.ApplicationPlanBase, error) { var app = model.ApplicationPlan{} if listOptions.Namespace != "" { app.Namespace = listOptions.Namespace @@ -104,7 +114,7 @@ func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptio } var list []*apisv1.ApplicationPlanBase for _, entity := range entitys { - appBase := c.converAppModelToBase(ctx, entity.(*model.ApplicationPlan)) + appBase := c.converAppModelToBase(entity.(*model.ApplicationPlan)) if listOptions.Query != "" && !(strings.Contains(appBase.Alias, listOptions.Query) || strings.Contains(appBase.Name, listOptions.Query) || @@ -116,28 +126,34 @@ func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptio } list = append(list, appBase) } + sort.Slice(list, func(i, j int) bool { + return list[i].UpdateTime.Unix() > list[j].UpdateTime.Unix() + }) return list, nil } -// GetApplication get application model -func (c *applicationUsecaseImpl) GetApplication(ctx context.Context, appName string) (*model.ApplicationPlan, error) { +// GetApplicationPlan get application model +func (c *applicationUsecaseImpl) GetApplicationPlan(ctx context.Context, appName string) (*model.ApplicationPlan, error) { var app = model.ApplicationPlan{ Name: appName, } if err := c.ds.Get(ctx, &app); err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrApplicationNotExist + } return nil, err } return &app, nil } -// DetailApplication detail application info -func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *model.ApplicationPlan) (*apisv1.DetailApplicationPlanResponse, error) { - base := c.converAppModelToBase(ctx, app) +// DetailApplicationPlan detail application plan info +func (c *applicationUsecaseImpl) DetailApplicationPlan(ctx context.Context, app *model.ApplicationPlan) (*apisv1.DetailApplicationPlanResponse, error) { + base := c.converAppModelToBase(app) policys, err := c.queryApplicationPolicys(ctx, app) if err != nil { return nil, err } - components, err := c.ListComponents(ctx, app) + components, err := c.ListComponents(ctx, app, apisv1.ListApplicationComponentOptions{}) if err != nil { return nil, err } @@ -162,8 +178,8 @@ func (c *applicationUsecaseImpl) PublishApplicationTemplate(ctx context.Context, return nil, nil } -// CreateApplication create application -func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apisv1.CreateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) { +// CreateApplicationPlan create application +func (c *applicationUsecaseImpl) CreateApplicationPlan(ctx context.Context, req apisv1.CreateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) { application := model.ApplicationPlan{ Name: req.Name, Alias: req.Alias, @@ -240,38 +256,7 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis // build-in create env binding policy if len(req.EnvBind) > 0 { - policy := model.ApplicationPolicyPlan{ - AppPrimaryKey: application.PrimaryKey(), - Name: "env-binds", - Description: "build-in create", - Type: string(EnvBindPolicy), - Creator: "", - } - var envBindingSpec v1alpha1.EnvBindingSpec - for _, envBind := range req.EnvBind { - placement := v1alpha1.EnvPlacement{ - ClusterSelector: &common.ClusterSelector{ - Name: envBind.ClusterSelector.Name, - }, - } - if envBind.ClusterSelector.Namespace != "" { - placement.NamespaceSelector = &v1alpha1.NamespaceSelector{ - Name: envBind.ClusterSelector.Namespace, - } - } - envBindingSpec.Envs = append(envBindingSpec.Envs, v1alpha1.EnvConfig{ - Name: envBind.Name, - Placement: placement, - }) - } - properties, err := model.NewJSONStructByStruct(envBindingSpec) - if err != nil { - log.Logger.Errorf("new env binding properties failure,%s", err.Error()) - return nil, bcode.ErrInvalidProperties - } - policy.Properties = properties - if err := c.ds.Add(ctx, &policy); err != nil { - log.Logger.Errorf("save env binding policy failure,%s", err.Error()) + if _, err := c.createApplictionPlanEnvBindingPolicy(ctx, &application, req.EnvBind); err != nil { return nil, err } } @@ -284,7 +269,7 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis return nil, err } // render app base info. - base := c.converAppModelToBase(ctx, &application) + base := c.converAppModelToBase(&application) // deploy to cluster if need. if req.Deploy && canDeploy { if _, err := c.Deploy(ctx, &application, apisv1.ApplicationDeployRequest{ @@ -297,6 +282,17 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis return base, nil } +func (c *applicationUsecaseImpl) UpdateApplicationPlan(ctx context.Context, app *model.ApplicationPlan, req apisv1.UpdateApplicationPlanRequest) (*apisv1.ApplicationPlanBase, error) { + app.Alias = req.Alias + app.Description = req.Description + app.Labels = req.Labels + app.Icon = req.Icon + if err := c.ds.Put(ctx, app); err != nil { + return nil, err + } + return c.converAppModelToBase(app), nil +} + func (c *applicationUsecaseImpl) saveApplicationComponent(ctx context.Context, app *model.ApplicationPlan, components []common.ApplicationComponent) error { var componentModels []datastore.Entity for _, component := range components { @@ -336,7 +332,7 @@ func (c *applicationUsecaseImpl) saveApplicationComponent(ctx context.Context, a return c.ds.BatchAdd(ctx, componentModels) } -func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model.ApplicationPlan) ([]*apisv1.ComponentPlanBase, error) { +func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model.ApplicationPlan, op apisv1.ListApplicationComponentOptions) ([]*apisv1.ComponentPlanBase, error) { var component = model.ApplicationComponentPlan{ AppPrimaryKey: app.PrimaryKey(), } @@ -344,11 +340,31 @@ func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model. if err != nil { return nil, err } + envComponents := map[string]bool{} + componentSelectorDefine := false + if op.EnvName != "" { + envbinding, err := c.GetApplicationPlanEnvBindingPolicy(ctx, app) + if err != nil && !errors.Is(err, bcode.ErrApplicationNotEnv) { + log.Logger.Errorf("query app plan env binding policy config failure %s", err.Error()) + } + if envbinding != nil { + for _, env := range envbinding.Envs { + if env.Selector != nil && env.Name == op.EnvName { + componentSelectorDefine = true + for _, componentName := range env.Selector.Components { + envComponents[componentName] = true + } + } + } + } + } + var list []*apisv1.ComponentPlanBase for _, component := range components { - log.Logger.Infof("component name %s", component.PrimaryKey()) pm := component.(*model.ApplicationComponentPlan) - list = append(list, c.converComponentModelToBase(pm)) + if !componentSelectorDefine || envComponents[pm.Name] { + list = append(list, c.converComponentModelToBase(pm)) + } } return list, nil } @@ -412,18 +428,49 @@ func (c *applicationUsecaseImpl) converPolicyModelToBase(policy *model.Applicati func (c *applicationUsecaseImpl) saveApplicationPolicy(ctx context.Context, app *model.ApplicationPlan, policys []v1beta1.AppPolicy) error { var policyModels []datastore.Entity + var envbindingPolicy *model.ApplicationPolicyPlan for _, policy := range policys { properties, err := model.NewJSONStruct(policy.Properties) if err != nil { log.Logger.Errorf("parse trait properties failire %w", err) return bcode.ErrInvalidProperties } - policyModels = append(policyModels, &model.ApplicationPolicyPlan{ + appPolicyPlan := &model.ApplicationPolicyPlan{ AppPrimaryKey: app.PrimaryKey(), Name: policy.Name, Type: policy.Type, Properties: properties, - }) + } + if policy.Type != string(EnvBindPolicy) { + policyModels = append(policyModels, appPolicyPlan) + } else { + envbindingPolicy = appPolicyPlan + } + } + // If multiple configurations are configured, enable only the last one. + if envbindingPolicy != nil { + envbindingPolicy.Name = EnvBindPolicyDefaultName + policyModels = append(policyModels, envbindingPolicy) + var envBindingSpec v1alpha1.EnvBindingSpec + if err := json.Unmarshal([]byte(envbindingPolicy.Properties.JSON()), &envBindingSpec); err != nil { + return fmt.Errorf("unmarshal env binding policy failure %w", err) + } + for _, env := range envBindingSpec.Envs { + envBind := &model.EnvBind{ + Name: env.Name, + Description: "", + } + if env.Selector != nil { + envBind.ComponentSelector = (*model.ComponentSelector)(env.Selector) + } + if env.Placement.ClusterSelector != nil { + envBind.ClusterSelector.Name = env.Placement.ClusterSelector.Name + } + if env.Placement.NamespaceSelector != nil { + envBind.ClusterSelector.Namespace = env.Placement.NamespaceSelector.Name + } + app.EnvBinds = append(app.EnvBinds, envBind) + } } return c.ds.BatchAdd(ctx, policyModels) } @@ -443,6 +490,52 @@ func (c *applicationUsecaseImpl) queryApplicationPolicys(ctx context.Context, ap return } +func (c *applicationUsecaseImpl) GetApplicationPlanEnvBindingPolicy(ctx context.Context, app *model.ApplicationPlan) (*v1alpha1.EnvBindingSpec, error) { + var policy = model.ApplicationPolicyPlan{ + AppPrimaryKey: app.PrimaryKey(), + Type: string(EnvBindPolicy), + Name: EnvBindPolicyDefaultName, + } + err := c.ds.Get(ctx, &policy) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrApplicationNotEnv + } + return nil, err + } + var envBindingSpec v1alpha1.EnvBindingSpec + if err := json.Unmarshal([]byte(policy.Properties.JSON()), &envBindingSpec); err != nil { + return nil, err + } + return &envBindingSpec, nil +} + +func (c *applicationUsecaseImpl) createApplictionPlanEnvBindingPolicy(ctx context.Context, app *model.ApplicationPlan, envbinds apisv1.EnvBindList) (*model.ApplicationPolicyPlan, error) { + policy := &model.ApplicationPolicyPlan{ + AppPrimaryKey: app.PrimaryKey(), + Name: EnvBindPolicyDefaultName, + Description: "build-in create", + Type: string(EnvBindPolicy), + Creator: "", + } + var envBindingSpec v1alpha1.EnvBindingSpec + for _, envBind := range envbinds { + envBindingSpec.Envs = append(envBindingSpec.Envs, createEnvBind(*envBind)) + app.EnvBinds = append(app.EnvBinds, createModelEnvBind(*envBind)) + } + properties, err := model.NewJSONStructByStruct(envBindingSpec) + if err != nil { + log.Logger.Errorf("new env binding properties failure,%s", err.Error()) + return nil, bcode.ErrInvalidProperties + } + policy.Properties = properties + if err := c.ds.Add(ctx, policy); err != nil { + log.Logger.Errorf("save env binding policy failure,%s", err.Error()) + return nil, err + } + return policy, nil +} + // DetailPolicy detail app policy // TODO: Add status data about the policy. func (c *applicationUsecaseImpl) DetailPolicy(ctx context.Context, app *model.ApplicationPlan, policyName string) (*apisv1.DetailPolicyResponse, error) { @@ -642,8 +735,8 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo return app, nil } -func (c *applicationUsecaseImpl) converAppModelToBase(ctx context.Context, app *model.ApplicationPlan) *apisv1.ApplicationPlanBase { - appBeas := &apisv1.ApplicationPlanBase{ +func (c *applicationUsecaseImpl) converAppModelToBase(app *model.ApplicationPlan) *apisv1.ApplicationPlanBase { + appBase := &apisv1.ApplicationPlanBase{ Name: app.Name, Alias: app.Alias, Namespace: app.Namespace, @@ -653,49 +746,28 @@ func (c *applicationUsecaseImpl) converAppModelToBase(ctx context.Context, app * Icon: app.Icon, Labels: app.Labels, } - var policy = model.ApplicationPolicyPlan{ - AppPrimaryKey: app.PrimaryKey(), - Type: string(EnvBindPolicy), - } - policys, err := c.ds.List(ctx, &policy, &datastore.ListOptions{}) - if err != nil { - log.Logger.Errorf("query application env binding policy failure %s", err.Error()) - } - for _, policyEntity := range policys { - policy := policyEntity.(*model.ApplicationPolicyPlan) - if policy.Properties != nil { - var envBindingSpec v1alpha1.EnvBindingSpec - if err := json.Unmarshal([]byte(policy.Properties.JSON()), &envBindingSpec); err != nil { - log.Logger.Errorf("unmarshal env binding policy failure %s", err.Error()) - continue - } - for _, env := range envBindingSpec.Envs { - envBind := &apisv1.EnvBind{ - Name: env.Name, - Description: "", - } - if env.Placement.ClusterSelector != nil { - envBind.ClusterSelector = &apisv1.ClusterSelector{ - Name: env.Placement.ClusterSelector.Name, - } - } - if env.Placement.NamespaceSelector != nil && envBind.ClusterSelector != nil { - envBind.ClusterSelector.Namespace = env.Placement.NamespaceSelector.Name - } - appBeas.EnvBind = append(appBeas.EnvBind, envBind) - } + for _, envBind := range app.EnvBinds { + apiEnvBind := &apisv1.EnvBind{ + Name: envBind.Name, + Alias: envBind.Alias, + Description: envBind.Description, + ClusterSelector: apisv1.ClusterSelector(envBind.ClusterSelector), } + if envBind.ComponentSelector != nil { + apiEnvBind.ComponentSelector = (*apisv1.ComponentSelector)(envBind.ComponentSelector) + } + appBase.EnvBind = append(appBase.EnvBind, apiEnvBind) } // TODO: get and render app status - return appBeas + return appBase } -// DeleteApplication delete application -func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *model.ApplicationPlan) error { +// DeleteApplicationPlan delete application plan +func (c *applicationUsecaseImpl) DeleteApplicationPlan(ctx context.Context, app *model.ApplicationPlan) error { // TODO: check app can be deleted // query all components to deleted - components, err := c.ListComponents(ctx, app) + components, err := c.ListComponents(ctx, app, apisv1.ListApplicationComponentOptions{}) if err != nil { return err } @@ -854,3 +926,193 @@ func (c *applicationUsecaseImpl) UpdatePolicy(ctx context.Context, app *model.Ap PolicyBase: *c.converPolicyModelToBase(&policy), }, nil } + +// UpdateApplicationEnvBindingPlan update application env binding diff +func (c *applicationUsecaseImpl) UpdateApplicationEnvBindingPlan( + ctx context.Context, + app *model.ApplicationPlan, + envName string, + envUpdate apisv1.PutApplicationPlanEnvRequest) (*apisv1.EnvBind, error) { + // update env-binding policy + envBinding, err := c.GetApplicationPlanEnvBindingPolicy(ctx, app) + if err != nil { + return nil, err + } + for i, env := range envBinding.Envs { + if env.Name == envName { + if envUpdate.ComponentSelector == nil { + envBinding.Envs[i].Selector = nil + } else { + envBinding.Envs[i].Selector = &v1alpha1.EnvSelector{ + Components: envUpdate.ComponentSelector.Components, + } + } + } + } + properties, err := model.NewJSONStructByStruct(envBinding) + if err != nil { + log.Logger.Errorf("new env binding properties failure,%s", err.Error()) + return nil, bcode.ErrInvalidProperties + } + policy := &model.ApplicationPolicyPlan{ + AppPrimaryKey: app.PrimaryKey(), + Name: EnvBindPolicyDefaultName, + } + if err := c.ds.Get(ctx, policy); err != nil { + return nil, err + } + policy.Properties = properties + if err := c.ds.Put(ctx, policy); err != nil { + return nil, err + } + var envBind model.EnvBind + // update env-binding base + for i, env := range app.EnvBinds { + if env.Name == envName { + if envUpdate.Description != nil { + app.EnvBinds[i].Description = *envUpdate.Description + } + if envUpdate.Alias != nil { + app.EnvBinds[i].Alias = *envUpdate.Alias + } + if envUpdate.ClusterSelector != nil { + app.EnvBinds[i].ClusterSelector = model.ClusterSelector{ + Name: envUpdate.ClusterSelector.Name, + Namespace: envUpdate.ClusterSelector.Namespace, + } + } + if envUpdate.ComponentSelector == nil { + app.EnvBinds[i].ComponentSelector = nil + } else { + app.EnvBinds[i].ComponentSelector = &model.ComponentSelector{ + Components: envUpdate.ComponentSelector.Components, + } + } + envBind = *app.EnvBinds[i] + } + } + if err := c.ds.Put(ctx, app); err != nil { + return nil, err + } + re := &apisv1.EnvBind{ + Name: envBind.Name, + Alias: envBind.Alias, + Description: envBind.Description, + ClusterSelector: apisv1.ClusterSelector(envBind.ClusterSelector), + } + if envBind.ComponentSelector != nil { + re.ComponentSelector = (*apisv1.ComponentSelector)(envBind.ComponentSelector) + } + return re, nil +} + +// CreateApplicationEnvBindingPlan create application env plan +func (c *applicationUsecaseImpl) CreateApplicationEnvBindingPlan(ctx context.Context, app *model.ApplicationPlan, envReq apisv1.CreateApplicationEnvPlanRequest) (*apisv1.EnvBind, error) { + for _, env := range app.EnvBinds { + if env.Name == envReq.Name { + return nil, bcode.ErrApplicationEnvExist + } + } + app.EnvBinds = append(app.EnvBinds, createModelEnvBind(envReq.EnvBind)) + envBinding, err := c.GetApplicationPlanEnvBindingPolicy(ctx, app) + if err != nil { + return nil, err + } + envBinding.Envs = append(envBinding.Envs, createEnvBind(envReq.EnvBind)) + properties, err := model.NewJSONStructByStruct(envBinding) + if err != nil { + log.Logger.Errorf("new env binding properties failure,%s", err.Error()) + return nil, bcode.ErrInvalidProperties + } + policy := &model.ApplicationPolicyPlan{ + AppPrimaryKey: app.PrimaryKey(), + Name: EnvBindPolicyDefaultName, + } + if err := c.ds.Get(ctx, policy); err != nil { + return nil, err + } + policy.Properties = properties + if err := c.ds.Put(ctx, policy); err != nil { + return nil, err + } + if err := c.ds.Put(ctx, app); err != nil { + return nil, err + } + return &envReq.EnvBind, nil +} + +// DeleteApplicationEnvBindingPlan delete application env binding plan +func (c *applicationUsecaseImpl) DeleteApplicationEnvBindingPlan(ctx context.Context, app *model.ApplicationPlan, envName string) error { + + for i, envBind := range app.EnvBinds { + if envBind.Name == envName { + app.EnvBinds = append(app.EnvBinds[0:i], app.EnvBinds[i+1:]...) + } + } + envBinding, err := c.GetApplicationPlanEnvBindingPolicy(ctx, app) + if err != nil { + return err + } + for i, envBind := range envBinding.Envs { + if envBind.Name == envName { + envBinding.Envs = append(envBinding.Envs[0:i], envBinding.Envs[i+1:]...) + } + } + properties, err := model.NewJSONStructByStruct(envBinding) + if err != nil { + log.Logger.Errorf("new env binding properties failure,%s", err.Error()) + return bcode.ErrInvalidProperties + } + policy := &model.ApplicationPolicyPlan{ + AppPrimaryKey: app.PrimaryKey(), + Name: EnvBindPolicyDefaultName, + } + if err := c.ds.Get(ctx, policy); err != nil { + return err + } + policy.Properties = properties + if err := c.ds.Put(ctx, policy); err != nil { + return err + } + if err := c.ds.Put(ctx, app); err != nil { + return err + } + return nil +} + +func createEnvBind(envBind apisv1.EnvBind) v1alpha1.EnvConfig { + placement := v1alpha1.EnvPlacement{ + ClusterSelector: &common.ClusterSelector{ + Name: envBind.ClusterSelector.Name, + }, + } + if envBind.ClusterSelector.Namespace != "" { + placement.NamespaceSelector = &v1alpha1.NamespaceSelector{ + Name: envBind.ClusterSelector.Namespace, + } + } + var componentSelector *v1alpha1.EnvSelector + if envBind.ComponentSelector != nil { + componentSelector = &v1alpha1.EnvSelector{ + Components: envBind.ComponentSelector.Components, + } + } + return v1alpha1.EnvConfig{ + Name: envBind.Name, + Placement: placement, + Selector: componentSelector, + } +} + +func createModelEnvBind(envBind apisv1.EnvBind) *model.EnvBind { + re := model.EnvBind{ + Name: envBind.Name, + Description: envBind.Description, + Alias: envBind.Alias, + ClusterSelector: model.ClusterSelector(envBind.ClusterSelector), + } + if envBind.ComponentSelector != nil { + re.ComponentSelector = (*model.ComponentSelector)(envBind.ComponentSelector) + } + return &re +} diff --git a/pkg/apiserver/rest/usecase/application_test.go b/pkg/apiserver/rest/usecase/applicationplan_test.go similarity index 64% rename from pkg/apiserver/rest/usecase/application_test.go rename to pkg/apiserver/rest/usecase/applicationplan_test.go index 3270aec1f..321335cdb 100644 --- a/pkg/apiserver/rest/usecase/application_test.go +++ b/pkg/apiserver/rest/usecase/applicationplan_test.go @@ -54,12 +54,27 @@ var _ = Describe("Test application usecase function", func() { Name: "test-app", Namespace: "test-app-namespace", Description: "this is a test app", + EnvBind: []*v1.EnvBind{{ + Name: "dev", + Description: "dev env", + ClusterSelector: v1.ClusterSelector{ + Name: "dev", + Namespace: "devnamespace", + }, + }, { + Name: "test", + Description: "test env", + ClusterSelector: v1.ClusterSelector{ + Name: "dev", + Namespace: "testnamespace", + }, + }}, } - base, err := appUsecase.CreateApplication(context.TODO(), req) + base, err := appUsecase.CreateApplicationPlan(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty()) - _, err = appUsecase.CreateApplication(context.TODO(), req) + _, err = appUsecase.CreateApplicationPlan(context.TODO(), req) equal := cmp.Equal(err, bcode.ErrApplicationExist, cmpopts.EquateErrors()) Expect(equal).Should(BeTrue()) @@ -74,7 +89,7 @@ var _ = Describe("Test application usecase function", func() { Labels: map[string]string{"test": "true"}, YamlConfig: string(bs), } - base, err = appUsecase.CreateApplication(context.TODO(), req) + base, err = appUsecase.CreateApplicationPlan(context.TODO(), req) Expect(err).Should(BeNil()) Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty()) @@ -86,7 +101,7 @@ var _ = Describe("Test application usecase function", func() { Labels: map[string]string{"test": "true"}, YamlConfig: "asdasdasdasd", } - base, err = appUsecase.CreateApplication(context.TODO(), req) + base, err = appUsecase.CreateApplicationPlan(context.TODO(), req) equal = cmp.Equal(err, bcode.ErrApplicationConfig, cmpopts.EquateErrors()) Expect(equal).Should(BeTrue()) Expect(base).Should(BeNil()) @@ -101,7 +116,7 @@ var _ = Describe("Test application usecase function", func() { Labels: map[string]string{"test": "true"}, YamlConfig: string(bs), } - _, err = appUsecase.CreateApplication(context.TODO(), req) + _, err = appUsecase.CreateApplicationPlan(context.TODO(), req) equal = cmp.Equal(err, bcode.ErrInvalidProperties, cmpopts.EquateErrors()) Expect(equal).Should(BeTrue()) @@ -115,49 +130,68 @@ var _ = Describe("Test application usecase function", func() { EnvBind: []*v1.EnvBind{ { Name: "dev", + Alias: "Chinese Word", Description: "This is a dev env", - ClusterSelector: &v1.ClusterSelector{ + ClusterSelector: v1.ClusterSelector{ Name: "dev-cluster", }, }, { Name: "prob", Description: "This is a prob env", - ClusterSelector: &v1.ClusterSelector{ + ClusterSelector: v1.ClusterSelector{ Name: "prob-cluster", Namespace: "prob", }, }, }, } - appBase, err := appUsecase.CreateApplication(context.TODO(), req) + appBase, err := appUsecase.CreateApplicationPlan(context.TODO(), req) Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(appBase.EnvBind), 2)).Should(BeEmpty()) - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd4") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd4") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - Expect(cmp.Diff(len(appBase.EnvBind), 2)).Should(BeEmpty()) + + }) + + It("Test GetApplicationPlanEnvBindingPolicy", func() { + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd4") + Expect(err).Should(BeNil()) + envBinding, err := appUsecase.GetApplicationPlanEnvBindingPolicy(context.TODO(), appModel) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(envBinding.Envs), 2)).Should(BeEmpty()) + }) + + It("Test UpdateApplicationPlanEnvBindingDiff", func() { + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") + Expect(err).Should(BeNil()) + _, err = appUsecase.UpdateApplicationEnvBindingPlan(context.TODO(), appModel, "staging", v1.PutApplicationPlanEnvRequest{ + ComponentSelector: &v1.ComponentSelector{Components: []string{"hello-world-server"}}, + }) + Expect(err).Should(BeNil()) }) It("Test ListApplications function", func() { - apps, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioPlanOptions{}) + apps, err := appUsecase.ListApplicationPlans(context.TODO(), v1.ListApplicatioPlanOptions{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(apps), 3)).Should(BeEmpty()) }) It("Test DetailApplication function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - detail, err := appUsecase.DetailApplication(context.TODO(), appModel) + detail, err := appUsecase.DetailApplicationPlan(context.TODO(), appModel) Expect(err).Should(BeNil()) Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, 2)).Should(BeEmpty()) Expect(cmp.Diff(len(detail.Policies), 1)).Should(BeEmpty()) }) It("Test GetWorkflow function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -167,7 +201,7 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ListPolicies function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -179,19 +213,33 @@ var _ = Describe("Test application usecase function", func() { }) It("Test ListComponents function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - components, err := appUsecase.ListComponents(context.TODO(), appModel) + components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(components), 2)).Should(BeEmpty()) Expect(cmp.Diff(components[0].ComponentType, "worker")).Should(BeEmpty()) Expect(components[1].UpdateTime).ShouldNot(BeNil()) + + components, err = appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{ + EnvName: "test", + }) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(components), 1)).Should(BeEmpty()) + Expect(cmp.Diff(components[0].Name, "data-worker")).Should(BeEmpty()) + + components, err = appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{ + EnvName: "staging", + }) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(components), 1)).Should(BeEmpty()) + Expect(cmp.Diff(components[0].Name, "hello-world-server")).Should(BeEmpty()) }) It("Test DetailComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) @@ -203,18 +251,18 @@ var _ = Describe("Test application usecase function", func() { }) It("Test DetailPolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, "example-multi-env-policy") + detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, EnvBindPolicyDefaultName) Expect(err).Should(BeNil()) Expect(cmp.Diff(detail.Type, "env-binding")).Should(BeEmpty()) Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty()) }) It("Test AddComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) base, err := appUsecase.AddComponent(context.TODO(), appModel, v1.CreateComponentPlanRequest{ @@ -230,7 +278,7 @@ var _ = Describe("Test application usecase function", func() { }) It("Test DetailComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) detailResponse, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2") @@ -241,11 +289,11 @@ var _ = Describe("Test application usecase function", func() { }) It("Test AddPolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) _, err = appUsecase.AddPolicy(context.TODO(), appModel, v1.CreatePolicyRequest{ - Name: "example-multi-env-policy", + Name: EnvBindPolicyDefaultName, Description: "this is a test2 policy", Type: "env-binding", Properties: ``, @@ -259,8 +307,9 @@ var _ = Describe("Test application usecase function", func() { }) Expect(err).Should(BeNil()) }) + It("Test DetailPolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, "env-binding-2") @@ -268,8 +317,9 @@ var _ = Describe("Test application usecase function", func() { Expect(detail.Properties).ShouldNot(BeNil()) Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty()) }) + It("Test UpdatePolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) base, err := appUsecase.UpdatePolicy(context.TODO(), appModel, "env-binding-2", v1.UpdatePolicyRequest{ @@ -281,21 +331,75 @@ var _ = Describe("Test application usecase function", func() { Expect((*base.Properties)["envs"]).Should(BeEmpty()) }) It("Test DeletePolicy function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) err = appUsecase.DeletePolicy(context.TODO(), appModel, "env-binding-2") Expect(err).Should(BeNil()) }) It("Test DeleteComponent function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) err = appUsecase.DeleteComponent(context.TODO(), appModel, "test2") Expect(err).Should(BeNil()) }) + + It("Test CreateApplicationEnvBindingPlan function", func() { + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") + Expect(err).Should(BeNil()) + Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) + env, err := appUsecase.CreateApplicationEnvBindingPlan(context.TODO(), appModel, v1.CreateApplicationEnvPlanRequest{ + EnvBind: v1.EnvBind{ + Name: "prod2", + Alias: "生产环境", + Description: "这是一个用户某客户的生产环境", + ClusterSelector: v1.ClusterSelector{ + Name: "prob", + }, + }, + }) + Expect(err).Should(BeNil()) + Expect(env).ShouldNot(BeNil()) + + appModelNew, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(appModelNew.EnvBinds), 4)).Should(BeEmpty()) + + spec, err := appUsecase.GetApplicationPlanEnvBindingPolicy(context.TODO(), appModelNew) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(spec.Envs), 4)).Should(BeEmpty()) + }) + + It("Test CreateApplicationEnvBindingPlan function", func() { + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") + Expect(err).Should(BeNil()) + Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) + env, err := appUsecase.UpdateApplicationEnvBindingPlan(context.TODO(), appModel, "prod2", v1.PutApplicationPlanEnvRequest{ + ComponentSelector: &v1.ComponentSelector{ + Components: []string{}, + }, + }) + Expect(err).Should(BeNil()) + Expect(env).ShouldNot(BeNil()) + + components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{ + EnvName: "prod2", + }) + Expect(err).Should(BeNil()) + Expect(cmp.Diff(len(components), 0)).Should(BeEmpty()) + }) + + It("Test DeleteApplicationEnvBindingPlan function", func() { + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") + Expect(err).Should(BeNil()) + Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) + err = appUsecase.DeleteApplicationEnvBindingPlan(context.TODO(), appModel, "prod2") + Expect(err).Should(BeNil()) + }) + It("Test Deploy Application function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) res, err := appUsecase.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{ @@ -311,12 +415,13 @@ var _ = Describe("Test application usecase function", func() { Expect(cmp.Diff(len(oam.Spec.Components), 2)).Should(BeEmpty()) Expect(cmp.Diff(len(oam.Spec.Policies), 1)).Should(BeEmpty()) }) + It("Test DeleteApplication function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") + appModel, err := appUsecase.GetApplicationPlan(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) - err = appUsecase.DeleteApplication(context.TODO(), appModel) + err = appUsecase.DeleteApplicationPlan(context.TODO(), appModel) Expect(err).Should(BeNil()) - components, err := appUsecase.ListComponents(context.TODO(), appModel) + components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{}) Expect(err).Should(BeNil()) Expect(cmp.Diff(len(components), 0)).Should(BeEmpty()) policies, err := appUsecase.ListPolicies(context.TODO(), appModel) diff --git a/pkg/apiserver/rest/utils/bcode/application.go b/pkg/apiserver/rest/utils/bcode/application.go index 2dcb3d142..3d2f06037 100644 --- a/pkg/apiserver/rest/utils/bcode/application.go +++ b/pkg/apiserver/rest/utils/bcode/application.go @@ -23,7 +23,7 @@ var ErrApplicationConfig = NewBcode(400, 10000, "application config does not com var ErrComponentTypeNotSupport = NewBcode(400, 10001, "An unsupported component type was used.") // ErrApplicationExist application is exist -var ErrApplicationExist = NewBcode(400, 10002, "application name is exist") +var ErrApplicationExist = NewBcode(400, 10002, "application plan name is exist") // ErrInvalidProperties properties(trait or component or others) is invalid var ErrInvalidProperties = NewBcode(400, 10003, "properties is invalid") @@ -38,10 +38,10 @@ var ErrDeployApplyFail = NewBcode(500, 10005, "application deploy apply failure" var ErrNoComponent = NewBcode(200, 10006, "application not have components, can not deploy") // ErrApplicationComponetExist application component is exist -var ErrApplicationComponetExist = NewBcode(400, 10007, "application component is exist") +var ErrApplicationComponetExist = NewBcode(400, 10007, "application component plan is exist") // ErrApplicationComponetNotExist application component is not exist -var ErrApplicationComponetNotExist = NewBcode(404, 10008, "application component is not exist") +var ErrApplicationComponetNotExist = NewBcode(404, 10008, "application component plan is not exist") // ErrApplicationPolicyExist application policy is exist var ErrApplicationPolicyExist = NewBcode(400, 10009, "application policy is exist") @@ -51,3 +51,12 @@ var ErrApplicationPolicyNotExist = NewBcode(404, 10010, "application policy is n // ErrCreateNamespace auto create namespace failure before deploy app var ErrCreateNamespace = NewBcode(500, 10011, "auto create namespace failure") + +// ErrApplicationNotExist application is not exist +var ErrApplicationNotExist = NewBcode(404, 10012, "application plan name is not exist") + +// ErrApplicationNotEnv no env binding policy +var ErrApplicationNotEnv = NewBcode(404, 10013, "application plan not set env binding policy") + +// ErrApplicationEnvExist application env is exist +var ErrApplicationEnvExist = NewBcode(400, 10014, "application env plan is exist") diff --git a/pkg/apiserver/rest/webservice/application.go b/pkg/apiserver/rest/webservice/applicationplan.go similarity index 59% rename from pkg/apiserver/rest/webservice/application.go rename to pkg/apiserver/rest/webservice/applicationplan.go index b23f15da5..f7e4268d9 100644 --- a/pkg/apiserver/rest/webservice/application.go +++ b/pkg/apiserver/rest/webservice/applicationplan.go @@ -29,18 +29,18 @@ import ( "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" ) -type applicationWebService struct { +type applicationPlanWebService struct { applicationUsecase usecase.ApplicationUsecase } -// NewApplicationWebService new application manage webservice -func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase) WebService { - return &applicationWebService{ +// NewApplicationPlanWebService new application manage webservice +func NewApplicationPlanWebService(applicationUsecase usecase.ApplicationUsecase) WebService { + return &applicationPlanWebService{ applicationUsecase: applicationUsecase, } } -func (c *applicationWebService) GetWebService() *restful.WebService { +func (c *applicationPlanWebService) GetWebService() *restful.WebService { ws := new(restful.WebService) ws.Path(versionPrefix+"/applicationplans"). Consumes(restful.MIME_XML, restful.MIME_JSON). @@ -49,8 +49,8 @@ func (c *applicationWebService) GetWebService() *restful.WebService { tags := []string{"application"} - ws.Route(ws.GET("/").To(c.listApplications). - Doc("list all applications"). + ws.Route(ws.GET("/").To(c.listApplicationPlans). + Doc("list all application plans"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(ws.QueryParameter("query", "Fuzzy search based on name or description").DataType("string")). Param(ws.QueryParameter("namespace", "Namespace-based search").DataType("string")). @@ -59,37 +59,80 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(400, "", bcode.Bcode{}). Writes(apis.ListApplicationPlanResponse{})) - ws.Route(ws.POST("/").To(c.createApplication). - Doc("create one application"). + ws.Route(ws.POST("/").To(c.createApplicationPlan). + Doc("create one application plan"). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateApplicationPlanRequest{}). Returns(200, "", apis.ApplicationPlanBase{}). Returns(400, "", bcode.Bcode{}). Writes(apis.ApplicationPlanBase{})) - ws.Route(ws.DELETE("/{name}").To(c.deleteApplication). + ws.Route(ws.DELETE("/{name}").To(c.deleteApplicationPlan). Doc("delete one application"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Returns(200, "", apis.EmptyResponse{}). Returns(400, "", bcode.Bcode{}). Writes(apis.EmptyResponse{})) - ws.Route(ws.GET("/{name}").To(c.detailApplication). - Doc("detail one application"). + ws.Route(ws.GET("/{name}").To(c.detailApplicationPlan). + Doc("detail one application plan"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Returns(200, "", apis.DetailApplicationPlanResponse{}). Returns(400, "", bcode.Bcode{}). Writes(apis.DetailApplicationPlanResponse{})) + ws.Route(ws.PUT("/{name}").To(c.updateApplicationPlan). + Doc("update one application plan"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). + Reads(apis.UpdateApplicationPlanRequest{}). + Returns(200, "", apis.ApplicationPlanBase{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.ApplicationPlanBase{})) + + ws.Route(ws.PUT("/{name}/envs/{envName}").To(c.updateApplicationEnvBinding). + Doc("set application plan differences in the specified environment"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Filter(c.envCheckFilter). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). + Param(ws.PathParameter("envName", "identifier of the application plan").DataType("string")). + Reads(apis.PutApplicationPlanEnvRequest{}). + Returns(200, "", apis.EnvBind{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.EnvBind{})) + + ws.Route(ws.POST("/{name}/envs").To(c.createApplicationEnv). + Doc("creating an application environment plan"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). + Reads(apis.CreateApplicationEnvPlanRequest{}). + Returns(200, "", apis.EnvBind{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.EmptyResponse{})) + + ws.Route(ws.DELETE("/{name}/envs/{envName}").To(c.deleteApplicationEnv). + Doc("delete an application environment plan"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Filter(c.envCheckFilter). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). + Param(ws.PathParameter("envName", "identifier of the application plan").DataType("string")). + Returns(200, "", apis.EmptyResponse{}). + Returns(404, "", bcode.Bcode{}). + Writes(apis.EmptyResponse{})) + ws.Route(ws.POST("/{name}/template").To(c.publishApplicationTemplate). Doc("create one application template"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Reads(apis.CreateApplicationTemplateRequest{}). Returns(200, "", apis.ApplicationTemplateBase{}). Returns(400, "", bcode.Bcode{}). @@ -99,7 +142,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Doc("deploy or upgrade the application"). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Returns(200, "", apis.ApplicationDeployRequest{}). Returns(400, "", bcode.Bcode{}). Writes(apis.ApplicationDeployResponse{})) @@ -107,7 +150,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{name}/componentplans").To(c.listApplicationComponents). Doc("gets the componentplan topology of the application"). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Param(ws.QueryParameter("envName", "list components that deployed in define env").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "", apis.ComponentPlanListResponse{}). @@ -117,7 +160,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.POST("/{name}/componentplans").To(c.createComponent). Doc("create component plan for application plan"). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Reads(apis.CreateComponentPlanRequest{}). Returns(200, "", apis.ComponentPlanBase{}). @@ -127,7 +170,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{name}/componentplans/{componentName}").To(c.detailComponent). Doc("detail component plan for application plan"). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "", apis.DetailComponentPlanResponse{}). Returns(400, "", bcode.Bcode{}). @@ -136,7 +179,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService { ws.Route(ws.GET("/{name}/policies").To(c.listApplicationPolicies). Doc("list policy for application"). Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application").DataType("string")). + Param(ws.PathParameter("name", "identifier of the application plan").DataType("string")). Metadata(restfulspec.KeyOpenAPITags, tags). Returns(200, "", apis.ListApplicationPolicy{}). Returns(400, "", bcode.Bcode{}). @@ -185,8 +228,8 @@ func (c *applicationWebService) GetWebService() *restful.WebService { return ws } -func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { - app, err := c.applicationUsecase.GetApplication(req.Request.Context(), req.PathParameter("name")) +func (c *applicationPlanWebService) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + app, err := c.applicationUsecase.GetApplicationPlan(req.Request.Context(), req.PathParameter("name")) if err != nil { bcode.ReturnError(req, res, err) return @@ -195,7 +238,24 @@ func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restfu chain.ProcessFilter(req, res) } -func (c *applicationWebService) createApplication(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) envCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) + envBinding, err := c.applicationUsecase.GetApplicationPlanEnvBindingPolicy(req.Request.Context(), app) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + for _, env := range envBinding.Envs { + if env.Name == req.PathParameter("envName") { + req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyApplicationEnvBinding, env)) + chain.ProcessFilter(req, res) + return + } + } + bcode.ReturnError(req, res, bcode.ErrApplicationNotEnv) +} + +func (c *applicationPlanWebService) createApplicationPlan(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateApplicationPlanRequest if err := req.ReadEntity(&createReq); err != nil { @@ -207,7 +267,7 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res return } // Call the usecase layer code - appBase, err := c.applicationUsecase.CreateApplication(req.Request.Context(), createReq) + appBase, err := c.applicationUsecase.CreateApplicationPlan(req.Request.Context(), createReq) if err != nil { log.Logger.Errorf("create application failure %s", err.Error()) bcode.ReturnError(req, res, err) @@ -221,8 +281,8 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res } } -func (c *applicationWebService) listApplications(req *restful.Request, res *restful.Response) { - apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicatioPlanOptions{ +func (c *applicationPlanWebService) listApplicationPlans(req *restful.Request, res *restful.Response) { + apps, err := c.applicationUsecase.ListApplicationPlans(req.Request.Context(), apis.ListApplicatioPlanOptions{ Namespace: req.QueryParameter("namespace"), Cluster: req.QueryParameter("cluster"), Query: req.QueryParameter("query"), @@ -237,9 +297,9 @@ func (c *applicationWebService) listApplications(req *restful.Request, res *rest } } -func (c *applicationWebService) detailApplication(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) detailApplicationPlan(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) - detail, err := c.applicationUsecase.DetailApplication(req.Request.Context(), app) + detail, err := c.applicationUsecase.DetailApplicationPlan(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -250,7 +310,7 @@ func (c *applicationWebService) detailApplication(req *restful.Request, res *res } } -func (c *applicationWebService) publishApplicationTemplate(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) publishApplicationTemplate(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) base, err := c.applicationUsecase.PublishApplicationTemplate(req.Request.Context(), app) if err != nil { @@ -264,7 +324,7 @@ func (c *applicationWebService) publishApplicationTemplate(req *restful.Request, } // deployApplication TODO: return event model -func (c *applicationWebService) deployApplication(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) deployApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) // Verify the validity of parameters var createReq apis.ApplicationDeployRequest @@ -287,9 +347,9 @@ func (c *applicationWebService) deployApplication(req *restful.Request, res *res } } -func (c *applicationWebService) deleteApplication(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) deleteApplicationPlan(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) - err := c.applicationUsecase.DeleteApplication(req.Request.Context(), app) + err := c.applicationUsecase.DeleteApplicationPlan(req.Request.Context(), app) if err != nil { bcode.ReturnError(req, res, err) return @@ -300,9 +360,11 @@ func (c *applicationWebService) deleteApplication(req *restful.Request, res *res } } -func (c *applicationWebService) listApplicationComponents(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) listApplicationComponents(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) - components, err := c.applicationUsecase.ListComponents(req.Request.Context(), app) + components, err := c.applicationUsecase.ListComponents(req.Request.Context(), app, apis.ListApplicationComponentOptions{ + EnvName: req.QueryParameter("envName"), + }) if err != nil { bcode.ReturnError(req, res, err) return @@ -313,7 +375,7 @@ func (c *applicationWebService) listApplicationComponents(req *restful.Request, } } -func (c *applicationWebService) createComponent(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) createComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) // Verify the validity of parameters var createReq apis.CreateComponentPlanRequest @@ -336,7 +398,7 @@ func (c *applicationWebService) createComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) detailComponent(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) detailComponent(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) detail, err := c.applicationUsecase.DetailComponent(req.Request.Context(), app, req.PathParameter("componentName")) if err != nil { @@ -349,7 +411,7 @@ func (c *applicationWebService) detailComponent(req *restful.Request, res *restf } } -func (c *applicationWebService) createApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) createApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) // Verify the validity of parameters var createReq apis.CreatePolicyRequest @@ -372,7 +434,7 @@ func (c *applicationWebService) createApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) listApplicationPolicies(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) listApplicationPolicies(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) policies, err := c.applicationUsecase.ListPolicies(req.Request.Context(), app) if err != nil { @@ -385,7 +447,7 @@ func (c *applicationWebService) listApplicationPolicies(req *restful.Request, re } } -func (c *applicationWebService) detailApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) detailApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) detail, err := c.applicationUsecase.DetailPolicy(req.Request.Context(), app, req.PathParameter("policyName")) if err != nil { @@ -398,7 +460,7 @@ func (c *applicationWebService) detailApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) deleteApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) deleteApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) err := c.applicationUsecase.DeletePolicy(req.Request.Context(), app, req.PathParameter("policyName")) if err != nil { @@ -411,7 +473,7 @@ func (c *applicationWebService) deleteApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, res *restful.Response) { +func (c *applicationPlanWebService) updateApplicationPolicy(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) // Verify the validity of parameters var updateReq apis.UpdatePolicyRequest @@ -433,3 +495,85 @@ func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, re return } } + +func (c *applicationPlanWebService) updateApplicationEnvBinding(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) + // Verify the validity of parameters + var updateReq apis.PutApplicationPlanEnvRequest + if err := req.ReadEntity(&updateReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := validate.Struct(&updateReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + diff, err := c.applicationUsecase.UpdateApplicationEnvBindingPlan(req.Request.Context(), app, req.PathParameter("envName"), updateReq) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(diff); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *applicationPlanWebService) updateApplicationPlan(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) + // Verify the validity of parameters + var updateReq apis.UpdateApplicationPlanRequest + if err := req.ReadEntity(&updateReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := validate.Struct(&updateReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + base, err := c.applicationUsecase.UpdateApplicationPlan(req.Request.Context(), app, updateReq) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(base); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *applicationPlanWebService) createApplicationEnv(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) + // Verify the validity of parameters + var createReq apis.CreateApplicationEnvPlanRequest + if err := req.ReadEntity(&createReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := validate.Struct(&createReq); err != nil { + bcode.ReturnError(req, res, err) + return + } + base, err := c.applicationUsecase.CreateApplicationEnvBindingPlan(req.Request.Context(), app, createReq) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(base); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *applicationPlanWebService) deleteApplicationEnv(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.ApplicationPlan) + err := c.applicationUsecase.DeleteApplicationEnvBindingPlan(req.Request.Context(), app, req.PathParameter("envName")) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(apis.EmptyResponse{}); err != nil { + bcode.ReturnError(req, res, err) + return + } +} diff --git a/pkg/apiserver/rest/webservice/validate_test.go b/pkg/apiserver/rest/webservice/validate_test.go index 1113e6b5e..8605450f7 100644 --- a/pkg/apiserver/rest/webservice/validate_test.go +++ b/pkg/apiserver/rest/webservice/validate_test.go @@ -52,5 +52,19 @@ var _ = Describe("Test validate function", func() { } err = validate.Struct(&app3) Expect(err).Should(BeNil()) + + var app4 = apisv1.CreateApplicationPlanRequest{ + Name: "asdasd-asdasd", + Namespace: "namespace", + } + err = validate.Struct(&app4) + Expect(err).Should(BeNil()) + + var component = apisv1.CreateComponentPlanRequest{ + Name: "asdasd-asdasd", + ComponentType: "alibaba-ack", + } + err = validate.Struct(&component) + Expect(err).Should(BeNil()) }) }) diff --git a/pkg/apiserver/rest/webservice/webservice.go b/pkg/apiserver/rest/webservice/webservice.go index 6915f595c..f7c93553e 100644 --- a/pkg/apiserver/rest/webservice/webservice.go +++ b/pkg/apiserver/rest/webservice/webservice.go @@ -68,7 +68,7 @@ func Init(ctx context.Context, ds datastore.DataStore) { definitionUsecase := usecase.NewDefinitionUsecase() addonUsecase := usecase.NewAddonUsecase(ds) RegistWebService(NewClusterWebService(clusterUsecase)) - RegistWebService(NewApplicationWebService(applicationUsecase)) + RegistWebService(NewApplicationPlanWebService(applicationUsecase)) RegistWebService(NewNamespaceWebService(namespaceUsecase)) RegistWebService(NewDefinitionWebservice(definitionUsecase)) RegistWebService(NewAddonWebService(addonUsecase)) diff --git a/pkg/apiserver/rest/webservice/workflow.go b/pkg/apiserver/rest/webservice/workflow.go index 30bcedd26..4ee1cf41e 100644 --- a/pkg/apiserver/rest/webservice/workflow.go +++ b/pkg/apiserver/rest/webservice/workflow.go @@ -125,7 +125,7 @@ func (w *workflowWebService) listApplicationWorkflows(req *restful.Request, res bcode.ReturnError(req, res, bcode.ErrMustQueryByApp) return } - app, err := w.applicationUsecase.GetApplication(req.Request.Context(), req.QueryParameter("appName")) + app, err := w.applicationUsecase.GetApplicationPlan(req.Request.Context(), req.QueryParameter("appName")) if err != nil { bcode.ReturnError(req, res, err) return @@ -157,7 +157,7 @@ func (w *workflowWebService) createApplicationWorkflow(req *restful.Request, res bcode.ReturnError(req, res, err) return } - app, err := w.applicationUsecase.GetApplication(req.Request.Context(), createReq.AppName) + app, err := w.applicationUsecase.GetApplicationPlan(req.Request.Context(), createReq.AppName) if err != nil { bcode.ReturnError(req, res, err) return diff --git a/test/e2e-apiserver-test/application_test.go b/test/e2e-apiserver-test/application_test.go index 5bf6b078b..71a0b2fb6 100644 --- a/test/e2e-apiserver-test/application_test.go +++ b/test/e2e-apiserver-test/application_test.go @@ -42,7 +42,7 @@ var _ = Describe("Test application rest api", func() { Description: "this is a test app", Icon: "", Labels: map[string]string{"test": "true"}, - EnvBind: []*apisv1.EnvBind{{Name: "dev-env", ClusterSelector: &apisv1.ClusterSelector{ + EnvBind: []*apisv1.EnvBind{{Name: "dev-env", ClusterSelector: apisv1.ClusterSelector{ Name: "dev-cluster", }}}, } diff --git a/test/e2e-apiserver-test/testdata/workflow.json b/test/e2e-apiserver-test/testdata/workflow.json new file mode 100644 index 000000000..e218e2bc1 --- /dev/null +++ b/test/e2e-apiserver-test/testdata/workflow.json @@ -0,0 +1,14 @@ +{ + "appName": "appplanName", + "name": "workflowName", + "alias": "workflowAlias", + "description": "workflow plan description", + "enable": true, + "default": true, + "steps": [ + { "name": "deploy-test", + "type": "deploy2env", + "properties":"" + } + ] +} \ No newline at end of file