From ffd25a4cbf73ce1e89ccab07e40d899bac2fbb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=99=93=E5=85=B5?= <596908030@qq.com> Date: Fri, 19 Nov 2021 10:37:40 +0800 Subject: [PATCH] Feat: refactor envbinding for adapt policy placement (#2731) * Feat: refactor envbinding for adapt policy placement * Fix: refactor envbinding logic * Fix: fix some bug * Fix: fix unit test * Fix: fix unit test * Fix: fix unit test * Fix: fix unit test * Fix: fix unit test ... * Fix: fix unit test Co-authored-by: zhuxiaobing --- pkg/apiserver/model/application.go | 11 - pkg/apiserver/model/envbinding.go | 57 +++ pkg/apiserver/rest/apis/v1/types.go | 31 +- pkg/apiserver/rest/usecase/application.go | 419 ++++++------------ .../rest/usecase/application_test.go | 122 +---- pkg/apiserver/rest/usecase/delivery_target.go | 12 +- pkg/apiserver/rest/usecase/envbinding.go | 288 ++++++++++++ pkg/apiserver/rest/usecase/workflow.go | 7 + pkg/apiserver/rest/utils/bcode/application.go | 2 +- .../rest/utils/bcode/delivery_target.go | 7 +- pkg/apiserver/rest/utils/bcode/envbinding.go | 32 ++ pkg/apiserver/rest/webservice/application.go | 278 ++++++------ .../rest/webservice/delivery_target.go | 25 +- pkg/apiserver/rest/webservice/webservice.go | 9 +- test/e2e-apiserver-test/application_test.go | 1 - 15 files changed, 751 insertions(+), 550 deletions(-) create mode 100644 pkg/apiserver/model/envbinding.go create mode 100644 pkg/apiserver/rest/usecase/envbinding.go create mode 100644 pkg/apiserver/rest/utils/bcode/envbinding.go diff --git a/pkg/apiserver/model/application.go b/pkg/apiserver/model/application.go index e26e47e95..7538634ec 100644 --- a/pkg/apiserver/model/application.go +++ b/pkg/apiserver/model/application.go @@ -36,7 +36,6 @@ type Application struct { Description string `json:"description"` Icon string `json:"icon"` Labels map[string]string `json:"labels,omitempty"` - EnvBinding []*EnvBinding `json:"envBinding,omitempty"` } // TableName return custom table name @@ -61,16 +60,6 @@ func (a *Application) Index() map[string]string { return index } -// EnvBinding application env binding -type EnvBinding struct { - Name string `json:"name"` - Alias string `json:"alias"` - Description string `json:"description,omitempty"` - TargetNames []string `json:"targetNames"` - ComponentSelector *ComponentSelector `json:"componentSelector"` - //TODO: componentPatchs -} - // ClusterSelector cluster selector type ClusterSelector struct { Name string `json:"name"` diff --git a/pkg/apiserver/model/envbinding.go b/pkg/apiserver/model/envbinding.go new file mode 100644 index 000000000..c4821521e --- /dev/null +++ b/pkg/apiserver/model/envbinding.go @@ -0,0 +1,57 @@ +/* +Copyright 2021 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package model + +import "fmt" + +func init() { + RegistModel(&EnvBinding{}) +} + +// EnvBinding application env binding +type EnvBinding struct { + Model + AppPrimaryKey string `json:"appPrimaryKey"` + Name string `json:"name"` + Alias string `json:"alias"` + Description string `json:"description,omitempty"` + TargetNames []string `json:"targetNames"` + ComponentSelector *ComponentSelector `json:"componentSelector"` + //TODO: componentPatchs +} + +// TableName return custom table name +func (e *EnvBinding) TableName() string { + return tableNamePrefix + "envbinding" +} + +// PrimaryKey return custom primary key +func (e *EnvBinding) PrimaryKey() string { + return fmt.Sprintf("%s-%s", e.AppPrimaryKey, e.Name) +} + +// Index return custom index +func (e *EnvBinding) Index() map[string]string { + index := make(map[string]string) + if e.Name != "" { + index["name"] = e.Name + } + if e.AppPrimaryKey != "" { + index["appPrimaryKey"] = e.AppPrimaryKey + } + return index +} diff --git a/pkg/apiserver/rest/apis/v1/types.go b/pkg/apiserver/rest/apis/v1/types.go index e52a7743e..0afde2958 100644 --- a/pkg/apiserver/rest/apis/v1/types.go +++ b/pkg/apiserver/rest/apis/v1/types.go @@ -253,7 +253,6 @@ type ApplicationBase struct { UpdateTime time.Time `json:"updateTime"` Icon string `json:"icon"` Labels map[string]string `json:"labels,omitempty"` - EnvBinding EnvBindingList `json:"envBinding,omitempty"` } // ApplicationStatusResponse application status response body @@ -292,6 +291,21 @@ type EnvBinding struct { ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"` } +// EnvBindingBase application env binding +type EnvBindingBase struct { + Name string `json:"name" validate:"checkname"` + Alias string `json:"alias" validate:"checkalias" optional:"true"` + Description string `json:"description,omitempty" optional:"true"` + TargetNames []string `json:"targetNames"` + ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"` + CreateTime time.Time `json:"createTime"` + UpdateTime time.Time `json:"updateTime"` +} + +type DetailEnvBindingResponse struct { + EnvBindingBase +} + // ClusterSelector cluster selector type ClusterSelector struct { Name string `json:"name" validate:"checkname"` @@ -308,6 +322,7 @@ type ComponentSelector struct { type DetailApplicationResponse struct { ApplicationBase Policies []string `json:"policies"` + EnvBindings []string `json:"envBindings"` Status string `json:"status"` ResourceInfo ApplicationResourceInfo `json:"resourceInfo"` WorkflowStatus []WorkflowStepStatus `json:"workflowStatus"` @@ -500,6 +515,7 @@ type CreateWorkflowRequest struct { Description string `json:"description" optional:"true"` Steps []WorkflowStep `json:"steps,omitempty"` Default bool `json:"default"` + EnvName string `json:"envName"` } // UpdateWorkflowRequest update or create application workflow @@ -509,6 +525,7 @@ type UpdateWorkflowRequest struct { Steps []WorkflowStep `json:"steps,omitempty"` Enable bool `json:"enable"` Default bool `json:"default"` + EnvName string `json:"envName"` } // WorkflowStep workflow step config @@ -543,6 +560,7 @@ type WorkflowBase struct { Description string `json:"description"` Enable bool `json:"enable"` Default bool `json:"default"` + EnvName string `json:"envName"` CreateTime time.Time `json:"createTime"` UpdateTime time.Time `json:"updateTime"` } @@ -595,9 +613,14 @@ type VelaQLViewResponse map[string]interface{} // PutApplicationEnvRequest set diff request type PutApplicationEnvRequest struct { ComponentSelector *ComponentSelector `json:"componentSelector,omitempty"` - Alias *string `json:"alias,omitempty" validate:"checkalias" optional:"true"` - Description *string `json:"description,omitempty" optional:"true"` - TargetNames *[]string `json:"targetNames"` + Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"` + Description string `json:"description,omitempty" optional:"true"` + TargetNames []string `json:"targetNames"` +} + +// ListApplicationEnvBinding list app envBindings +type ListApplicationEnvBinding struct { + EnvBindings []*EnvBindingBase `json:"envBindings"` } // CreateApplicationEnvRequest new application env diff --git a/pkg/apiserver/rest/usecase/application.go b/pkg/apiserver/rest/usecase/application.go index ce96d9d8a..0f0a90092 100644 --- a/pkg/apiserver/rest/usecase/application.go +++ b/pkg/apiserver/rest/usecase/application.go @@ -77,10 +77,6 @@ type ApplicationUsecase interface { DetailPolicy(ctx context.Context, app *model.Application, policyName string) (*apisv1.DetailPolicyResponse, error) DeletePolicy(ctx context.Context, app *model.Application, policyName string) error UpdatePolicy(ctx context.Context, app *model.Application, policyName string, policy apisv1.UpdatePolicyRequest) (*apisv1.DetailPolicyResponse, error) - GetApplicationEnvBindingPolicy(ctx context.Context, app *model.Application) (*v1alpha1.EnvBindingSpec, error) - UpdateApplicationEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvRequest) (*apisv1.EnvBinding, error) - CreateApplicationEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error) - DeleteApplicationEnvBinding(ctx context.Context, app *model.Application, envName string) error CreateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.CreateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) DeleteApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string) error UpdateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, traitType string, req apisv1.UpdateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) @@ -89,23 +85,27 @@ type ApplicationUsecase interface { } type applicationUsecaseImpl struct { - ds datastore.DataStore - kubeClient client.Client - apply apply.Applicator - workflowUsecase WorkflowUsecase + ds datastore.DataStore + kubeClient client.Client + apply apply.Applicator + workflowUsecase WorkflowUsecase + envBindingUsecase EnvBindingUsecase + deliveryTargetUsecase DeliveryTargetUsecase } // NewApplicationUsecase new application usecase -func NewApplicationUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase) ApplicationUsecase { +func NewApplicationUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase, envBindingUsecase EnvBindingUsecase, deliveryTargetUsecase DeliveryTargetUsecase) ApplicationUsecase { kubecli, err := clients.GetKubeClient() if err != nil { log.Logger.Fatalf("get kubeclient failure %s", err.Error()) } return &applicationUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - kubeClient: kubecli, - apply: apply.NewAPIApplicator(kubecli), + ds: ds, + workflowUsecase: workflowUsecase, + envBindingUsecase: envBindingUsecase, + deliveryTargetUsecase: deliveryTargetUsecase, + kubeClient: kubecli, + apply: apply.NewAPIApplicator(kubecli), } } @@ -128,8 +128,11 @@ func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptio strings.Contains(appBase.Description, listOptions.Query)) { continue } - if listOptions.TargetName != "" && !appBase.EnvBinding.ContainTarget(listOptions.TargetName) { - continue + if listOptions.TargetName != "" { + targetIsContain, _ := c.envBindingUsecase.CheckAppEnvBindingsContainTarget(ctx, &app, listOptions.TargetName) + if targetIsContain { + continue + } } list = append(list, appBase) } @@ -161,16 +164,22 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod return nil, err } components, err := c.ListComponents(ctx, app, apisv1.ListApplicationComponentOptions{}) + envBindings, err := c.envBindingUsecase.GetEnvBindings(ctx, app) if err != nil { return nil, err } var policyNames []string + var envBindingNames []string for _, p := range policys { policyNames = append(policyNames, p.Name) } + for _, e := range envBindings { + envBindingNames = append(envBindingNames, e.Name) + } var detail = &apisv1.DetailApplicationResponse{ ApplicationBase: *base, Policies: policyNames, + EnvBindings: envBindingNames, ResourceInfo: apisv1.ApplicationResourceInfo{ ComponentNum: len(components), }, @@ -236,45 +245,22 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis } } if oamApp.Spec.Workflow != nil && len(oamApp.Spec.Workflow.Steps) > 0 { - var steps []apisv1.WorkflowStep - for _, step := range oamApp.Spec.Workflow.Steps { - var propertyStr string - if step.Properties != nil { - properties, err := model.NewJSONStruct(step.Properties) - if err != nil { - log.Logger.Errorf("workflow %s step %s properties is invalid %s", application.Name, step.Name, err.Error()) - continue - } - propertyStr = properties.JSON() - } - steps = append(steps, apisv1.WorkflowStep{ - Name: step.Name, - Type: step.Type, - DependsOn: step.DependsOn, - Properties: propertyStr, - Inputs: step.Inputs, - Outputs: step.Outputs, - }) - } - _, err := c.workflowUsecase.CreateWorkflow(ctx, &application, apisv1.CreateWorkflowRequest{ - AppName: application.PrimaryKey(), - Name: application.Name, - Description: "Created automatically.", - Steps: steps, - Default: true, - }) - if err != nil { + if err := c.saveApplicationWorkflow(ctx, &application, oamApp.Spec.Workflow.Steps, application.Name); err != nil { + log.Logger.Errorf("save applictaion polocies failure,%s", err.Error()) return nil, err } } + // TODO Waiting for Spec.EnvBinding support } - // build-in create env binding policy + // build-in create env binding if len(req.EnvBinding) > 0 { - if _, err := c.createApplictionEnvBindingPolicy(ctx, &application, req.EnvBinding); err != nil { + err := c.saveApplicationEnvBinding(ctx, application, req.EnvBinding) + if err != nil { return nil, err } } + if req.Component != nil { _, err = c.AddComponent(ctx, &application, *req.Component) if err != nil { @@ -293,6 +279,70 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis return base, nil } +func (c *applicationUsecaseImpl) genPolicyByEnv(ctx context.Context, app *model.Application, envName string) (v1beta1.AppPolicy, error) { + appPolicy := v1beta1.AppPolicy{} + envBinding, err := c.envBindingUsecase.GetEnvBinding(ctx, app, envName) + if err != nil { + return appPolicy, err + } + appPolicy.Name = genPolicyName(envBinding.Name) + appPolicy.Type = string(EnvBindingPolicy) + + var envBindingSpec v1alpha1.EnvBindingSpec + for _, targetName := range envBinding.TargetNames { + target, err := c.deliveryTargetUsecase.GetDeliveryTarget(ctx, targetName) + if err != nil || target == nil { + return appPolicy, bcode.ErrFoundEnvbindingDeliveryTarget + } + envBindingSpec.Envs = append(envBindingSpec.Envs, createTargetClusterEnv(envBinding.EnvBindingBase, target)) + } + properties, err := model.NewJSONStructByStruct(envBindingSpec) + appPolicy.Properties = properties.RawExtension() + return appPolicy, nil +} + +func (c *applicationUsecaseImpl) saveApplicationWorkflow(ctx context.Context, application *model.Application, workflowSteps []v1beta1.WorkflowStep, workflowName string) error { + var steps []apisv1.WorkflowStep + for _, step := range workflowSteps { + var propertyStr string + if step.Properties != nil { + properties, err := model.NewJSONStruct(step.Properties) + if err != nil { + log.Logger.Errorf("workflow %s step %s properties is invalid %s", application.Name, step.Name, err.Error()) + continue + } + propertyStr = properties.JSON() + } + steps = append(steps, apisv1.WorkflowStep{ + Name: step.Name, + Type: step.Type, + DependsOn: step.DependsOn, + Properties: propertyStr, + Inputs: step.Inputs, + Outputs: step.Outputs, + }) + } + _, err := c.workflowUsecase.CreateWorkflow(ctx, application, apisv1.CreateWorkflowRequest{ + AppName: application.PrimaryKey(), + Name: workflowName, + Description: "Created automatically.", + Steps: steps, + Default: true, + }) + if err != nil { + return err + } + return nil +} + +func (c *applicationUsecaseImpl) saveApplicationEnvBinding(ctx context.Context, app model.Application, envBindings []*apisv1.EnvBinding) error { + err := c.envBindingUsecase.BatchCreateEnvBinding(ctx, &app, envBindings) + if err != nil { + return err + } + return nil +} + func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *model.Application, req apisv1.UpdateApplicationRequest) (*apisv1.ApplicationBase, error) { app.Alias = req.Alias app.Description = req.Description @@ -354,15 +404,15 @@ func (c *applicationUsecaseImpl) ListComponents(ctx context.Context, app *model. envComponents := map[string]bool{} componentSelectorDefine := false if op.EnvName != "" { - envbinding, err := c.GetApplicationEnvBindingPolicy(ctx, app) + envbindings, err := c.envBindingUsecase.GetEnvBindings(ctx, app) if err != nil && !errors.Is(err, bcode.ErrApplicationNotEnv) { log.Logger.Errorf("query app env binding policy config failure %s", err.Error()) } - if envbinding != nil { - for _, env := range envbinding.Envs { - if env.Selector != nil && env.Name == op.EnvName { + if len(envbindings) > 0 { + for _, env := range envbindings { + if env != nil && env.Name == op.EnvName { componentSelectorDefine = true - for _, componentName := range env.Selector.Components { + for _, componentName := range env.ComponentSelector.Components { envComponents[componentName] = true } } @@ -474,7 +524,6 @@ func (c *applicationUsecaseImpl) saveApplicationPolicy(ctx context.Context, app if env.Selector != nil { envBind.ComponentSelector = (*model.ComponentSelector)(env.Selector) } - app.EnvBinding = append(app.EnvBinding, envBind) } } return c.ds.BatchAdd(ctx, policyModels) @@ -495,54 +544,6 @@ func (c *applicationUsecaseImpl) queryApplicationPolicys(ctx context.Context, ap return } -func (c *applicationUsecaseImpl) GetApplicationEnvBindingPolicy(ctx context.Context, app *model.Application) (*v1alpha1.EnvBindingSpec, error) { - var policy = model.ApplicationPolicy{ - AppPrimaryKey: app.PrimaryKey(), - Type: string(EnvBindingPolicy), - Name: EnvBindingPolicyDefaultName, - } - 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 -} - -// nolint -func (c *applicationUsecaseImpl) createApplictionEnvBindingPolicy(ctx context.Context, app *model.Application, envbinds apisv1.EnvBindingList) (*model.ApplicationPolicy, error) { - policy := &model.ApplicationPolicy{ - AppPrimaryKey: app.PrimaryKey(), - Name: EnvBindingPolicyDefaultName, - Description: "build-in create", - Type: string(EnvBindingPolicy), - Creator: "", - } - var envBindingSpec v1alpha1.EnvBindingSpec - for _, envBind := range envbinds { - // TODO: check delivery target - envBindingSpec.Envs = append(envBindingSpec.Envs, createEnvBind(*envBind)) - app.EnvBinding = append(app.EnvBinding, 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.Application, policyName string) (*apisv1.DetailPolicyResponse, error) { @@ -727,6 +728,14 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo if err != nil { return nil, err } + if workflow.EnvName != "" { + envPolicy, err := c.genPolicyByEnv(ctx, appModel, workflow.EnvName) + if err != nil { + return nil, err + } + app.Spec.Policies = append(app.Spec.Policies, envPolicy) + } + } else { workflow, err = c.workflowUsecase.GetApplicationDefaultWorkflow(ctx, appModel) if err != nil && !errors.Is(err, bcode.ErrWorkflowNoDefault) { @@ -747,11 +756,13 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo if step.Properties != nil { wstep.Properties = step.Properties.RawExtension() } + steps = append(steps, wstep) } app.Spec.Workflow = &v1beta1.Workflow{ Steps: steps, } } + return app, nil } @@ -766,18 +777,6 @@ func (c *applicationUsecaseImpl) converAppModelToBase(app *model.Application) *a Icon: app.Icon, Labels: app.Labels, } - for _, envBind := range app.EnvBinding { - apiEnvBind := &apisv1.EnvBinding{ - Name: envBind.Name, - Alias: envBind.Alias, - Description: envBind.Description, - TargetNames: envBind.TargetNames, - } - if envBind.ComponentSelector != nil { - apiEnvBind.ComponentSelector = (*apisv1.ComponentSelector)(envBind.ComponentSelector) - } - appBase.EnvBinding = append(appBase.EnvBinding, apiEnvBind) - } return appBase } @@ -795,6 +794,7 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod if err != nil { return err } + // delete workflow if err := c.workflowUsecase.DeleteWorkflow(ctx, app.Name); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) { log.Logger.Errorf("delete workflow %s failure %s", app.Name, err.Error()) @@ -814,6 +814,10 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod } } + if err := c.envBindingUsecase.BatchDeleteEnvBinding(ctx, app); err != nil { + log.Logger.Errorf("delete envbindings in app %s failure %s", app.Name, err.Error()) + } + return c.ds.Delete(ctx, app) } @@ -946,166 +950,6 @@ func (c *applicationUsecaseImpl) UpdatePolicy(ctx context.Context, app *model.Ap }, nil } -// UpdateApplicationEnvBinding update application env binding diff -func (c *applicationUsecaseImpl) UpdateApplicationEnvBinding( - ctx context.Context, - app *model.Application, - envName string, - envUpdate apisv1.PutApplicationEnvRequest) (*apisv1.EnvBinding, error) { - // update env-binding policy - envBinding, err := c.GetApplicationEnvBindingPolicy(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.ApplicationPolicy{ - AppPrimaryKey: app.PrimaryKey(), - Name: EnvBindingPolicyDefaultName, - } - 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.EnvBinding - // update env-binding base - for i, env := range app.EnvBinding { - if env.Name == envName { - if envUpdate.Description != nil { - app.EnvBinding[i].Description = *envUpdate.Description - } - if envUpdate.Alias != nil { - app.EnvBinding[i].Alias = *envUpdate.Alias - } - if envUpdate.TargetNames != nil { - app.EnvBinding[i].TargetNames = *envUpdate.TargetNames - } - if envUpdate.ComponentSelector == nil { - app.EnvBinding[i].ComponentSelector = nil - } else { - app.EnvBinding[i].ComponentSelector = &model.ComponentSelector{ - Components: envUpdate.ComponentSelector.Components, - } - } - envBind = *app.EnvBinding[i] - } - } - if err := c.ds.Put(ctx, app); err != nil { - return nil, err - } - re := &apisv1.EnvBinding{ - Name: envBind.Name, - Alias: envBind.Alias, - Description: envBind.Description, - TargetNames: envBind.TargetNames, - } - if envBind.ComponentSelector != nil { - re.ComponentSelector = (*apisv1.ComponentSelector)(envBind.ComponentSelector) - } - return re, nil -} - -// CreateApplicationEnvBinding create application env -func (c *applicationUsecaseImpl) CreateApplicationEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error) { - for _, env := range app.EnvBinding { - if env.Name == envReq.Name { - return nil, bcode.ErrApplicationEnvExist - } - } - envBinding, err := c.GetApplicationEnvBindingPolicy(ctx, app) - if err != nil { - if !errors.Is(err, bcode.ErrApplicationNotEnv) { - return nil, err - } - } - if envBinding == nil { - _, err := c.createApplictionEnvBindingPolicy(ctx, app, []*apisv1.EnvBinding{&envReq.EnvBinding}) - if err != nil { - return nil, err - } - } else { - app.EnvBinding = append(app.EnvBinding, createModelEnvBind(envReq.EnvBinding)) - envBinding.Envs = append(envBinding.Envs, createEnvBind(envReq.EnvBinding)) - 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.ApplicationPolicy{ - AppPrimaryKey: app.PrimaryKey(), - Name: EnvBindingPolicyDefaultName, - } - 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.EnvBinding, nil -} - -// DeleteApplicationEnvBinding delete application env binding -func (c *applicationUsecaseImpl) DeleteApplicationEnvBinding(ctx context.Context, app *model.Application, envName string) error { - - for i, envBind := range app.EnvBinding { - if envBind.Name == envName { - app.EnvBinding = append(app.EnvBinding[0:i], app.EnvBinding[i+1:]...) - } - } - envBinding, err := c.GetApplicationEnvBindingPolicy(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.ApplicationPolicy{ - AppPrimaryKey: app.PrimaryKey(), - Name: EnvBindingPolicyDefaultName, - } - 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 (c *applicationUsecaseImpl) CreateApplicationTrait(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.CreateApplicationTraitRequest) (*apisv1.ApplicationTrait, error) { var comp = model.ApplicationComponent{ AppPrimaryKey: app.PrimaryKey(), @@ -1235,7 +1079,7 @@ func (c *applicationUsecaseImpl) DetailRevision(ctx context.Context, appName, re }, nil } -func createEnvBind(envBind apisv1.EnvBinding) v1alpha1.EnvConfig { +func createTargetClusterEnv(envBind apisv1.EnvBindingBase, target *model.DeliveryTarget) v1alpha1.EnvConfig { placement := v1alpha1.EnvPlacement{} var componentSelector *v1alpha1.EnvSelector if envBind.ComponentSelector != nil { @@ -1243,26 +1087,29 @@ func createEnvBind(envBind apisv1.EnvBinding) v1alpha1.EnvConfig { Components: envBind.ComponentSelector.Components, } } + if target.Cluster != nil { + placement.ClusterSelector = &common.ClusterSelector{Name: target.Cluster.ClusterName} + placement.NamespaceSelector = &v1alpha1.NamespaceSelector{Name: target.Cluster.Namespace} + } return v1alpha1.EnvConfig{ - Name: envBind.Name, + Name: genPolicyEnvName(target.Name), Placement: placement, Selector: componentSelector, } } -func createModelEnvBind(envBind apisv1.EnvBinding) *model.EnvBinding { - re := model.EnvBinding{ - Name: envBind.Name, - Description: envBind.Description, - Alias: envBind.Alias, - TargetNames: envBind.TargetNames, - } - if envBind.ComponentSelector != nil { - re.ComponentSelector = (*model.ComponentSelector)(envBind.ComponentSelector) - } - return &re -} - func converAppName(app *model.Application, envName string) string { return fmt.Sprintf("%s-%s", app.Name, envName) } + +func genPolicyName(envName string) string { + return fmt.Sprintf("%s-%s", EnvBindingPolicyDefaultName, envName) +} + +func genWorkflowName(app *model.Application, envName string) string { + return fmt.Sprintf("%s-%s", app.Name, envName) +} + +func genPolicyEnvName(targetName string) string { + return targetName +} diff --git a/pkg/apiserver/rest/usecase/application_test.go b/pkg/apiserver/rest/usecase/application_test.go index 91548b388..6178cd3c3 100644 --- a/pkg/apiserver/rest/usecase/application_test.go +++ b/pkg/apiserver/rest/usecase/application_test.go @@ -37,16 +37,22 @@ import ( var _ = Describe("Test application usecase function", func() { var ( - appUsecase *applicationUsecaseImpl - workflowUsecase *workflowUsecaseImpl + appUsecase *applicationUsecaseImpl + workflowUsecase *workflowUsecaseImpl + envBindingUsecase *envBindingUsecaseImpl + deliveryTargetUsecase *deliveryTargetUsecaseImpl ) BeforeEach(func() { workflowUsecase = &workflowUsecaseImpl{ds: ds} + envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase} + deliveryTargetUsecase = &deliveryTargetUsecaseImpl{ds: ds} appUsecase = &applicationUsecaseImpl{ - ds: ds, - workflowUsecase: workflowUsecase, - apply: apply.NewAPIApplicator(k8sClient), - kubeClient: k8sClient, + ds: ds, + workflowUsecase: workflowUsecase, + apply: apply.NewAPIApplicator(k8sClient), + kubeClient: k8sClient, + envBindingUsecase: envBindingUsecase, + deliveryTargetUsecase: deliveryTargetUsecase, } }) It("Test CreateApplication function", func() { @@ -138,7 +144,7 @@ var _ = Describe("Test application usecase function", func() { } appBase, err := appUsecase.CreateApplication(context.TODO(), req) Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(appBase.EnvBinding), 2)).Should(BeEmpty()) + Expect(cmp.Diff(appBase.Name, "test-app-sadasd4")).Should(BeEmpty()) appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd4") Expect(err).Should(BeNil()) @@ -146,23 +152,6 @@ var _ = Describe("Test application usecase function", func() { }) - It("Test GetApplicationEnvBindingingPolicy", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd4") - Expect(err).Should(BeNil()) - envBinding, err := appUsecase.GetApplicationEnvBindingPolicy(context.TODO(), appModel) - Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(envBinding.Envs), 2)).Should(BeEmpty()) - }) - - It("Test UpdateApplicationEnvBindingingDiff", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") - Expect(err).Should(BeNil()) - _, err = appUsecase.UpdateApplicationEnvBinding(context.TODO(), appModel, "staging", v1.PutApplicationEnvRequest{ - ComponentSelector: &v1.ComponentSelector{Components: []string{"hello-world-server"}}, - }) - Expect(err).Should(BeNil()) - }) - It("Test ListApplications function", func() { apps, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{}) Expect(err).Should(BeNil()) @@ -216,15 +205,15 @@ var _ = Describe("Test application usecase function", func() { EnvName: "test", }) Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(components), 1)).Should(BeEmpty()) + Expect(cmp.Diff(len(components), 2)).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()) + Expect(cmp.Diff(len(components), 2)).Should(BeEmpty()) + Expect(cmp.Diff(components[0].Name, "data-worker")).Should(BeEmpty()) }) It("Test DetailComponent function", func() { @@ -408,85 +397,6 @@ var _ = Describe("Test application usecase function", func() { Expect(err).Should(BeNil()) }) - It("Test CreateApplicationEnvBinding function", func() { - req := v1.CreateApplicationRequest{ - Name: "not-have-env-bind", - Namespace: "test-app-namespace", - Description: "this is a test app", - Icon: "", - Labels: map[string]string{"test": "true"}, - } - _, err := appUsecase.CreateApplication(context.TODO(), req) - Expect(err).Should(BeNil()) - appModel4, err := appUsecase.GetApplication(context.TODO(), "not-have-env-bind") - Expect(err).Should(BeNil()) - By("test create first env") - env4, err := appUsecase.CreateApplicationEnvBinding(context.TODO(), appModel4, v1.CreateApplicationEnvRequest{ - EnvBinding: v1.EnvBinding{ - Name: "prod2", - Alias: "生产环境", - Description: "这是一个用户某客户的生产环境", - TargetNames: []string{"prod-target"}, - }, - }) - Expect(err).Should(BeNil()) - Expect(env4).ShouldNot(BeNil()) - - appModelNew, err := appUsecase.GetApplication(context.TODO(), "not-have-env-bind") - Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(appModelNew.EnvBinding), 1)).Should(BeEmpty()) - - By("test create not first env") - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") - Expect(err).Should(BeNil()) - Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - env, err := appUsecase.CreateApplicationEnvBinding(context.TODO(), appModel, v1.CreateApplicationEnvRequest{ - EnvBinding: v1.EnvBinding{ - Name: "prod2", - Alias: "生产环境", - Description: "这是一个用户某客户的生产环境", - TargetNames: []string{"prod-target"}, - }, - }) - Expect(err).Should(BeNil()) - Expect(env).ShouldNot(BeNil()) - - appModelNew, err = appUsecase.GetApplication(context.TODO(), "test-app-sadasd") - Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(appModelNew.EnvBinding), 4)).Should(BeEmpty()) - - spec, err := appUsecase.GetApplicationEnvBindingPolicy(context.TODO(), appModelNew) - Expect(err).Should(BeNil()) - Expect(cmp.Diff(len(spec.Envs), 4)).Should(BeEmpty()) - }) - - It("Test UpdateApplicationEnvBinding function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") - Expect(err).Should(BeNil()) - Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - env, err := appUsecase.UpdateApplicationEnvBinding(context.TODO(), appModel, "prod2", v1.PutApplicationEnvRequest{ - 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 DeleteApplicationEnvBindinging function", func() { - appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") - Expect(err).Should(BeNil()) - Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty()) - err = appUsecase.DeleteApplicationEnvBinding(context.TODO(), appModel, "prod2") - Expect(err).Should(BeNil()) - }) - It("Test Deploy Application function", func() { appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd") Expect(err).Should(BeNil()) diff --git a/pkg/apiserver/rest/usecase/delivery_target.go b/pkg/apiserver/rest/usecase/delivery_target.go index 8b0d470e4..fe2353332 100644 --- a/pkg/apiserver/rest/usecase/delivery_target.go +++ b/pkg/apiserver/rest/usecase/delivery_target.go @@ -37,15 +37,17 @@ type DeliveryTargetUsecase interface { ListDeliveryTargets(ctx context.Context, page, pageSize int, namespace string) (*apisv1.ListDeliveryTargetResponse, error) } -// NewDeliveryTargetUsecase new DeliveryTarget usecase -func NewDeliveryTargetUsecase(ds datastore.DataStore) DeliveryTargetUsecase { - return &deliveryTargetUsecaseImpl{ds: ds} -} - type deliveryTargetUsecaseImpl struct { ds datastore.DataStore } +// NewDeliveryTargetUsecase new DeliveryTarget usecase +func NewDeliveryTargetUsecase(ds datastore.DataStore) DeliveryTargetUsecase { + return &deliveryTargetUsecaseImpl{ + ds: ds, + } +} + func (dt *deliveryTargetUsecaseImpl) ListDeliveryTargets(ctx context.Context, page, pageSize int, namespace string) (*apisv1.ListDeliveryTargetResponse, error) { deliveryTarget := model.DeliveryTarget{} if namespace != "" { diff --git a/pkg/apiserver/rest/usecase/envbinding.go b/pkg/apiserver/rest/usecase/envbinding.go new file mode 100644 index 000000000..cd34fd976 --- /dev/null +++ b/pkg/apiserver/rest/usecase/envbinding.go @@ -0,0 +1,288 @@ +/* +Copyright 2021 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package usecase + +import ( + "context" + "errors" + + "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" + "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" + "github.com/oam-dev/kubevela/pkg/oam/util" + + "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/model" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" +) + +// EnvBindingUsecase envbinding usecase +type EnvBindingUsecase interface { + GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) + GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*apisv1.DetailEnvBindingResponse, error) + CheckAppEnvBindingsContainTarget(ctx context.Context, app *model.Application, targetName string) (bool, error) + CreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error) + BatchCreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.EnvBindingList) error + UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvRequest) (*apisv1.DetailEnvBindingResponse, error) + DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error + BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error + DetailEnvBinding(ctx context.Context, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) +} + +type envBindingUsecaseImpl struct { + ds datastore.DataStore + workflowUsecase WorkflowUsecase +} + +// NewEnvBindingUsecase new envBinding usecase +func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase) EnvBindingUsecase { + return &envBindingUsecaseImpl{ + ds: ds, + workflowUsecase: workflowUsecase, + } +} + +func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) { + var envBinding = model.EnvBinding{ + AppPrimaryKey: app.PrimaryKey(), + } + envBindings, err := e.ds.List(ctx, &envBinding, &datastore.ListOptions{}) + if err != nil { + return nil, bcode.ErrEnvBindingsNotExist + } + var list []*apisv1.EnvBindingBase + for _, ebd := range envBindings { + eb := ebd.(*model.EnvBinding) + list = append(list, convertEnvbindingModelToBase(eb)) + } + return list, nil +} + +func (e *envBindingUsecaseImpl) GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*apisv1.DetailEnvBindingResponse, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrEnvBindingsNotExist + } + return nil, err + } + return e.DetailEnvBinding(ctx, envBinding) +} + +func (e *envBindingUsecaseImpl) CheckAppEnvBindingsContainTarget(ctx context.Context, app *model.Application, targetName string) (bool, error) { + envBindings, err := e.GetEnvBindings(ctx, app) + if err != nil { + return false, err + } + var filteredList []*apisv1.EnvBindingBase + for _, envBinding := range envBindings { + if utils.StringsContain(envBinding.TargetNames, targetName) { + filteredList = append(filteredList, envBinding) + } + } + return len(filteredList) > 0, nil +} + +func (e *envBindingUsecaseImpl) CreateEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envReq.Name) + if err != nil { + if !errors.Is(err, datastore.ErrRecordNotExist) { + return nil, err + } + } + if envBinding != nil { + return nil, bcode.ErrEnvBindingExist + } else { + envBindingModel := convertCreateReqToEnvBindingModel(app, envReq) + if err := e.ds.Add(ctx, &envBindingModel); err != nil { + return nil, err + } + err := e.createEnvWorkflow(ctx, app, &envBindingModel) + if err != nil { + return nil, err + } + } + return &envReq.EnvBinding, nil +} + +func (e *envBindingUsecaseImpl) BatchCreateEnvBinding(ctx context.Context, app *model.Application, envbindings apisv1.EnvBindingList) error { + for _, envBinding := range envbindings { + envBindingModel := convertToEnvBindingModel(app, *envBinding) + if err := e.ds.Add(ctx, envBindingModel); err != nil { + return err + } + err := e.createEnvWorkflow(ctx, app, envBindingModel) + if err != nil { + return err + } + } + return nil +} + +func (e *envBindingUsecaseImpl) getBindingByEnv(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) { + var envBinding = model.EnvBinding{ + AppPrimaryKey: app.PrimaryKey(), + Name: envName, + } + err := e.ds.Get(ctx, &envBinding) + if err != nil { + return nil, err + } + return &envBinding, nil +} + +func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, envUpdate apisv1.PutApplicationEnvRequest) (*apisv1.DetailEnvBindingResponse, error) { + envBinding, err := e.getBindingByEnv(ctx, app, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrEnvBindingNotExist + } + return nil, err + } + envBindingModel := &model.EnvBinding{ + Name: envBinding.Name, + Alias: envUpdate.Alias, + Description: envUpdate.Description, + TargetNames: envUpdate.TargetNames, + } + if envBinding.ComponentSelector != nil { + envBindingModel.ComponentSelector = envBinding.ComponentSelector + } + if err := e.ds.Put(ctx, envBindingModel); err != nil { + return nil, err + } + return e.DetailEnvBinding(ctx, envBindingModel) +} + +func (e *envBindingUsecaseImpl) DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error { + envBinding, err := e.getBindingByEnv(ctx, app, envName) + if err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return bcode.ErrEnvBindingNotExist + } + return err + } + if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: app.PrimaryKey(), Name: envBinding.Name}); err != nil { + return err + } + return nil +} + +func (e *envBindingUsecaseImpl) BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error { + envBindings, err := e.GetEnvBindings(ctx, app) + if err != nil { + return err + } + for _, envBinding := range envBindings { + if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: app.PrimaryKey(), Name: envBinding.Name}); err != nil { + return err + } + } + return nil +} + +func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *model.Application, env *model.EnvBinding) error { + var workflowSteps []v1beta1.WorkflowStep + for _, targetName := range env.TargetNames { + step := v1beta1.WorkflowStep{ + Name: genPolicyEnvName(targetName), + Type: "deploy2env", + Properties: util.Object2RawExtension(map[string]string{ + "policy": genPolicyName(env.Name), + "env": genPolicyEnvName(targetName), + }), + } + workflowSteps = append(workflowSteps, step) + } + var steps []apisv1.WorkflowStep + for _, step := range workflowSteps { + var propertyStr string + if step.Properties != nil { + properties, err := model.NewJSONStruct(step.Properties) + if err != nil { + log.Logger.Errorf("workflow %s step %s properties is invalid %s", app.Name, step.Name, err.Error()) + continue + } + propertyStr = properties.JSON() + } + steps = append(steps, apisv1.WorkflowStep{ + Name: step.Name, + Type: step.Type, + DependsOn: step.DependsOn, + Properties: propertyStr, + Inputs: step.Inputs, + Outputs: step.Outputs, + }) + } + _, err := e.workflowUsecase.CreateWorkflow(ctx, app, apisv1.CreateWorkflowRequest{ + AppName: app.PrimaryKey(), + Name: genWorkflowName(app, env.Name), + Description: "Created automatically by envbinding.", + EnvName: env.Name, + Steps: steps, + Default: false, + }) + if err != nil { + return err + } + return nil +} + +func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) { + return &apisv1.DetailEnvBindingResponse{ + EnvBindingBase: *convertEnvbindingModelToBase(envBinding), + }, nil +} + +func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvRequest) model.EnvBinding { + envBinding := model.EnvBinding{ + AppPrimaryKey: app.Name, + Name: req.Name, + Alias: req.Alias, + Description: req.Description, + TargetNames: req.TargetNames, + } + return envBinding +} + +func convertEnvbindingModelToBase(envBinding *model.EnvBinding) *apisv1.EnvBindingBase { + ebb := &apisv1.EnvBindingBase{ + Name: envBinding.Name, + Alias: envBinding.Alias, + Description: envBinding.Description, + TargetNames: envBinding.TargetNames, + ComponentSelector: (*apisv1.ComponentSelector)(envBinding.ComponentSelector), + CreateTime: envBinding.CreateTime, + UpdateTime: envBinding.UpdateTime, + } + return ebb +} + +func convertToEnvBindingModel(app *model.Application, envBind apisv1.EnvBinding) *model.EnvBinding { + re := model.EnvBinding{ + AppPrimaryKey: app.Name, + Name: envBind.Name, + Description: envBind.Description, + Alias: envBind.Alias, + TargetNames: envBind.TargetNames, + } + if envBind.ComponentSelector != nil { + re.ComponentSelector = (*model.ComponentSelector)(envBind.ComponentSelector) + } + return &re +} diff --git a/pkg/apiserver/rest/usecase/workflow.go b/pkg/apiserver/rest/usecase/workflow.go index 37dc4e27e..d14d782ba 100644 --- a/pkg/apiserver/rest/usecase/workflow.go +++ b/pkg/apiserver/rest/usecase/workflow.go @@ -110,6 +110,7 @@ func (w *workflowUsecaseImpl) CreateWorkflow(ctx context.Context, app *model.App Name: req.Name, Description: req.Description, Default: req.Default, + EnvName: req.EnvName, AppPrimaryKey: app.PrimaryKey(), } if err := w.ds.Add(ctx, &workflow); err != nil { @@ -138,6 +139,7 @@ func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *mode workflow.Description = req.Description // It is allowed to set multiple workflows as default, and only one takes effect. workflow.Default = req.Default + workflow.EnvName = req.EnvName if err := w.ds.Put(ctx, workflow); err != nil { return nil, err } @@ -165,6 +167,7 @@ func (w *workflowUsecaseImpl) DetailWorkflow(ctx context.Context, workflow *mode Name: workflow.Name, Description: workflow.Description, Default: workflow.Default, + EnvName: workflow.EnvName, CreateTime: workflow.CreateTime, UpdateTime: workflow.UpdateTime, }, @@ -178,6 +181,9 @@ func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, workflowName stri Name: workflowName, } if err := w.ds.Get(ctx, &workflow); err != nil { + if errors.Is(err, datastore.ErrRecordNotExist) { + return nil, bcode.ErrWorkflowNotExist + } return nil, err } return &workflow, nil @@ -199,6 +205,7 @@ func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app * Name: wm.Name, Description: wm.Description, Default: wm.Default, + EnvName: wm.EnvName, CreateTime: wm.CreateTime, UpdateTime: wm.UpdateTime, }) diff --git a/pkg/apiserver/rest/utils/bcode/application.go b/pkg/apiserver/rest/utils/bcode/application.go index db8d27e22..0bc309d57 100644 --- a/pkg/apiserver/rest/utils/bcode/application.go +++ b/pkg/apiserver/rest/utils/bcode/application.go @@ -56,7 +56,7 @@ var ErrCreateNamespace = NewBcode(500, 10011, "auto create namespace failure") var ErrApplicationNotExist = NewBcode(404, 10012, "application name is not exist") // ErrApplicationNotEnv no env binding policy -var ErrApplicationNotEnv = NewBcode(404, 10013, "application not set env binding policy") +var ErrApplicationNotEnv = NewBcode(404, 10013, "application not set env binding") // ErrApplicationEnvExist application env is exist var ErrApplicationEnvExist = NewBcode(400, 10014, "application env is exist") diff --git a/pkg/apiserver/rest/utils/bcode/delivery_target.go b/pkg/apiserver/rest/utils/bcode/delivery_target.go index 32c019068..727cf72f7 100644 --- a/pkg/apiserver/rest/utils/bcode/delivery_target.go +++ b/pkg/apiserver/rest/utils/bcode/delivery_target.go @@ -17,7 +17,10 @@ limitations under the License. package bcode // ErrDeliveryTargetExist deliveryTarget is exist -var ErrDeliveryTargetExist = NewBcode(400, 20006, "deliveryTarget is exist") +var ErrDeliveryTargetExist = NewBcode(400, 80001, "deliveryTarget is exist") // ErrDeliveryTargetNotExist deliveryTarget is not exist -var ErrDeliveryTargetNotExist = NewBcode(404, 20007, "deliveryTarget is not exist") +var ErrDeliveryTargetNotExist = NewBcode(404, 80002, "deliveryTarget is not exist") + +// ErrDeliveryTargetInUseCantDeleted deliveryTarget being used +var ErrDeliveryTargetInUseCantDeleted = NewBcode(404, 80003, "deliveryTarget in use, can't be deleted") diff --git a/pkg/apiserver/rest/utils/bcode/envbinding.go b/pkg/apiserver/rest/utils/bcode/envbinding.go new file mode 100644 index 000000000..6e4b7a3f4 --- /dev/null +++ b/pkg/apiserver/rest/utils/bcode/envbinding.go @@ -0,0 +1,32 @@ +/* +Copyright 2021 The KubeVela Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bcode + +// ErrEnvbindingDeliveryTargetNotAllExist application envbinding deliveryTarget is not exist +var ErrEnvbindingDeliveryTargetNotAllExist = NewBcode(400, 90001, "application envbinding deliveryTarget is not all exist") + +// ErrFoundEnvbindingDeliveryTarget found application envbinding deliveryTarget failure +var ErrFoundEnvbindingDeliveryTarget = NewBcode(400, 90002, "found application envbinding deliveryTarget failure") + +// ErrEnvBindingNotExist application envbinding is not exist +var ErrEnvBindingNotExist = NewBcode(400, 90003, "application envbinding not exist") + +// ErrEnvBindingsNotExist application envbindings is not exist +var ErrEnvBindingsNotExist = NewBcode(400, 90004, "application envbinding snot exist") + +// ErrEnvBindingExist application envbinding is exist +var ErrEnvBindingExist = NewBcode(400, 90005, "application envbinding is exist") diff --git a/pkg/apiserver/rest/webservice/application.go b/pkg/apiserver/rest/webservice/application.go index b2d706618..a55b96b45 100644 --- a/pkg/apiserver/rest/webservice/application.go +++ b/pkg/apiserver/rest/webservice/application.go @@ -19,6 +19,8 @@ package webservice import ( "context" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" + restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" @@ -26,18 +28,19 @@ import ( "github.com/oam-dev/kubevela/pkg/apiserver/model" apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" - "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils" "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" ) type applicationWebService struct { applicationUsecase usecase.ApplicationUsecase + envBindingUsecase usecase.EnvBindingUsecase } // NewApplicationWebService new application manage webservice -func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase) WebService { +func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase) WebService { return &applicationWebService{ applicationUsecase: applicationUsecase, + envBindingUsecase: envBindingUsecase, } } @@ -86,28 +89,6 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(400, "", bcode.Bcode{}). Writes(apis.DetailApplicationResponse{})) - ws.Route(ws.PUT("/{name}").To(c.updateApplication). - Doc("update one application "). - Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.appCheckFilter). - Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). - Reads(apis.UpdateApplicationRequest{}). - Returns(200, "", apis.ApplicationBase{}). - Returns(400, "", bcode.Bcode{}). - Writes(apis.ApplicationBase{})) - - ws.Route(ws.PUT("/{name}/envs/{envName}").To(c.updateApplicationEnvBinding). - Doc("set application differences in the specified environment"). - Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.appCheckFilter). - Filter(c.envCheckFilter). - Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). - Param(ws.PathParameter("envName", "identifier of the application ").DataType("string")). - Reads(apis.PutApplicationEnvRequest{}). - Returns(200, "", apis.EnvBinding{}). - Returns(400, "", bcode.Bcode{}). - Writes(apis.EnvBinding{})) - ws.Route(ws.GET("/{name}/envs/{envName}/status").To(c.getApplicationStatus). Doc("get application status"). Metadata(restfulspec.KeyOpenAPITags, tags). @@ -118,26 +99,15 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(400, "", bcode.Bcode{}). Writes(apis.ApplicationStatusResponse{})) - ws.Route(ws.POST("/{name}/envs").To(c.createApplicationEnv). - Doc("creating an application environment "). + ws.Route(ws.PUT("/{name}").To(c.updateApplication). + Doc("update one application "). Metadata(restfulspec.KeyOpenAPITags, tags). Filter(c.appCheckFilter). Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). - Reads(apis.CreateApplicationEnvRequest{}). - Returns(200, "", apis.EnvBinding{}). + Reads(apis.UpdateApplicationRequest{}). + Returns(200, "", apis.ApplicationBase{}). Returns(400, "", bcode.Bcode{}). - Writes(apis.EmptyResponse{})) - - ws.Route(ws.DELETE("/{name}/envs/{envName}").To(c.deleteApplicationEnv). - Doc("delete an application environment "). - Metadata(restfulspec.KeyOpenAPITags, tags). - Filter(c.appCheckFilter). - Filter(c.envCheckFilter). - Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). - Param(ws.PathParameter("envName", "identifier of the application envbinding").DataType("string")). - Returns(200, "", apis.EmptyResponse{}). - Returns(404, "", bcode.Bcode{}). - Writes(apis.EmptyResponse{})) + Writes(apis.ApplicationBase{})) ws.Route(ws.POST("/{name}/template").To(c.publishApplicationTemplate). Doc("create one application template"). @@ -293,36 +263,52 @@ func (c *applicationWebService) GetWebService() *restful.WebService { Returns(200, "", apis.DetailRevisionResponse{}). Returns(400, "", bcode.Bcode{}). Writes(apis.DetailRevisionResponse{})) + + ws.Route(ws.GET("/{name}/envs").To(c.listApplicationEnvs). + Doc("list policy for application"). + Filter(c.appCheckFilter). + Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). + Metadata(restfulspec.KeyOpenAPITags, tags). + Returns(200, "", apis.ListApplicationEnvBinding{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.ListApplicationEnvBinding{})) + + ws.Route(ws.POST("/{name}/envs").To(c.createApplicationEnv). + Doc("creating an application environment "). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). + Reads(apis.CreateApplicationEnvRequest{}). + Returns(200, "", apis.EnvBinding{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.EmptyResponse{})) + + ws.Route(ws.PUT("/{name}/envs/{envName}").To(c.updateApplicationEnv). + Doc("set application differences in the specified environment"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Filter(c.envCheckFilter). + Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). + Param(ws.PathParameter("envName", "identifier of the envBinding ").DataType("string")). + Reads(apis.PutApplicationEnvRequest{}). + Returns(200, "", apis.EnvBinding{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.EnvBinding{})) + + ws.Route(ws.DELETE("/{name}/envs/{envName}").To(c.deleteApplicationEnv). + Doc("delete an application environment "). + Metadata(restfulspec.KeyOpenAPITags, tags). + Filter(c.appCheckFilter). + Filter(c.envCheckFilter). + Param(ws.PathParameter("name", "identifier of the application ").DataType("string")). + Param(ws.PathParameter("envName", "identifier of the envBinding ").DataType("string")). + Returns(200, "", apis.EmptyResponse{}). + Returns(404, "", bcode.Bcode{}). + Writes(apis.EmptyResponse{})) + 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")) - if err != nil { - bcode.ReturnError(req, res, err) - return - } - req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyApplication, app)) - chain.ProcessFilter(req, res) -} - -func (c *applicationWebService) envCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { - app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - envBinding, err := c.applicationUsecase.GetApplicationEnvBindingPolicy(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 *applicationWebService) createApplication(req *restful.Request, res *restful.Response) { // Verify the validity of parameters var createReq apis.CreateApplicationRequest @@ -564,29 +550,6 @@ func (c *applicationWebService) updateApplicationPolicy(req *restful.Request, re } } -func (c *applicationWebService) updateApplicationEnvBinding(req *restful.Request, res *restful.Response) { - app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - // Verify the validity of parameters - var updateReq apis.PutApplicationEnvRequest - 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.UpdateApplicationEnvBinding(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 *applicationWebService) updateApplication(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) // Verify the validity of parameters @@ -610,42 +573,6 @@ func (c *applicationWebService) updateApplication(req *restful.Request, res *res } } -func (c *applicationWebService) createApplicationEnv(req *restful.Request, res *restful.Response) { - app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - // Verify the validity of parameters - var createReq apis.CreateApplicationEnvRequest - 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.CreateApplicationEnvBinding(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 *applicationWebService) deleteApplicationEnv(req *restful.Request, res *restful.Response) { - app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) - err := c.applicationUsecase.DeleteApplicationEnvBinding(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 - } -} - func (c *applicationWebService) addApplicationTrait(req *restful.Request, res *restful.Response) { app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) var createReq apis.CreateApplicationTraitRequest @@ -749,3 +676,102 @@ func (c *applicationWebService) detailApplicationRevision(req *restful.Request, return } } + +func (c *applicationWebService) updateApplicationEnv(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) + // Verify the validity of parameters + var updateReq apis.PutApplicationEnvRequest + 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.envBindingUsecase.UpdateEnvBinding(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 *applicationWebService) listApplicationEnvs(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) + envBindings, err := c.envBindingUsecase.GetEnvBindings(req.Request.Context(), app) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(apis.ListApplicationEnvBinding{EnvBindings: envBindings}); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *applicationWebService) createApplicationEnv(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) + // Verify the validity of parameters + var createReq apis.CreateApplicationEnvRequest + 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.envBindingUsecase.CreateEnvBinding(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 *applicationWebService) deleteApplicationEnv(req *restful.Request, res *restful.Response) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) + err := c.envBindingUsecase.DeleteEnvBinding(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 + } +} + +func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + app, err := c.applicationUsecase.GetApplication(req.Request.Context(), req.PathParameter("name")) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyApplication, app)) + chain.ProcessFilter(req, res) +} + +func (c *applicationWebService) envCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) { + app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application) + envBindings, err := c.envBindingUsecase.GetEnvBindings(req.Request.Context(), app) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + for _, env := range envBindings { + 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) +} diff --git a/pkg/apiserver/rest/webservice/delivery_target.go b/pkg/apiserver/rest/webservice/delivery_target.go index 8168cbd53..8fdf6ba79 100644 --- a/pkg/apiserver/rest/webservice/delivery_target.go +++ b/pkg/apiserver/rest/webservice/delivery_target.go @@ -19,6 +19,10 @@ package webservice import ( "context" + "github.com/pkg/errors" + + "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" @@ -31,15 +35,17 @@ import ( ) // NewDeliveryTargetWebService new deliveryTarget webservice -func NewDeliveryTargetWebService(deliveryTargetUsecase usecase.DeliveryTargetUsecase) WebService { +func NewDeliveryTargetWebService(deliveryTargetUsecase usecase.DeliveryTargetUsecase, applicationUsecase usecase.ApplicationUsecase) WebService { return &DeliveryTargetWebService{ deliveryTargetUsecase: deliveryTargetUsecase, + applicationUsecase: applicationUsecase, } } // DeliveryTargetWebService delivery target web service type DeliveryTargetWebService struct { deliveryTargetUsecase usecase.DeliveryTargetUsecase + applicationUsecase usecase.ApplicationUsecase } // GetWebService get web service @@ -169,7 +175,20 @@ func (dt *DeliveryTargetWebService) updateDeliveryTarget(req *restful.Request, r } func (dt *DeliveryTargetWebService) deleteDeliveryTarget(req *restful.Request, res *restful.Response) { - if err := dt.deliveryTargetUsecase.DeleteDeliveryTarget(req.Request.Context(), req.PathParameter("name")); err != nil { + deliveryTargetName := req.PathParameter("name") + //deliveryTarget in use, can't be deleted + applications, err := dt.applicationUsecase.ListApplications(context.TODO(), apis.ListApplicatioOptions{TargetName: deliveryTargetName}) + if err != nil { + if !errors.Is(err, datastore.ErrRecordNotExist) { + bcode.ReturnError(req, res, err) + return + } + } + if applications != nil { + bcode.ReturnError(req, res, bcode.ErrDeliveryTargetInUseCantDeleted) + return + } + if err := dt.deliveryTargetUsecase.DeleteDeliveryTarget(req.Request.Context(), deliveryTargetName); err != nil { bcode.ReturnError(req, res, err) return } @@ -185,13 +204,11 @@ func (dt *DeliveryTargetWebService) listDeliveryTargets(req *restful.Request, re bcode.ReturnError(req, res, err) return } - deliveryTargets, err := dt.deliveryTargetUsecase.ListDeliveryTargets(req.Request.Context(), page, pageSize, req.QueryParameter("namespace")) if err != nil { bcode.ReturnError(req, res, err) return } - if err := res.WriteEntity(deliveryTargets); err != nil { bcode.ReturnError(req, res, err) return diff --git a/pkg/apiserver/rest/webservice/webservice.go b/pkg/apiserver/rest/webservice/webservice.go index 8463ca307..047b38657 100644 --- a/pkg/apiserver/rest/webservice/webservice.go +++ b/pkg/apiserver/rest/webservice/webservice.go @@ -60,15 +60,16 @@ func returns500(b *restful.RouteBuilder) { func Init(ds datastore.DataStore) { clusterUsecase := usecase.NewClusterUsecase(ds) workflowUsecase := usecase.NewWorkflowUsecase(ds) - applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase) + deliveryTargetUsecase := usecase.NewDeliveryTargetUsecase(ds) namespaceUsecase := usecase.NewNamespaceUsecase() oamApplicationUsecase := usecase.NewOAMApplicationUsecase() velaQLUsecase := usecase.NewVelaQLUsecase() definitionUsecase := usecase.NewDefinitionUsecase() addonUsecase := usecase.NewAddonUsecase(ds) - deliveryTargetUsecase := usecase.NewDeliveryTargetUsecase(ds) + envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase) + applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, deliveryTargetUsecase) RegistWebService(NewClusterWebService(clusterUsecase)) - RegistWebService(NewApplicationWebService(applicationUsecase)) + RegistWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase)) RegistWebService(NewNamespaceWebService(namespaceUsecase)) RegistWebService(NewDefinitionWebservice(definitionUsecase)) RegistWebService(NewAddonWebService(addonUsecase)) @@ -76,6 +77,6 @@ func Init(ds datastore.DataStore) { RegistWebService(NewOAMApplication(oamApplicationUsecase)) RegistWebService(&policyDefinitionWebservice{}) RegistWebService(NewWorkflowWebService(workflowUsecase, applicationUsecase)) - RegistWebService(NewDeliveryTargetWebService(deliveryTargetUsecase)) + RegistWebService(NewDeliveryTargetWebService(deliveryTargetUsecase, applicationUsecase)) RegistWebService(NewVelaQLWebService(velaQLUsecase)) } diff --git a/test/e2e-apiserver-test/application_test.go b/test/e2e-apiserver-test/application_test.go index 48387fab7..4b107b2ff 100644 --- a/test/e2e-apiserver-test/application_test.go +++ b/test/e2e-apiserver-test/application_test.go @@ -59,7 +59,6 @@ var _ = Describe("Test application rest api", func() { Expect(cmp.Diff(appBase.Description, req.Description)).Should(BeEmpty()) Expect(cmp.Diff(appBase.Namespace, req.Namespace)).Should(BeEmpty()) Expect(cmp.Diff(appBase.Labels["test"], req.Labels["test"])).Should(BeEmpty()) - Expect(cmp.Diff(appBase.EnvBinding[0].Name, "dev-env")).Should(BeEmpty()) }) It("Test delete app", func() {