mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-20 13:06:46 +00:00
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 <zhuxiaobing@lixiang.com>
This commit is contained in:
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 != "" {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user