Feat: support update component and query app statistics info (#2746)

* Feat: change swagger config

* Feat: support update component and query app statistics info

* Fix: fix workflow list bug

* Fix: fix test bug

* Fix: fix e2e test bug

* Feat: change workflow api

* Fix: fix app deploy e2e test bug

* Fix: change e2e test

* Fix: fix workflow bug

* Fix: fix deploy bug

* Fix: fix selector bug

* Feat: support recycle env

* Fix: debug e2e

* Fix: fix e2e case bug

Co-authored-by: barnettZQG <yiyun.pro>
This commit is contained in:
barnettZQG
2021-11-20 19:50:36 +08:00
committed by GitHub
co-authored by lnx01
parent ff405cd62a
commit adcb7bd65e
19 changed files with 1793 additions and 1194 deletions
+718 -510
View File
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -17,6 +17,7 @@ limitations under the License.
package model
import (
"fmt"
"strconv"
"time"
@@ -51,6 +52,7 @@ type WorkflowStep struct {
OrderIndex int `json:"orderIndex"`
Inputs common.StepInputs `json:"inputs,omitempty"`
Outputs common.StepOutputs `json:"outputs,omitempty"`
DependsOn []string `json:"dependsOn"`
Properties *JSONStruct `json:"properties,omitempty"`
}
@@ -61,7 +63,7 @@ func (w *Workflow) TableName() string {
// PrimaryKey return custom primary key
func (w *Workflow) PrimaryKey() string {
return w.Name
return fmt.Sprintf("%s-%s", w.AppPrimaryKey, w.Name)
}
// Index return custom primary key
@@ -83,7 +85,7 @@ func (w *Workflow) Index() map[string]string {
// WorkflowRecord is the workflow record database model
type WorkflowRecord struct {
Model
WorkflowPrimaryKey string `json:"workflowPrimaryKey"`
WorkflowName string `json:"workflowName"`
AppPrimaryKey string `json:"appPrimaryKey"`
RevisionPrimaryKey string `json:"revisionPrimaryKey"`
Name string `json:"name"`
@@ -113,8 +115,8 @@ func (w *WorkflowRecord) Index() map[string]string {
if w.Namespace != "" {
index["namespace"] = w.Namespace
}
if w.WorkflowPrimaryKey != "" {
index["workflowPrimaryKey"] = w.WorkflowPrimaryKey
if w.WorkflowName != "" {
index["workflowPrimaryKey"] = w.WorkflowName
}
if w.AppPrimaryKey != "" {
index["appPrimaryKey"] = w.AppPrimaryKey
+44 -23
View File
@@ -39,6 +39,8 @@ var (
CtxKeyDeliveryTarget = "delivery-target"
// CtxKeyApplicationEnvBinding request context key of env binding
CtxKeyApplicationEnvBinding = "envbinding-policy"
// CtxKeyApplicationComponent request context key of component
CtxKeyApplicationComponent = "component"
)
// AddonPhase defines the phase of an addon
@@ -283,6 +285,14 @@ type ApplicationStatusResponse struct {
Status *common.AppStatus `json:"status"`
}
// ApplicationStatisticsResponse application statistics response body
type ApplicationStatisticsResponse struct {
EnvCount int64 `json:"envCount"`
DeliveryTargetCount int64 `json:"deliveryTargetCount"`
RevisonCount int64 `json:"revisonCount"`
WorkflowCount int64 `json:"workflowCount"`
}
// CreateApplicationRequest create application request body
type CreateApplicationRequest struct {
Name string `json:"name" validate:"checkname"`
@@ -315,13 +325,15 @@ type EnvBinding struct {
// 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"`
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"`
Targets []DeliveryTargetBase `json:"deliveryTargets,omitempty"`
ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"`
CreateTime time.Time `json:"createTime"`
UpdateTime time.Time `json:"updateTime"`
AppDeployName string `json:"appDeployName"`
}
// DetailEnvBindingResponse defines the response of env-binding details
@@ -344,11 +356,10 @@ type ComponentSelector struct {
// DetailApplicationResponse application detail
type DetailApplicationResponse struct {
ApplicationBase
Policies []string `json:"policies"`
EnvBindings []string `json:"envBindings"`
Status string `json:"status"`
ResourceInfo ApplicationResourceInfo `json:"resourceInfo"`
WorkflowStatus []WorkflowStepStatus `json:"workflowStatus"`
Policies []string `json:"policies"`
EnvBindings []string `json:"envBindings"`
Status string `json:"status"`
ResourceInfo ApplicationResourceInfo `json:"resourceInfo"`
}
// WorkflowStepStatus workflow step status model
@@ -360,7 +371,7 @@ type WorkflowStepStatus struct {
// ApplicationResourceInfo application-level resource consumption statistics
type ApplicationResourceInfo struct {
ComponentNum int `json:"componentNum"`
ComponentNum int64 `json:"componentNum"`
// Others, such as: Memory、CPU、GPU、Storage
}
@@ -398,6 +409,16 @@ type CreateComponentRequest struct {
Traits []*CreateApplicationTraitRequest `json:"traits,omitempty" optional:"true"`
}
// UpdateApplicationComponentRequest update component request body
type UpdateApplicationComponentRequest struct {
Alias *string `json:"alias" validate:"checkalias" optional:"true"`
Description *string `json:"description" optional:"true"`
Icon *string `json:"icon" optional:"true"`
Labels *map[string]string `json:"labels,omitempty"`
Properties *string `json:"properties,omitempty"`
DependsOn *[]string `json:"dependsOn" optional:"true"`
}
// DetailComponentResponse detail component response body
type DetailComponentResponse struct {
model.ApplicationComponent
@@ -567,8 +588,6 @@ type WorkflowStep struct {
// DetailWorkflowResponse detail workflow response
type DetailWorkflowResponse struct {
WorkflowBase
Steps []WorkflowStep `json:"steps,omitempty"`
LastRecord *WorkflowRecord `json:"workflowRecord"`
}
// ListWorkflowResponse list application workflows
@@ -578,14 +597,15 @@ type ListWorkflowResponse struct {
// WorkflowBase workflow base model
type WorkflowBase struct {
Name string `json:"name"`
Alias string `json:"alias"`
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"`
Name string `json:"name"`
Alias string `json:"alias"`
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"`
Steps []WorkflowStep `json:"steps,omitempty"`
}
// ListWorkflowRecordsResponse list workflow execution record
@@ -722,6 +742,7 @@ type DeliveryTargetBase struct {
Variable map[string]interface{} `json:"variable,omitempty"`
CreateTime time.Time `json:"createTime"`
UpdateTime time.Time `json:"updateTime"`
AppNum int64 `json:"appNum,omitempty"`
}
// ApplicationRevisionBase application revision base spec
+178 -124
View File
@@ -18,7 +18,6 @@ package usecase
import (
"context"
"encoding/json"
"errors"
"fmt"
"sort"
@@ -28,6 +27,8 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
@@ -68,10 +69,12 @@ type ApplicationUsecase interface {
UpdateApplication(context.Context, *model.Application, apisv1.UpdateApplicationRequest) (*apisv1.ApplicationBase, error)
DeleteApplication(ctx context.Context, app *model.Application) error
Deploy(ctx context.Context, app *model.Application, req apisv1.ApplicationDeployRequest) (*apisv1.ApplicationDeployResponse, error)
GetApplicationComponent(ctx context.Context, app *model.Application, componentName string) (*model.ApplicationComponent, error)
ListComponents(ctx context.Context, app *model.Application, op apisv1.ListApplicationComponentOptions) ([]*apisv1.ComponentBase, error)
AddComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest) (*apisv1.ComponentBase, error)
DetailComponent(ctx context.Context, app *model.Application, componentName string) (*apisv1.DetailComponentResponse, error)
DeleteComponent(ctx context.Context, app *model.Application, componentName string) error
UpdateComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.UpdateApplicationComponentRequest) (*apisv1.ComponentBase, error)
ListPolicies(ctx context.Context, app *model.Application) ([]*apisv1.PolicyBase, error)
AddPolicy(ctx context.Context, app *model.Application, policy apisv1.CreatePolicyRequest) (*apisv1.PolicyBase, error)
DetailPolicy(ctx context.Context, app *model.Application, policyName string) (*apisv1.DetailPolicyResponse, error)
@@ -81,7 +84,8 @@ type ApplicationUsecase interface {
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)
ListRevisions(ctx context.Context, appName, envName, status string, page, pageSize int) (*apisv1.ListRevisionsResponse, error)
DetailRevision(ctx context.Context, appName, revisionVersion string) (*apisv1.DetailRevisionResponse, error)
DetailRevision(ctx context.Context, appName, revisionName string) (*apisv1.DetailRevisionResponse, error)
Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error)
}
type applicationUsecaseImpl struct {
@@ -163,7 +167,7 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
if err != nil {
return nil, err
}
components, err := c.ListComponents(ctx, app, apisv1.ListApplicationComponentOptions{})
componentNum, err := c.ds.Count(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{})
if err != nil {
return nil, err
}
@@ -184,9 +188,8 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
Policies: policyNames,
EnvBindings: envBindingNames,
ResourceInfo: apisv1.ApplicationResourceInfo{
ComponentNum: len(components),
ComponentNum: componentNum,
},
WorkflowStatus: []apisv1.WorkflowStepStatus{},
}
return detail, nil
}
@@ -194,7 +197,7 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
// GetApplicationStatus get application status from controller cluster
func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmodel *model.Application, envName string) (*common.AppStatus, error) {
var app v1beta1.Application
err := c.kubeClient.Get(ctx, types.NamespacedName{Namespace: appmodel.Namespace, Name: converAppName(appmodel, envName)}, &app)
err := c.kubeClient.Get(ctx, types.NamespacedName{Namespace: appmodel.Namespace, Name: converAppName(appmodel.Name, envName)}, &app)
if err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
@@ -204,6 +207,27 @@ func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmo
return &app.Status, nil
}
// GetApplicationCR get application cr in cluster
func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appmodel *model.Application) (*v1beta1.ApplicationList, error) {
var apps v1beta1.ApplicationList
selector := labels.NewSelector()
re, err := labels.NewRequirement(oam.AnnotationAppName, selection.Equals, []string{appmodel.Name})
if err != nil {
return nil, err
}
selector = selector.Add(*re)
err = c.kubeClient.List(ctx, &apps, &client.ListOptions{
LabelSelector: selector,
})
if err != nil {
if apierrors.IsNotFound(err) {
return &apps, nil
}
return nil, err
}
return &apps, nil
}
// PublishApplicationTemplate publish app template
func (c *applicationUsecaseImpl) PublishApplicationTemplate(ctx context.Context, app *model.Application) (*apisv1.ApplicationTemplateBase, error) {
//TODO:
@@ -247,13 +271,6 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
return nil, err
}
}
if oamApp.Spec.Workflow != nil && len(oamApp.Spec.Workflow.Steps) > 0 {
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
@@ -301,46 +318,12 @@ func (c *applicationUsecaseImpl) genPolicyByEnv(ctx context.Context, app *model.
}
properties, err := model.NewJSONStructByStruct(envBindingSpec)
if err != nil {
return appPolicy, err
return appPolicy, bcode.ErrInvalidProperties
}
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 {
@@ -495,7 +478,6 @@ func (c *applicationUsecaseImpl) converPolicyModelToBase(policy *model.Applicati
func (c *applicationUsecaseImpl) saveApplicationPolicy(ctx context.Context, app *model.Application, policys []v1beta1.AppPolicy) error {
var policyModels []datastore.Entity
var envbindingPolicy *model.ApplicationPolicy
for _, policy := range policys {
properties, err := model.NewJSONStruct(policy.Properties)
if err != nil {
@@ -510,26 +492,6 @@ func (c *applicationUsecaseImpl) saveApplicationPolicy(ctx context.Context, app
}
if policy.Type != string(EnvBindingPolicy) {
policyModels = append(policyModels, appPolicy)
} else {
envbindingPolicy = appPolicy
}
}
// If multiple configurations are configured, enable only the last one.
if envbindingPolicy != nil {
envbindingPolicy.Name = EnvBindingPolicyDefaultName
policyModels = append(policyModels, envbindingPolicy)
var envBindingSpec v1alpha1.EnvBindingSpec
if err := json.Unmarshal([]byte(envbindingPolicy.Properties.JSON()), &envBindingSpec); err != nil {
return fmt.Errorf("unmarshal env binding policy failure %w", err)
}
for _, env := range envBindingSpec.Envs {
envBind := &model.EnvBinding{
Name: env.Name,
Description: "",
}
if env.Selector != nil {
envBind.ComponentSelector = (*model.ComponentSelector)(env.Selector)
}
}
}
return c.ds.BatchAdd(ctx, policyModels)
@@ -578,26 +540,30 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
return nil, err
}
configByte, _ := yaml.Marshal(oamApp)
workflow, err := c.workflowUsecase.GetWorkflow(ctx, app, oamApp.Annotations[oam.AnnotationWorkflowName])
if err != nil {
return nil, err
}
// step2: check and create deploy event
if !req.Force {
var lastVersion = model.ApplicationRevision{
AppPrimaryKey: app.PrimaryKey(),
EnvName: workflow.EnvName,
}
list, err := c.ds.List(ctx, &lastVersion, &datastore.ListOptions{PageSize: 1, Page: 1})
list, err := c.ds.List(ctx, &lastVersion, &datastore.ListOptions{
PageSize: 1, Page: 1, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
if err != nil && !errors.Is(err, datastore.ErrRecordNotExist) {
log.Logger.Errorf("query app latest revision failure %s", err.Error())
return nil, bcode.ErrDeployConflict
}
if len(list) > 0 && list[0].(*model.ApplicationRevision).Status != model.RevisionStatusComplete {
log.Logger.Warnf("last app revision can not complete %s/%s", list[0].(*model.ApplicationRevision).AppPrimaryKey, list[0].(*model.ApplicationRevision).Version)
return nil, bcode.ErrDeployConflict
}
}
workflow, err := c.workflowUsecase.GetWorkflow(ctx, oamApp.Annotations[oam.AnnotationWorkflowName])
if err != nil {
return nil, err
}
var appRevision = &model.ApplicationRevision{
AppPrimaryKey: app.PrimaryKey(),
Version: version,
@@ -615,7 +581,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
return nil, err
}
// step3: create workflow record
if err := c.workflowUsecase.CreateWorkflowRecord(ctx, oamApp); err != nil {
if err := c.workflowUsecase.CreateWorkflowRecord(ctx, app, oamApp, workflow); err != nil {
return nil, err
}
// step4: check and create namespace
@@ -657,19 +623,46 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
}
func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appModel *model.Application, reqWorkflowName, version string) (*v1beta1.Application, error) {
// Priority 1 uses the requested workflow as release .
// Priority 2 uses the default workflow as release .
var workflow *model.Workflow
var err error
if reqWorkflowName != "" {
workflow, err = c.workflowUsecase.GetWorkflow(ctx, appModel, reqWorkflowName)
if err != nil {
return nil, err
}
} else {
workflow, err = c.workflowUsecase.GetApplicationDefaultWorkflow(ctx, appModel)
if err != nil && !errors.Is(err, bcode.ErrWorkflowNoDefault) {
return nil, err
}
}
if workflow == nil || workflow.EnvName == "" {
return nil, bcode.ErrWorkflowNotExist
}
labels := make(map[string]string)
for key, value := range appModel.Labels {
labels[key] = value
}
labels[oam.AnnotationAppName] = appModel.Name
var app = &v1beta1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: appModel.Name,
Name: converAppName(appModel.Name, workflow.EnvName),
Namespace: appModel.Namespace,
Labels: appModel.Labels,
Labels: labels,
Annotations: map[string]string{
oam.AnnotationDeployVersion: version,
// publish version is the identifier of workflow record
oam.AnnotationPublishVersion: utils.GenerateVersion(reqWorkflowName),
oam.AnnotationAppName: appModel.Name,
oam.AnnotationAppAlias: appModel.Alias,
},
},
}
@@ -705,7 +698,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
traits = append(traits, aTrait)
}
bc := common.ApplicationComponent{
Name: component.Name,
Name: converComponentName(component.Name, workflow.EnvName),
Type: component.Type,
ExternalRevision: component.ExternalRevision,
DependsOn: component.DependsOn,
@@ -732,48 +725,29 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
}
app.Spec.Policies = append(app.Spec.Policies, apolicy)
}
// Priority 1 uses the requested workflow as release .
// Priority 2 uses the default workflow as release .
var workflow *model.Workflow
if reqWorkflowName != "" {
workflow, err = c.workflowUsecase.GetWorkflow(ctx, reqWorkflowName)
if workflow.EnvName != "" {
envPolicy, err := c.genPolicyByEnv(ctx, appModel, workflow.EnvName)
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) {
return nil, err
}
app.Spec.Policies = append(app.Spec.Policies, envPolicy)
}
if workflow != nil {
app.Annotations[oam.AnnotationWorkflowName] = workflow.Name
var steps []v1beta1.WorkflowStep
for _, step := range workflow.Steps {
var wstep = v1beta1.WorkflowStep{
Name: step.Name,
Type: step.Type,
Inputs: step.Inputs,
Outputs: step.Outputs,
}
if step.Properties != nil {
wstep.Properties = step.Properties.RawExtension()
}
steps = append(steps, wstep)
app.Annotations[oam.AnnotationWorkflowName] = workflow.Name
var steps []v1beta1.WorkflowStep
for _, step := range workflow.Steps {
var wstep = v1beta1.WorkflowStep{
Name: step.Name,
Type: step.Type,
Inputs: step.Inputs,
Outputs: step.Outputs,
}
app.Spec.Workflow = &v1beta1.Workflow{
Steps: steps,
if step.Properties != nil {
wstep.Properties = step.Properties.RawExtension()
}
steps = append(steps, wstep)
}
app.Spec.Workflow = &v1beta1.Workflow{
Steps: steps,
}
return app, nil
@@ -796,7 +770,13 @@ func (c *applicationUsecaseImpl) converAppModelToBase(app *model.Application) *a
// DeleteApplication delete application
func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *model.Application) error {
// TODO: check app can be deleted
crs, err := c.GetApplicationCR(ctx, app)
if err != nil {
return err
}
if len(crs.Items) > 0 {
return bcode.ErrApplicationRefusedDelete
}
// query all components to deleted
components, err := c.ListComponents(ctx, app, apisv1.ListApplicationComponentOptions{})
if err != nil {
@@ -809,7 +789,7 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod
}
// delete workflow
if err := c.workflowUsecase.DeleteWorkflow(ctx, app.Name); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) {
if err := c.workflowUsecase.DeleteWorkflowByApp(ctx, app); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) {
log.Logger.Errorf("delete workflow %s failure %s", app.Name, err.Error())
}
@@ -834,6 +814,50 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod
return c.ds.Delete(ctx, app)
}
func (c *applicationUsecaseImpl) GetApplicationComponent(ctx context.Context, app *model.Application, componentName string) (*model.ApplicationComponent, error) {
var component = model.ApplicationComponent{
AppPrimaryKey: app.PrimaryKey(),
Name: componentName,
}
err := c.ds.Get(ctx, &component)
if err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return nil, bcode.ErrApplicationComponetNotExist
}
return nil, err
}
return &component, nil
}
func (c *applicationUsecaseImpl) UpdateComponent(ctx context.Context, app *model.Application, component *model.ApplicationComponent, req apisv1.UpdateApplicationComponentRequest) (*apisv1.ComponentBase, error) {
if req.Alias != nil {
component.Alias = *req.Alias
}
if req.Description != nil {
component.Description = *req.Description
}
if req.DependsOn != nil {
component.DependsOn = *req.DependsOn
}
if req.Icon != nil {
component.Icon = *req.Icon
}
if req.Labels != nil {
component.Labels = *req.Labels
}
if req.Properties != nil {
properties, err := model.NewJSONStructByString(*req.Properties)
if err != nil {
return nil, bcode.ErrInvalidProperties
}
component.Properties = properties
}
if err := c.ds.Put(ctx, component); err != nil {
return nil, err
}
return converComponentModelToBase(component), nil
}
func (c *applicationUsecaseImpl) AddComponent(ctx context.Context, app *model.Application, com apisv1.CreateComponentRequest) (*apisv1.ComponentBase, error) {
componentModel := model.ApplicationComponent{
AppPrimaryKey: app.PrimaryKey(),
@@ -859,6 +883,13 @@ func (c *applicationUsecaseImpl) AddComponent(ctx context.Context, app *model.Ap
log.Logger.Warnf("add component for app %s failure %s", app.PrimaryKey(), err.Error())
return nil, err
}
return converComponentModelToBase(&componentModel), nil
}
func converComponentModelToBase(componentModel *model.ApplicationComponent) *apisv1.ComponentBase {
if componentModel == nil {
return nil
}
return &apisv1.ComponentBase{
Name: componentModel.Name,
Description: componentModel.Description,
@@ -869,7 +900,7 @@ func (c *applicationUsecaseImpl) AddComponent(ctx context.Context, app *model.Ap
Creator: componentModel.Creator,
CreateTime: componentModel.CreateTime,
UpdateTime: componentModel.UpdateTime,
}, nil
}
}
func (c *applicationUsecaseImpl) DeleteComponent(ctx context.Context, app *model.Application, componentName string) error {
@@ -1096,6 +1127,29 @@ func (c *applicationUsecaseImpl) DetailRevision(ctx context.Context, appName, re
}, nil
}
func (c *applicationUsecaseImpl) Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error) {
var targetMap = make(map[string]int)
envbinding, err := c.envBindingUsecase.GetEnvBindings(ctx, app)
if err != nil {
log.Logger.Errorf("query app envbinding failure %s", err.Error())
}
for _, env := range envbinding {
for _, target := range env.TargetNames {
targetMap[target]++
}
}
count, err := c.ds.Count(ctx, &model.ApplicationRevision{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{})
if err != nil {
return nil, err
}
return &apisv1.ApplicationStatisticsResponse{
EnvCount: int64(len(envbinding)),
DeliveryTargetCount: int64(len(targetMap)),
RevisonCount: count,
WorkflowCount: c.workflowUsecase.CountWorkflow(ctx, app),
}, nil
}
func createTargetClusterEnv(envBind apisv1.EnvBindingBase, target *model.DeliveryTarget) v1alpha1.EnvConfig {
placement := v1alpha1.EnvPlacement{}
var componentSelector *v1alpha1.EnvSelector
@@ -1115,18 +1169,18 @@ func createTargetClusterEnv(envBind apisv1.EnvBindingBase, target *model.Deliver
}
}
func converAppName(app *model.Application, envName string) string {
return fmt.Sprintf("%s-%s", app.Name, envName)
func converAppName(appModelName, envName string) string {
return fmt.Sprintf("%s-%s", appModelName, envName)
}
func converComponentName(componentModelName, envName string) string {
return fmt.Sprintf("%s-%s", componentModelName, 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
}
+27 -67
View File
@@ -28,7 +28,6 @@ import (
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
@@ -158,9 +157,8 @@ var _ = Describe("Test application usecase function", func() {
})
It("Test ListApplications function", func() {
apps, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{})
_, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{})
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(apps), 3)).Should(BeEmpty())
})
It("Test DetailApplication function", func() {
@@ -170,29 +168,8 @@ var _ = Describe("Test application usecase function", func() {
detail, err := appUsecase.DetailApplication(context.TODO(), appModel)
Expect(err).Should(BeNil())
Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, 2)).Should(BeEmpty())
Expect(cmp.Diff(len(detail.Policies), 1)).Should(BeEmpty())
})
It("Test GetWorkflow function", func() {
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
Expect(err).Should(BeNil())
Expect(cmp.Diff(appModel.Namespace, "test-app-namespace")).Should(BeEmpty())
_, err = workflowUsecase.GetWorkflow(context.TODO(), "test-app-sadasd")
Expect(err).Should(BeNil())
})
It("Test ListPolicies 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())
policies, err := appUsecase.ListPolicies(context.TODO(), appModel)
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(policies), 1)).Should(BeEmpty())
Expect(cmp.Diff(policies[0].Type, "env-binding")).Should(BeEmpty())
Expect((*policies[0].Properties)["envs"]).ShouldNot(BeEmpty())
Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, int64(2))).Should(BeEmpty())
Expect(cmp.Diff(len(detail.Policies), 0)).Should(BeEmpty())
})
It("Test ListComponents function", func() {
@@ -233,17 +210,6 @@ var _ = Describe("Test application usecase function", func() {
Expect(cmp.Diff(strings.Contains((*detail.Properties)["image"].(string), "crccheck/hello-world"), true)).Should(BeEmpty())
})
It("Test DetailPolicy 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())
detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName)
Expect(err).Should(BeNil())
Expect(cmp.Diff(detail.Type, "env-binding")).Should(BeEmpty())
Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty())
})
It("Test AddComponent function", func() {
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
Expect(err).Should(BeNil())
@@ -279,23 +245,34 @@ var _ = Describe("Test application usecase function", func() {
Name: EnvBindingPolicyDefaultName,
Description: "this is a test2 policy",
Type: "env-binding",
Properties: ``,
})
Expect(cmp.Equal(err, bcode.ErrApplicationPolicyExist, cmpopts.EquateErrors())).Should(BeTrue())
_, err = appUsecase.AddPolicy(context.TODO(), appModel, v1.CreatePolicyRequest{
Name: "env-binding-2",
Description: "this is a test2 policy",
Type: "env-binding",
Properties: `{"envs":{ "name": "test", "placement":{"namespaceSelector":{ "name": "TEST_NAMESPACE"}}, "selector":{ "components": ["data-worker"]}}}`,
})
Expect(err).Should(BeNil())
_, err = appUsecase.AddPolicy(context.TODO(), appModel, v1.CreatePolicyRequest{
Name: EnvBindingPolicyDefaultName,
Description: "this is a test2 policy",
Type: "env-binding",
Properties: ``,
})
Expect(cmp.Equal(err, bcode.ErrApplicationPolicyExist, cmpopts.EquateErrors())).Should(BeTrue())
})
It("Test ListPolicies 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())
policies, err := appUsecase.ListPolicies(context.TODO(), appModel)
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(policies), 1)).Should(BeEmpty())
})
It("Test DetailPolicy 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())
detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, "env-binding-2")
detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName)
Expect(err).Should(BeNil())
Expect(detail.Properties).ShouldNot(BeNil())
Expect((*detail.Properties)["envs"]).ShouldNot(BeEmpty())
@@ -305,7 +282,7 @@ var _ = Describe("Test application usecase 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())
base, err := appUsecase.UpdatePolicy(context.TODO(), appModel, "env-binding-2", v1.UpdatePolicyRequest{
base, err := appUsecase.UpdatePolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName, v1.UpdatePolicyRequest{
Type: "env-binding",
Properties: `{"envs":{}}`,
})
@@ -317,7 +294,7 @@ var _ = Describe("Test application usecase 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.DeletePolicy(context.TODO(), appModel, "env-binding-2")
err = appUsecase.DeletePolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName)
Expect(err).Should(BeNil())
})
@@ -402,24 +379,6 @@ var _ = Describe("Test application usecase function", func() {
Expect(err).Should(BeNil())
})
It("Test Deploy Application 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())
res, err := appUsecase.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{
Note: "unit test deploy",
TriggerType: "api",
})
Expect(err).Should(BeNil())
Expect(cmp.Diff(res.Status, model.RevisionStatusRunning)).Should(BeEmpty())
var oam v1beta1.Application
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: appModel.Name, Namespace: appModel.Namespace}, &oam)
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(oam.Spec.Components), 2)).Should(BeEmpty())
Expect(cmp.Diff(len(oam.Spec.Policies), 1)).Should(BeEmpty())
})
It("Test DeleteApplication function", func() {
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
Expect(err).Should(BeNil())
@@ -478,15 +437,16 @@ var _ = Describe("Test application usecase function", func() {
})
})
func createTestSuspendApp(ctx context.Context, appName, revisionVersion, wfName, recordName string, kubeClient client.Client) (*v1beta1.Application, error) {
func createTestSuspendApp(ctx context.Context, appName, envName, revisionVersion, wfName, recordName string, kubeClient client.Client) (*v1beta1.Application, error) {
testapp := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: appName,
Name: converAppName(appName, envName),
Namespace: "default",
Annotations: map[string]string{
oam.AnnotationDeployVersion: revisionVersion,
oam.AnnotationWorkflowName: wfName,
oam.AnnotationPublishVersion: recordName,
oam.AnnotationAppName: appName,
},
},
Spec: v1beta1.ApplicationSpec{
@@ -154,6 +154,8 @@ func convertCreateReqToDeliveryTargetModel(req apisv1.CreateDeliveryTargetReques
}
func convertFromDeliveryTargetModel(deliveryTarget *model.DeliveryTarget) *apisv1.DeliveryTargetBase {
var appNum int64 = 0
// TODO: query app num in target
return &apisv1.DeliveryTargetBase{
Name: deliveryTarget.Name,
Namespace: deliveryTarget.Namespace,
@@ -163,5 +165,6 @@ func convertFromDeliveryTargetModel(deliveryTarget *model.DeliveryTarget) *apisv
Variable: deliveryTarget.Variable,
CreateTime: deliveryTarget.CreateTime,
UpdateTime: deliveryTarget.UpdateTime,
AppNum: appNum,
}
}
+73 -15
View File
@@ -19,16 +19,21 @@ package usecase
import (
"context"
"errors"
"fmt"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"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
@@ -41,19 +46,26 @@ type EnvBindingUsecase interface {
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)
DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error)
ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error
}
type envBindingUsecaseImpl struct {
ds datastore.DataStore
workflowUsecase WorkflowUsecase
kubeClient client.Client
}
// NewEnvBindingUsecase new envBinding usecase
func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase) EnvBindingUsecase {
kubecli, err := clients.GetKubeClient()
if err != nil {
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
}
return &envBindingUsecaseImpl{
ds: ds,
workflowUsecase: workflowUsecase,
kubeClient: kubecli,
}
}
@@ -65,10 +77,17 @@ func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.A
if err != nil {
return nil, bcode.ErrEnvBindingsNotExist
}
deliveryTarget := model.DeliveryTarget{
Namespace: app.Namespace,
}
deliveryTargets, err := e.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{})
if err != nil {
return nil, err
}
var list []*apisv1.EnvBindingBase
for _, ebd := range envBindings {
eb := ebd.(*model.EnvBinding)
list = append(list, convertEnvbindingModelToBase(eb))
list = append(list, convertEnvbindingModelToBase(app, eb, deliveryTargets))
}
return list, nil
}
@@ -81,7 +100,7 @@ func (e *envBindingUsecaseImpl) GetEnvBinding(ctx context.Context, app *model.Ap
}
return nil, err
}
return e.DetailEnvBinding(ctx, envBinding)
return e.DetailEnvBinding(ctx, app, envBinding)
}
func (e *envBindingUsecaseImpl) CheckAppEnvBindingsContainTarget(ctx context.Context, app *model.Application, targetName string) (bool, error) {
@@ -165,18 +184,23 @@ func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model
if err := e.ds.Put(ctx, envBindingModel); err != nil {
return nil, err
}
return e.DetailEnvBinding(ctx, envBindingModel)
return e.DetailEnvBinding(ctx, app, envBindingModel)
}
func (e *envBindingUsecaseImpl) DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error {
envBinding, err := e.getBindingByEnv(ctx, app, envName)
func (e *envBindingUsecaseImpl) DeleteEnvBinding(ctx context.Context, appModel *model.Application, envName string) error {
envBinding, err := e.getBindingByEnv(ctx, appModel, 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 {
var app v1beta1.Application
err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: converAppName(appModel.Name, envBinding.Name)}, &app)
if err != nil && !apierrors.IsNotFound(err) {
return bcode.ErrApplicationRefusedDelete
}
if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: appModel.PrimaryKey(), Name: envBinding.Name}); err != nil {
return err
}
return nil
@@ -230,7 +254,8 @@ func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *mode
}
_, err := e.workflowUsecase.CreateWorkflow(ctx, app, apisv1.CreateWorkflowRequest{
AppName: app.PrimaryKey(),
Name: genWorkflowName(app, env.Name),
Name: env.Name,
Alias: fmt.Sprintf("%s env workflow", env.Alias),
Description: "Created automatically by envbinding.",
EnvName: env.Name,
Steps: steps,
@@ -242,12 +267,31 @@ func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *mode
return nil
}
func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) {
func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) {
deliveryTarget := model.DeliveryTarget{
Namespace: app.Namespace,
}
deliveryTargets, err := e.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{})
if err != nil {
return nil, err
}
return &apisv1.DetailEnvBindingResponse{
EnvBindingBase: *convertEnvbindingModelToBase(envBinding),
EnvBindingBase: *convertEnvbindingModelToBase(app, envBinding, deliveryTargets),
}, nil
}
func (e *envBindingUsecaseImpl) ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error {
var app v1beta1.Application
err := e.kubeClient.Get(ctx, types.NamespacedName{Namespace: app.Namespace, Name: converAppName(appModel.Name, envBinding.Name)}, &app)
if err != nil {
if apierrors.IsNotFound(err) {
return nil
}
return err
}
return e.kubeClient.Delete(ctx, &app)
}
func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvRequest) model.EnvBinding {
envBinding := model.EnvBinding{
AppPrimaryKey: app.Name,
@@ -259,15 +303,29 @@ func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.Create
return envBinding
}
func convertEnvbindingModelToBase(envBinding *model.EnvBinding) *apisv1.EnvBindingBase {
func convertEnvbindingModelToBase(app *model.Application, envBinding *model.EnvBinding, deliveryTargets []datastore.Entity) *apisv1.EnvBindingBase {
var dtMap = make(map[string]*model.DeliveryTarget, len(deliveryTargets))
for _, dte := range deliveryTargets {
dt := dte.(*model.DeliveryTarget)
dtMap[dt.Name] = dt
}
var targets []apisv1.DeliveryTargetBase
for _, targetName := range envBinding.TargetNames {
dt := dtMap[targetName]
if dt != nil {
targets = append(targets, *convertFromDeliveryTargetModel(dt))
}
}
ebb := &apisv1.EnvBindingBase{
Name: envBinding.Name,
Alias: envBinding.Alias,
Description: envBinding.Description,
TargetNames: envBinding.TargetNames,
Targets: targets,
ComponentSelector: (*apisv1.ComponentSelector)(envBinding.ComponentSelector),
CreateTime: envBinding.CreateTime,
UpdateTime: envBinding.UpdateTime,
AppDeployName: converAppName(app.Name, envBinding.Name),
}
return ebb
}
+92 -92
View File
@@ -99,20 +99,6 @@
label: 持久化存储
sort: 11
subParameters:
- description: ""
jsonKey: mountPath
label: MountPath
sort: 100
uiType: Input
validate:
required: true
- description: ""
jsonKey: name
label: Name
sort: 100
uiType: Input
validate:
required: true
- description: 'Specify volume type, options: "pvc","configMap","secret","emptyDir"'
jsonKey: type
label: Type
@@ -129,6 +115,20 @@
- label: EmptyDir
value: emptyDir
required: true
- description: ""
jsonKey: mountPath
label: MountPath
sort: 100
uiType: Input
validate:
required: true
- description: ""
jsonKey: name
label: Name
sort: 100
uiType: Input
validate:
required: true
uiType: Structs
validate: {}
- description: Instructions for assessing whether the container is in a suitable state
@@ -137,6 +137,24 @@
label: ReadinessProbe检测
sort: 13
subParameters:
- description: Instructions for assessing container health by probing a TCP socket.
Either this attribute or the exec attribute or the httpGet attribute MUST be
specified. This attribute is mutually exclusive with both the exec attribute
and the httpGet attribute.
jsonKey: tcpSocket
label: TcpSocket
sort: 100
subParameters:
- description: The TCP socket within the container that should be probed to assess
container health.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
uiType: KV
validate: {}
- description: Number of seconds after which the probe times out.
jsonKey: timeoutSeconds
label: TimeoutSeconds
@@ -182,6 +200,14 @@
label: HttpGet
sort: 100
subParameters:
- description: The TCP socket within the container to which the HTTP GET request
should be directed.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
- description: ""
jsonKey: httpHeaders
label: HttpHeaders
@@ -211,14 +237,6 @@
uiType: Input
validate:
required: true
- description: The TCP socket within the container to which the HTTP GET request
should be directed.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
uiType: KV
validate: {}
- description: Number of seconds after the container is started before the first
@@ -247,24 +265,6 @@
validate:
defaultValue: 1
required: true
- description: Instructions for assessing container health by probing a TCP socket.
Either this attribute or the exec attribute or the httpGet attribute MUST be
specified. This attribute is mutually exclusive with both the exec attribute
and the httpGet attribute.
jsonKey: tcpSocket
label: TcpSocket
sort: 100
subParameters:
- description: The TCP socket within the container that should be probed to assess
container health.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
uiType: KV
validate: {}
uiType: Group
validate: {}
- description: Instructions for assessing whether the container is alive.
@@ -272,6 +272,52 @@
label: LivenessProbe检测
sort: 15
subParameters:
- description: Instructions for assessing container health by probing a TCP socket.
Either this attribute or the exec attribute or the httpGet attribute MUST be
specified. This attribute is mutually exclusive with both the exec attribute
and the httpGet attribute.
jsonKey: tcpSocket
label: TcpSocket
sort: 100
subParameters:
- description: The TCP socket within the container that should be probed to assess
container health.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
uiType: KV
validate: {}
- description: Number of seconds after which the probe times out.
jsonKey: timeoutSeconds
label: TimeoutSeconds
sort: 100
uiType: Number
validate:
defaultValue: 1
required: true
- description: Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute MUST
be specified. This attribute is mutually exclusive with both the httpGet attribute
and the tcpSocket attribute.
jsonKey: exec
label: Exec
sort: 100
subParameters:
- description: A command to be executed inside the container to assess its health.
Each space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit codes
are considered failures.
jsonKey: command
label: Command
sort: 100
uiType: Strings
validate:
required: true
uiType: KV
validate: {}
- description: Number of consecutive failures required to determine the container
is not alive (liveness probe) or not ready (readiness probe).
jsonKey: failureThreshold
@@ -354,52 +400,6 @@
validate:
defaultValue: 1
required: true
- description: Instructions for assessing container health by probing a TCP socket.
Either this attribute or the exec attribute or the httpGet attribute MUST be
specified. This attribute is mutually exclusive with both the exec attribute
and the httpGet attribute.
jsonKey: tcpSocket
label: TcpSocket
sort: 100
subParameters:
- description: The TCP socket within the container that should be probed to assess
container health.
jsonKey: port
label: Port
sort: 100
uiType: Number
validate:
required: true
uiType: KV
validate: {}
- description: Number of seconds after which the probe times out.
jsonKey: timeoutSeconds
label: TimeoutSeconds
sort: 100
uiType: Number
validate:
defaultValue: 1
required: true
- description: Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute MUST
be specified. This attribute is mutually exclusive with both the httpGet attribute
and the tcpSocket attribute.
jsonKey: exec
label: Exec
sort: 100
subParameters:
- description: A command to be executed inside the container to assess its health.
Each space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit codes
are considered failures.
jsonKey: command
label: Command
sort: 100
uiType: Strings
validate:
required: true
uiType: KV
validate: {}
uiType: Group
validate: {}
- description: Specify image pull policy for your service
@@ -416,6 +416,12 @@
value: Always
- label: 永不更新
value: Never
- description: Specify image pull secrets for your service
jsonKey: imagePullSecrets
label: ImagePullSecrets
sort: 100
uiType: Strings
validate: {}
- description: If addRevisionLabel is true, the appRevision label will be added to
the underlying pods
jsonKey: addRevisionLabel
@@ -425,9 +431,3 @@
validate:
defaultValue: false
required: true
- description: Specify image pull secrets for your service
jsonKey: imagePullSecrets
label: ImagePullSecrets
sort: 100
uiType: Strings
validate: {}
+144 -91
View File
@@ -48,20 +48,22 @@ const (
// WorkflowUsecase workflow manage api
type WorkflowUsecase interface {
ListApplicationWorkflow(ctx context.Context, app *model.Application, enable *bool) ([]*apisv1.WorkflowBase, error)
GetWorkflow(ctx context.Context, workflowName string) (*model.Workflow, error)
ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error)
GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error)
DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error)
GetApplicationDefaultWorkflow(ctx context.Context, app *model.Application) (*model.Workflow, error)
DeleteWorkflow(ctx context.Context, workflowName string) error
DeleteWorkflow(ctx context.Context, app *model.Application, workflowName string) error
DeleteWorkflowByApp(ctx context.Context, app *model.Application) error
CreateWorkflow(ctx context.Context, app *model.Application, req apisv1.CreateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error)
CreateWorkflowRecord(ctx context.Context, app *v1beta1.Application) error
UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error)
ListWorkflowRecords(ctx context.Context, workflowName string, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error)
DetailWorkflowRecord(ctx context.Context, workflowName, recordName string) (*apisv1.DetailWorkflowRecordResponse, error)
CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error
ListWorkflowRecords(ctx context.Context, workflow *model.Workflow, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error)
DetailWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*apisv1.DetailWorkflowRecordResponse, error)
SyncWorkflowRecord(ctx context.Context) error
ResumeRecord(ctx context.Context, appModel *model.Application, recordName string) error
TerminateRecord(ctx context.Context, appModel *model.Application, recordName string) error
RollbackRecord(ctx context.Context, appModel *model.Application, recordName, revisionName string) error
ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error
TerminateRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error
RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionName string) error
CountWorkflow(ctx context.Context, app *model.Application) int64
}
// NewWorkflowUsecase new workflow usecase
@@ -84,9 +86,10 @@ type workflowUsecaseImpl struct {
}
// DeleteWorkflow delete application workflow
func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, workflowName string) error {
func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, app *model.Application, workflowName string) error {
var workflow = &model.Workflow{
Name: workflowName,
Name: workflowName,
AppPrimaryKey: app.PrimaryKey(),
}
if err := w.ds.Delete(ctx, workflow); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
@@ -97,7 +100,30 @@ func (w *workflowUsecaseImpl) DeleteWorkflow(ctx context.Context, workflowName s
return nil
}
func (w *workflowUsecaseImpl) DeleteWorkflowByApp(ctx context.Context, app *model.Application) error {
var workflow = &model.Workflow{
AppPrimaryKey: app.PrimaryKey(),
}
workflows, err := w.ds.List(ctx, workflow, &datastore.ListOptions{})
if err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return nil
}
return err
}
for i := range workflows {
if err := w.ds.Delete(ctx, workflows[i]); err != nil {
log.Logger.Errorf("delete workflow %s failure %s", workflows[i].PrimaryKey(), err.Error())
}
}
return nil
}
func (w *workflowUsecaseImpl) CreateWorkflow(ctx context.Context, app *model.Application, req apisv1.CreateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) {
if req.EnvName == "" {
return nil, bcode.ErrWorkflowNoEnv
}
var steps []model.WorkflowStep
for _, step := range req.Steps {
properties, err := model.NewJSONStructByString(step.Properties)
@@ -106,11 +132,13 @@ func (w *workflowUsecaseImpl) CreateWorkflow(ctx context.Context, app *model.App
return nil, bcode.ErrInvalidProperties
}
steps = append(steps, model.WorkflowStep{
Name: step.Name,
Type: step.Type,
Inputs: step.Inputs,
Outputs: step.Outputs,
Properties: properties,
Name: step.Name,
Type: step.Type,
Inputs: step.Inputs,
Outputs: step.Outputs,
Description: step.Description,
DependsOn: step.DependsOn,
Properties: properties,
})
}
// It is allowed to set multiple workflows as default, and only one takes effect.
@@ -155,39 +183,46 @@ func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *mode
return w.DetailWorkflow(ctx, workflow)
}
// DetailWorkflow detail workflow
func (w *workflowUsecaseImpl) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) {
func converWorkflowBase(workflow *model.Workflow) apisv1.WorkflowBase {
var steps []apisv1.WorkflowStep
for _, step := range workflow.Steps {
apiStep := apisv1.WorkflowStep{
Name: step.Name,
Type: step.Type,
Inputs: step.Inputs,
Outputs: step.Outputs,
Properties: step.Properties.JSON(),
Name: step.Name,
Type: step.Type,
Description: step.Description,
Inputs: step.Inputs,
Outputs: step.Outputs,
Properties: step.Properties.JSON(),
DependsOn: step.DependsOn,
}
if step.Properties != nil {
apiStep.Properties = step.Properties.JSON()
}
steps = append(steps, apiStep)
}
return apisv1.WorkflowBase{
Name: workflow.Name,
Description: workflow.Description,
Default: workflow.Default,
EnvName: workflow.EnvName,
CreateTime: workflow.CreateTime,
UpdateTime: workflow.UpdateTime,
Steps: steps,
}
}
// DetailWorkflow detail workflow
func (w *workflowUsecaseImpl) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) {
return &apisv1.DetailWorkflowResponse{
WorkflowBase: apisv1.WorkflowBase{
Name: workflow.Name,
Description: workflow.Description,
Default: workflow.Default,
EnvName: workflow.EnvName,
CreateTime: workflow.CreateTime,
UpdateTime: workflow.UpdateTime,
},
Steps: steps,
WorkflowBase: converWorkflowBase(workflow),
}, nil
}
// GetWorkflow get workflow model
func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, workflowName string) (*model.Workflow, error) {
func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error) {
var workflow = model.Workflow{
Name: workflowName,
Name: workflowName,
AppPrimaryKey: app.PrimaryKey(),
}
if err := w.ds.Get(ctx, &workflow); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
@@ -199,7 +234,7 @@ func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, workflowName stri
}
// ListApplicationWorkflow list application workflows
func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app *model.Application, enable *bool) ([]*apisv1.WorkflowBase, error) {
func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app *model.Application) ([]*apisv1.WorkflowBase, error) {
var workflow = model.Workflow{
AppPrimaryKey: app.PrimaryKey(),
}
@@ -210,14 +245,8 @@ func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app *
var list []*apisv1.WorkflowBase
for _, workflow := range workflows {
wm := workflow.(*model.Workflow)
list = append(list, &apisv1.WorkflowBase{
Name: wm.Name,
Description: wm.Description,
Default: wm.Default,
EnvName: wm.EnvName,
CreateTime: wm.CreateTime,
UpdateTime: wm.UpdateTime,
})
base := converWorkflowBase(wm)
list = append(list, &base)
}
return list, nil
}
@@ -239,9 +268,10 @@ func (w *workflowUsecaseImpl) GetApplicationDefaultWorkflow(ctx context.Context,
}
// ListWorkflowRecords list workflow record
func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflowName string, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error) {
func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflow *model.Workflow, page, pageSize int) (*apisv1.ListWorkflowRecordsResponse, error) {
var record = model.WorkflowRecord{
WorkflowPrimaryKey: workflowName,
AppPrimaryKey: workflow.AppPrimaryKey,
WorkflowName: workflow.Name,
}
records, err := w.ds.List(ctx, &record, &datastore.ListOptions{Page: page, PageSize: pageSize})
if err != nil {
@@ -267,13 +297,17 @@ func (w *workflowUsecaseImpl) ListWorkflowRecords(ctx context.Context, workflowN
}
// DetailWorkflowRecord get workflow record detail with name
func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflowName, recordName string) (*apisv1.DetailWorkflowRecordResponse, error) {
func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow *model.Workflow, recordName string) (*apisv1.DetailWorkflowRecordResponse, error) {
var record = model.WorkflowRecord{
WorkflowPrimaryKey: workflowName,
Name: recordName,
AppPrimaryKey: workflow.AppPrimaryKey,
WorkflowName: workflow.Name,
Name: recordName,
}
err := w.ds.Get(ctx, &record)
if err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return nil, bcode.ErrWorkflowRecordNotExist
}
return nil, err
}
@@ -283,6 +317,9 @@ func (w *workflowUsecaseImpl) DetailWorkflowRecord(ctx context.Context, workflow
}
err = w.ds.Get(ctx, &revision)
if err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return nil, bcode.ErrApplicationRevisionNotExist
}
return nil, err
}
@@ -307,23 +344,27 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error {
for _, item := range records {
app := &v1beta1.Application{}
index := item.Index()
appPrimaryKey := index["appPrimaryKey"]
namespace := index["namespace"]
recordName := index["name"]
record := item.(*model.WorkflowRecord)
workflow := &model.Workflow{
Name: record.WorkflowName,
AppPrimaryKey: record.AppPrimaryKey,
}
if err := w.ds.Get(ctx, workflow); err != nil {
log.Logger.Errorf("get workflow %s/%s failure %s", record.AppPrimaryKey, record.WorkflowName, err.Error())
continue
}
if err := w.kubeClient.Get(ctx, types.NamespacedName{
Name: appPrimaryKey,
Namespace: namespace,
Name: converAppName(record.AppPrimaryKey, workflow.EnvName),
Namespace: record.Namespace,
}, app); err != nil {
klog.ErrorS(err, "failed to get app", "app name", appPrimaryKey)
klog.ErrorS(err, "failed to get app", "app name", record.AppPrimaryKey)
return err
}
// try to sync the status from the running application
if app.Annotations != nil && app.Annotations[oam.AnnotationPublishVersion] == recordName {
if err := w.syncWorkflowStatus(ctx, app, recordName); err != nil {
klog.ErrorS(err, "failed to sync workflow status", "app name", appPrimaryKey, "workflow record name", recordName)
if app.Annotations != nil && app.Annotations[oam.AnnotationPublishVersion] == record.Name {
if err := w.syncWorkflowStatus(ctx, app, record.Name); err != nil {
klog.ErrorS(err, "failed to sync workflow status", "app name", record.AppPrimaryKey, "workflow record name", record.Name)
}
continue
}
@@ -331,19 +372,19 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error {
// try to sync the status from the controller revision
cr := &appsv1.ControllerRevision{}
if err := w.kubeClient.Get(ctx, types.NamespacedName{
Name: fmt.Sprintf("record-%s-%s", appPrimaryKey, recordName),
Namespace: namespace,
Name: fmt.Sprintf("record-%s-%s", record.AppPrimaryKey, record.Name),
Namespace: record.Namespace,
}, cr); err != nil {
klog.ErrorS(err, "failed to get controller revision", "app name", appPrimaryKey, "workflow record name", recordName)
klog.ErrorS(err, "failed to get controller revision", "app name", record.AppPrimaryKey, "workflow record name", record.Name)
continue
}
appInRevision, err := util.RawExtension2Application(cr.Data)
if err != nil {
klog.ErrorS(err, "failed to get app data in controller revision", "controller revision name", cr.Name, "app name", appPrimaryKey, "workflow record name", recordName)
klog.ErrorS(err, "failed to get app data in controller revision", "controller revision name", cr.Name, "app name", record.AppPrimaryKey, "workflow record name", record.Name)
continue
}
if err := w.syncWorkflowStatus(ctx, appInRevision, recordName); err != nil {
klog.ErrorS(err, "failed to sync workflow status", "app name", appPrimaryKey, "workflow record version", recordName)
if err := w.syncWorkflowStatus(ctx, appInRevision, record.Name); err != nil {
klog.ErrorS(err, "failed to sync workflow status", "app name", record.AppPrimaryKey, "workflow record version", record.Name)
continue
}
@@ -353,18 +394,26 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error {
}
func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1beta1.Application, recordName string) error {
var record = &model.WorkflowRecord{
AppPrimaryKey: app.Name,
AppPrimaryKey: app.Annotations[oam.AnnotationAppName],
Name: recordName,
}
if err := w.ds.Get(ctx, record); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return bcode.ErrWorkflowRecordNotExist
}
return err
}
var revision = &model.ApplicationRevision{
AppPrimaryKey: app.Name,
AppPrimaryKey: app.Annotations[oam.AnnotationAppName],
Version: record.RevisionPrimaryKey,
}
if err := w.ds.Get(ctx, revision); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
return bcode.ErrApplicationRevisionNotExist
}
return err
}
@@ -395,13 +444,10 @@ func (w *workflowUsecaseImpl) syncWorkflowStatus(ctx context.Context, app *v1bet
return nil
}
func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, app *v1beta1.Application) error {
func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, appModel *model.Application, app *v1beta1.Application, workflow *model.Workflow) error {
if app.Annotations == nil {
return fmt.Errorf("empty annotations in application")
}
if app.Annotations[oam.AnnotationWorkflowName] == "" {
return fmt.Errorf("failed to get workflow name from application")
}
if app.Annotations[oam.AnnotationPublishVersion] == "" {
return fmt.Errorf("failed to get record version from application")
}
@@ -410,19 +456,26 @@ func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, app *v1b
}
return w.ds.Add(ctx, &model.WorkflowRecord{
WorkflowPrimaryKey: app.Annotations[oam.AnnotationWorkflowName],
AppPrimaryKey: app.Name,
WorkflowName: workflow.Name,
AppPrimaryKey: appModel.PrimaryKey(),
RevisionPrimaryKey: app.Annotations[oam.AnnotationDeployVersion],
Name: app.Annotations[oam.AnnotationPublishVersion],
Namespace: app.Namespace,
Namespace: appModel.Namespace,
Finished: "false",
StartTime: time.Now().Time,
Status: model.RevisionStatusInit,
})
}
func (w *workflowUsecaseImpl) CountWorkflow(ctx context.Context, app *model.Application) int64 {
count, err := w.ds.Count(ctx, &model.Workflow{AppPrimaryKey: app.PrimaryKey()}, &datastore.FilterOptions{})
if err != nil {
log.Logger.Errorf("count app %s workflow failure %s", app.PrimaryKey(), err.Error())
}
return count
}
func (w *workflowUsecaseImpl) ResumeRecord(ctx context.Context, appModel *model.Application, recordName string) error {
oamApp, err := w.checkRecordRunning(ctx, appModel)
func (w *workflowUsecaseImpl) ResumeRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error {
oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName)
if err != nil {
return err
}
@@ -438,8 +491,8 @@ func (w *workflowUsecaseImpl) ResumeRecord(ctx context.Context, appModel *model.
return nil
}
func (w *workflowUsecaseImpl) TerminateRecord(ctx context.Context, appModel *model.Application, recordName string) error {
oamApp, err := w.checkRecordRunning(ctx, appModel)
func (w *workflowUsecaseImpl) TerminateRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName string) error {
oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName)
if err != nil {
return err
}
@@ -455,7 +508,7 @@ func (w *workflowUsecaseImpl) TerminateRecord(ctx context.Context, appModel *mod
return nil
}
func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *model.Application, recordName, revisionVersion string) error {
func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *model.Application, workflow *model.Workflow, recordName, revisionVersion string) error {
if revisionVersion == "" {
// find the latest complete revision version
var revision = model.ApplicationRevision{
@@ -472,23 +525,24 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
return err
}
if len(revisions) == 0 {
fmt.Errorf("there is no complete revision, please specify a revision version")
return bcode.ErrApplicationNoReadyRevision
}
revisionVersion = revisions[0].Index()["version"]
}
oamApp, err := w.checkRecordRunning(ctx, appModel)
if err != nil {
return err
}
var record = &model.WorkflowRecord{
AppPrimaryKey: appModel.Name,
AppPrimaryKey: appModel.PrimaryKey(),
Name: recordName,
}
if err := w.ds.Get(ctx, record); err != nil {
return err
}
oamApp, err := w.checkRecordRunning(ctx, appModel, workflow.EnvName)
if err != nil {
return err
}
var rollbackRevision = model.ApplicationRevision{
AppPrimaryKey: appModel.Name,
Version: revisionVersion,
@@ -507,12 +561,11 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
if oamApp.Annotations == nil {
oamApp.Annotations = make(map[string]string)
}
newRecordName := utils.GenerateVersion(record.WorkflowPrimaryKey)
newRecordName := utils.GenerateVersion(record.WorkflowName)
oamApp.Annotations[oam.AnnotationDeployVersion] = revisionVersion
oamApp.Annotations[oam.AnnotationPublishVersion] = newRecordName
// create a new workflow record
if err := w.CreateWorkflowRecord(ctx, oamApp); err != nil {
if err := w.CreateWorkflowRecord(ctx, appModel, oamApp, workflow); err != nil {
return err
}
@@ -527,9 +580,9 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
return nil
}
func (w *workflowUsecaseImpl) checkRecordRunning(ctx context.Context, appModel *model.Application) (*v1beta1.Application, error) {
func (w *workflowUsecaseImpl) checkRecordRunning(ctx context.Context, appModel *model.Application, envName string) (*v1beta1.Application, error) {
oamApp := &v1beta1.Application{}
if err := w.kubeClient.Get(ctx, types.NamespacedName{Name: appModel.Name, Namespace: appModel.Namespace}, oamApp); err != nil {
if err := w.kubeClient.Get(ctx, types.NamespacedName{Name: converAppName(appModel.Name, envName), Namespace: appModel.Namespace}, oamApp); err != nil {
return nil, err
}
if oamApp.Status.Workflow != nil && !oamApp.Status.Workflow.Suspend && !oamApp.Status.Workflow.Terminated && !oamApp.Status.Workflow.Finished {
+172 -59
View File
@@ -37,20 +37,42 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/apply"
)
var appName = "app-workflow"
var _ = Describe("Test workflow usecase functions", func() {
var (
workflowUsecase *workflowUsecaseImpl
appUsecase *applicationUsecaseImpl
)
BeforeEach(func() {
workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, apply: apply.NewAPIApplicator(k8sClient)}
appUsecase = &applicationUsecaseImpl{ds: ds, kubeClient: k8sClient, apply: apply.NewAPIApplicator(k8sClient), envBindingUsecase: &envBindingUsecaseImpl{
ds: ds,
workflowUsecase: workflowUsecase,
}}
})
It("Test CreateWorkflow function", func() {
reqApp := apisv1.CreateApplicationRequest{
Name: appName,
Namespace: "default",
Description: "this is a test app",
EnvBinding: []*apisv1.EnvBinding{{
Name: "dev",
Description: "dev env",
TargetNames: []string{"dev-target"},
}},
}
_, err := appUsecase.CreateApplication(context.TODO(), reqApp)
Expect(err).Should(BeNil())
req := apisv1.CreateWorkflowRequest{
Name: "test-workflow-1",
Description: "this is a workflow",
EnvName: "dev",
}
base, err := workflowUsecase.CreateWorkflow(context.TODO(), &model.Application{
Name: "test-app",
Name: appName,
Namespace: "default",
}, req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
@@ -58,10 +80,12 @@ var _ = Describe("Test workflow usecase functions", func() {
req = apisv1.CreateWorkflowRequest{
Name: "test-workflow-2",
Description: "this is test workflow",
EnvName: "dev",
Default: true,
}
base, err = workflowUsecase.CreateWorkflow(context.TODO(), &model.Application{
Name: "test-app",
Name: appName,
Namespace: "default",
}, req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
@@ -69,7 +93,8 @@ var _ = Describe("Test workflow usecase functions", func() {
It("Test GetApplicationDefaultWorkflow function", func() {
workflow, err := workflowUsecase.GetApplicationDefaultWorkflow(context.TODO(), &model.Application{
Name: "test-app",
Name: appName,
Namespace: "default",
})
Expect(err).Should(BeNil())
Expect(workflow).ShouldNot(BeNil())
@@ -83,14 +108,22 @@ var _ = Describe("Test workflow usecase functions", func() {
app := &v1beta1.Application{}
err = json.Unmarshal(raw, app)
Expect(err).Should(BeNil())
app.Annotations[oam.AnnotationWorkflowName] = "list-workflow-name"
app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-2"
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, "test-workflow-2")
Expect(err).Should(BeNil())
for i := 0; i < 3; i++ {
app.Annotations[oam.AnnotationPublishVersion] = fmt.Sprintf("list-workflow-name-%d", i)
err := workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
}
resp, err := workflowUsecase.ListWorkflowRecords(context.TODO(), "list-workflow-name", 0, 10)
resp, err := workflowUsecase.ListWorkflowRecords(context.TODO(), workflow, 0, 10)
Expect(err).Should(BeNil())
Expect(resp.Total).Should(Equal(int64(3)))
})
@@ -102,28 +135,35 @@ var _ = Describe("Test workflow usecase functions", func() {
app := &v1beta1.Application{}
err = json.Unmarshal(raw, app)
Expect(err).Should(BeNil())
app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-name"
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-name-123"
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-123"
app.Annotations[oam.AnnotationDeployVersion] = "1234"
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, "test-workflow-2")
Expect(err).Should(BeNil())
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
var revision = &model.ApplicationRevision{
AppPrimaryKey: "test",
AppPrimaryKey: appName,
Version: "1234",
Status: model.RevisionStatusInit,
DeployUser: "test-user",
Note: "test-commit",
TriggerType: "API",
WorkflowName: "test-workflow-name",
WorkflowName: "test-workflow-2",
}
err = workflowUsecase.createTestApplicationRevision(context.TODO(), revision)
Expect(err).Should(BeNil())
detail, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), "test-workflow-name", "test-workflow-name-123")
detail, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-123")
Expect(err).Should(BeNil())
Expect(detail.WorkflowRecord.Name).Should(Equal("test-workflow-name-123"))
Expect(detail.WorkflowRecord.Name).Should(Equal("test-workflow-2-123"))
Expect(detail.DeployUser).Should(Equal("test-user"))
})
@@ -135,19 +175,27 @@ var _ = Describe("Test workflow usecase functions", func() {
err = json.Unmarshal(raw, app)
Expect(err).Should(BeNil())
app.Status.Workflow.Finished = false
app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-name"
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-name-233"
app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-2"
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-233"
app.Annotations[oam.AnnotationDeployVersion] = "4321"
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, "test-workflow-2")
Expect(err).Should(BeNil())
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
By("create one revision to test sync workflow record")
var revision = &model.ApplicationRevision{
AppPrimaryKey: "test",
AppPrimaryKey: appName,
Version: "4321",
Status: model.RevisionStatusInit,
DeployUser: "test-user",
WorkflowName: "test-workflow-name",
WorkflowName: "test-workflow-2",
}
err = workflowUsecase.createTestApplicationRevision(context.TODO(), revision)
Expect(err).Should(BeNil())
@@ -158,12 +206,17 @@ var _ = Describe("Test workflow usecase functions", func() {
err = workflowUsecase.kubeClient.Create(ctx, app)
Expect(err).Should(BeNil())
err = workflowUsecase.kubeClient.Status().Patch(ctx, app, client.Merge)
Expect(err).Should(BeNil())
err = workflowUsecase.SyncWorkflowRecord(ctx)
Expect(err).Should(BeNil())
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, "test-workflow-2")
Expect(err).Should(BeNil())
By("check the record")
record, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), "test-workflow-name", "test-workflow-name-233")
record, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-233")
Expect(err).Should(BeNil())
Expect(record.Status).Should(Equal(model.RevisionStatusComplete))
@@ -174,18 +227,21 @@ var _ = Describe("Test workflow usecase functions", func() {
By("create another workflow record to test sync status from controller revision")
app.Status.Workflow.Finished = false
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-name-111"
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-111"
app.Annotations[oam.AnnotationDeployVersion] = "1111"
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
By("create another revision to test sync workflow record")
var anotherRevision = &model.ApplicationRevision{
AppPrimaryKey: "test",
AppPrimaryKey: appName,
Version: "1111",
Status: model.RevisionStatusInit,
DeployUser: "test-user",
WorkflowName: "test-workflow-name",
WorkflowName: "test-workflow-2",
}
err = workflowUsecase.createTestApplicationRevision(context.TODO(), anotherRevision)
Expect(err).Should(BeNil())
@@ -194,9 +250,9 @@ var _ = Describe("Test workflow usecase functions", func() {
Expect(err).Should(BeNil())
cr := &appsv1.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Name: "record-test-test-workflow-name-111",
Name: "record-" + appName + "-test-workflow-2-111",
Namespace: "default",
Labels: map[string]string{"vela.io/wf-revision": "test-workflow-name-111"},
Labels: map[string]string{"vela.io/wf-revision": "test-workflow-2-111"},
},
Data: runtime.RawExtension{Raw: raw},
}
@@ -207,7 +263,7 @@ var _ = Describe("Test workflow usecase functions", func() {
Expect(err).Should(BeNil())
By("check the record")
anotherRecord, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), "test-workflow-name", "test-workflow-name-111")
anotherRecord, err := workflowUsecase.DetailWorkflowRecord(context.TODO(), workflow, "test-workflow-2-111")
Expect(err).Should(BeNil())
Expect(anotherRecord.Status).Should(Equal(model.RevisionStatusComplete))
@@ -219,88 +275,141 @@ var _ = Describe("Test workflow usecase functions", func() {
It("Test ResumeRecord function", func() {
ctx := context.TODO()
app, err := createTestSuspendApp(ctx, "resume-app", "revision-resume1", "workflow-resume", "workflow-resume-1", workflowUsecase.kubeClient)
ResumeWorkflow := "resume-workflow"
req := apisv1.CreateWorkflowRequest{
Name: ResumeWorkflow,
Description: "this is a workflow",
EnvName: "resume",
}
base, err := workflowUsecase.CreateWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
app, err := createTestSuspendApp(ctx, appName, "resume", "revision-resume1", ResumeWorkflow, "workflow-resume-1", workflowUsecase.kubeClient)
Expect(err).Should(BeNil())
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, &model.Workflow{Name: ResumeWorkflow})
Expect(err).Should(BeNil())
err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{
AppPrimaryKey: "resume-app",
Version: "revision-resume1",
Status: model.RevisionStatusRunning,
AppPrimaryKey: appName,
Version: "revision-resume1",
Status: model.RevisionStatusRunning,
})
Expect(err).Should(BeNil())
err = workflowUsecase.ResumeRecord(ctx, &model.Application{
Name: "resume-app",
Name: appName,
Namespace: "default",
}, "workflow-resume-1")
}, &model.Workflow{Name: ResumeWorkflow, EnvName: "resume"}, "workflow-resume-1")
Expect(err).Should(BeNil())
record, err := workflowUsecase.DetailWorkflowRecord(ctx, "workflow-resume", "workflow-resume-1")
record, err := workflowUsecase.DetailWorkflowRecord(ctx, &model.Workflow{Name: ResumeWorkflow, AppPrimaryKey: appName}, "workflow-resume-1")
Expect(err).Should(BeNil())
Expect(record.Status).Should(Equal(model.RevisionStatusRunning))
})
It("Test TerminateRecord function", func() {
ctx := context.TODO()
app, err := createTestSuspendApp(ctx, "terminate-app", "revision-terminate1", "workflow-terminate", "workflow-terminate-1", workflowUsecase.kubeClient)
workflowName := "terminate-workflow"
req := apisv1.CreateWorkflowRequest{
Name: workflowName,
Description: "this is a workflow",
EnvName: "terminate",
}
workflow := &model.Workflow{Name: workflowName, EnvName: "terminate"}
base, err := workflowUsecase.CreateWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
app, err := createTestSuspendApp(ctx, appName, "terminate", "revision-terminate1", workflow.Name, "test-workflow-2-1", workflowUsecase.kubeClient)
Expect(err).Should(BeNil())
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{
AppPrimaryKey: "terminate-app",
AppPrimaryKey: appName,
Version: "revision-terminate1",
Status: model.RevisionStatusRunning,
})
Expect(err).Should(BeNil())
err = workflowUsecase.TerminateRecord(ctx, &model.Application{
Name: "terminate-app",
Name: appName,
Namespace: "default",
}, "workflow-terminate-1")
}, workflow, "test-workflow-2-1")
Expect(err).Should(BeNil())
record, err := workflowUsecase.DetailWorkflowRecord(ctx, "workflow-terminate", "workflow-terminate-1")
record, err := workflowUsecase.DetailWorkflowRecord(ctx, workflow, "test-workflow-2-1")
Expect(err).Should(BeNil())
Expect(record.Status).Should(Equal(model.RevisionStatusTerminated))
})
It("Test RollbackRecord function", func() {
ctx := context.TODO()
app, err := createTestSuspendApp(ctx, "rollback-app", "revision-rollback1", "workflow-rollback", "workflow-rollback-1", workflowUsecase.kubeClient)
workflowName := "rollback-workflow"
req := apisv1.CreateWorkflowRequest{
Name: workflowName,
Description: "this is a workflow",
EnvName: "rollback",
}
workflow := &model.Workflow{Name: workflowName, EnvName: "rollback"}
base, err := workflowUsecase.CreateWorkflow(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, req)
Expect(err).Should(BeNil())
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
app, err := createTestSuspendApp(ctx, appName, "rollback", "revision-rollback1", workflow.Name, "test-workflow-2-2", workflowUsecase.kubeClient)
Expect(err).Should(BeNil())
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{
AppPrimaryKey: "rollback-app",
AppPrimaryKey: appName,
Version: "revision-rollback1",
Status: model.RevisionStatusRunning,
})
Expect(err).Should(BeNil())
err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{
AppPrimaryKey: "rollback-app",
AppPrimaryKey: appName,
Version: "revision-rollback0",
ApplyAppConfig: `{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"annotations":{"app.oam.dev/workflowName":"workflow-rollback","app.oam.dev/deployVersion":"revision-rollback1","vela.io/publish-version":"workflow-rollback1"},"name":"first-vela-app","namespace":"default"},"spec":{"components":[{"name":"express-server","properties":{"image":"crccheck/hello-world","port":8000},"traits":[{"properties":{"domain":"testsvc.example.com","http":{"/":8000}},"type":"ingress-1-20"}],"type":"webservice"}]}}`,
ApplyAppConfig: `{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"annotations":{"app.oam.dev/workflowName":"test-workflow-2-2","app.oam.dev/deployVersion":"revision-rollback1","vela.io/publish-version":"workflow-rollback1"},"name":"first-vela-app","namespace":"default"},"spec":{"components":[{"name":"express-server","properties":{"image":"crccheck/hello-world","port":8000},"traits":[{"properties":{"domain":"testsvc.example.com","http":{"/":8000}},"type":"ingress-1-20"}],"type":"webservice"}]}}`,
Status: model.RevisionStatusComplete,
})
Expect(err).Should(BeNil())
err = workflowUsecase.RollbackRecord(ctx, &model.Application{
Name: "rollback-app",
Name: appName,
Namespace: "default",
}, "workflow-rollback-1", "revision-rollback0")
}, workflow, "test-workflow-2-2", "revision-rollback0")
Expect(err).Should(BeNil())
recordsNum, err := workflowUsecase.ds.Count(ctx, &model.WorkflowRecord{
AppPrimaryKey: "rollback-app",
WorkflowPrimaryKey: "workflow-rollback",
AppPrimaryKey: appName,
WorkflowName: workflow.Name,
RevisionPrimaryKey: "revision-rollback0",
}, nil)
Expect(err).Should(BeNil())
@@ -308,18 +417,21 @@ var _ = Describe("Test workflow usecase functions", func() {
By("rollback application without revision version")
app.Annotations[oam.AnnotationPublishVersion] = "workflow-rollback-2"
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), app)
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
Name: appName,
Namespace: "default",
}, app, workflow)
Expect(err).Should(BeNil())
err = workflowUsecase.RollbackRecord(ctx, &model.Application{
Name: "rollback-app",
Name: appName,
Namespace: "default",
}, "workflow-rollback-2", "")
}, workflow, "workflow-rollback-2", "")
Expect(err).Should(BeNil())
recordsNum, err = workflowUsecase.ds.Count(ctx, &model.WorkflowRecord{
AppPrimaryKey: "rollback-app",
WorkflowPrimaryKey: "workflow-rollback",
AppPrimaryKey: appName,
WorkflowName: workflow.Name,
RevisionPrimaryKey: "revision-rollback0",
}, nil)
Expect(err).Should(BeNil())
@@ -331,10 +443,11 @@ var yamlStr = `apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
app.oam.dev/workflowName: test-workflow-name
app.oam.dev/workflowName: test-workflow-2
app.oam.dev/deployVersion: "1234"
vela.io/publish-version: "test-workflow-name-111"
name: test
app.oam.dev/publishVersion: "test-workflow-name-111"
app.oam.dev/appName: "app-workflow"
name: app-workflow-dev
namespace: default
spec:
components:
@@ -66,3 +66,12 @@ var ErrTraitNotExist = NewBcode(400, 10015, "trait is not exist")
// ErrTraitAlreadyExist trait is already exist
var ErrTraitAlreadyExist = NewBcode(400, 10016, "trait is already exist")
// ErrApplicationNoReadyRevision application not have ready revision
var ErrApplicationNoReadyRevision = NewBcode(400, 10017, "application not have ready revision")
// ErrApplicationRevisionNotExist application revision is not exist
var ErrApplicationRevisionNotExist = NewBcode(404, 10018, "application revision is not exist")
// ErrApplicationRefusedDelete The application cannot be deleted because it has been deployed
var ErrApplicationRefusedDelete = NewBcode(400, 10019, "The application cannot be deleted because it has been deployed")
@@ -27,3 +27,9 @@ var ErrWorkflowNoDefault = NewBcode(404, 20004, "application default workflow is
// ErrMustQueryByApp you can only query the Workflow list based on applications.
var ErrMustQueryByApp = NewBcode(404, 20005, "you can only query the Workflow list based on applications.")
// ErrWorkflowNoEnv workflow have not env
var ErrWorkflowNoEnv = NewBcode(400, 20006, "workflow must set env name")
// ErrWorkflowRecordNotExist workflow record is not exist
var ErrWorkflowRecordNotExist = NewBcode(404, 20007, "workflow record is not exist")
+230 -9
View File
@@ -5,7 +5,7 @@ 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
http://wwc.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,
@@ -32,13 +32,18 @@ import (
)
type applicationWebService struct {
workflowWebService
applicationUsecase usecase.ApplicationUsecase
envBindingUsecase usecase.EnvBindingUsecase
}
// NewApplicationWebService new application manage webservice
func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase) WebService {
func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase, workflowUsecase usecase.WorkflowUsecase) WebService {
return &applicationWebService{
workflowWebService: workflowWebService{
workflowUsecase: workflowUsecase,
applicationUsecase: applicationUsecase,
},
applicationUsecase: applicationUsecase,
envBindingUsecase: envBindingUsecase,
}
@@ -89,15 +94,23 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailApplicationResponse{}))
ws.Route(ws.GET("/{name}/envs/{envName}/status").To(c.getApplicationStatus).
Doc("get application status").
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")).
Param(ws.PathParameter("envName", "identifier of the application envbinding").DataType("string")).
Returns(200, "", apis.ApplicationStatusResponse{}).
Reads(apis.UpdateApplicationRequest{}).
Returns(200, "", apis.ApplicationBase{}).
Returns(400, "", bcode.Bcode{}).
Writes(apis.ApplicationStatusResponse{}))
Writes(apis.ApplicationBase{}))
ws.Route(ws.GET("/{name}/statistics").To(c.applicationStatistics).
Doc("detail one application ").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
Returns(200, "", apis.ApplicationStatisticsResponse{}).
Returns(400, "", bcode.Bcode{}).
Writes(apis.ApplicationStatisticsResponse{}))
ws.Route(ws.PUT("/{name}").To(c.updateApplication).
Doc("update one application ").
@@ -149,7 +162,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
Writes(apis.ComponentBase{}))
ws.Route(ws.GET("/{name}/components/{componentName}").To(c.detailComponent).
Doc("detail component for application ").
Doc("detail component for application ").
Filter(c.appCheckFilter).
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
@@ -157,6 +170,17 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailComponentResponse{}))
ws.Route(ws.PUT("/{name}/components/{componentName}").To(c.updateComponent).
Doc("update component config").
Filter(c.appCheckFilter).
Filter(c.componentCheckFilter).
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(apis.UpdateApplicationComponentRequest{}).
Returns(200, "", apis.ComponentBase{}).
Returns(400, "", bcode.Bcode{}).
Writes(apis.ComponentBase{}))
ws.Route(ws.GET("/{name}/policies").To(c.listApplicationPolicies).
Doc("list policy for application").
Filter(c.appCheckFilter).
@@ -210,6 +234,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
ws.Route(ws.POST("/{name}/components/{compName}/traits").To(c.addApplicationTrait).
Doc("add trait for a component").
Filter(c.appCheckFilter).
Filter(c.componentCheckFilter).
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
@@ -221,6 +246,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
ws.Route(ws.PUT("/{name}/components/{compName}/traits/{traitType}").To(c.updateApplicationTrait).
Doc("update trait from a component").
Filter(c.appCheckFilter).
Filter(c.componentCheckFilter).
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
Param(ws.PathParameter("traitType", "identifier of the type of trait").DataType("string")).
@@ -233,6 +259,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
ws.Route(ws.DELETE("/{name}/components/{compName}/traits/{traitType}").To(c.deleteApplicationTrait).
Doc("delete trait from a component").
Filter(c.appCheckFilter).
Filter(c.componentCheckFilter).
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
Param(ws.PathParameter("traitType", "identifier of the type of trait").DataType("string")).
@@ -306,6 +333,138 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
Returns(404, "", bcode.Bcode{}).
Writes(apis.EmptyResponse{}))
ws.Route(ws.GET("/{name}/envs/{envName}/status").To(c.getApplicationStatus).
Doc("get application status").
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.ApplicationStatusResponse{}).
Returns(400, "", bcode.Bcode{}).
Writes(apis.ApplicationStatusResponse{}))
ws.Route(ws.POST("/{name}/envs/{envName}/recycle").To(c.recycleApplicationEnv).
Doc("get application status").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.envCheckFilter).
Param(ws.PathParameter("name", "identifier of the application ").DataType("string").Required(true)).
Param(ws.PathParameter("envName", "identifier of the application envbinding").DataType("string").Required(true)).
Returns(200, "", apis.EmptyResponse{}).
Returns(400, "", bcode.Bcode{}).
Writes(apis.EmptyResponse{}))
ws.Route(ws.GET("/{name}/workflows").To(c.listApplicationWorkflows).
Doc("list application workflow").
Filter(c.appCheckFilter).
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "", apis.ListWorkflowResponse{}).
Writes(apis.ListWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.POST("/{name}/workflows").To(c.createApplicationWorkflow).
Doc("create application workflow").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(apis.CreateWorkflowRequest{}).
Filter(c.appCheckFilter).
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Returns(200, "create success", apis.DetailWorkflowResponse{}).
Returns(400, "create failure", bcode.Bcode{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/workflows/{workflowName}").To(c.detailWorkflow).
Doc("detail application workflow").
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workfloc.").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.workflowCheckFilter).
Returns(200, "create success", apis.DetailWorkflowResponse{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.PUT("/{name}/workflows/{workflowName}").To(c.updateWorkflow).
Doc("update application workflow config").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Reads(apis.UpdateWorkflowRequest{}).
Returns(200, "", apis.DetailWorkflowResponse{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.DELETE("/{name}/workflows/{workflowName}").To(c.deleteWorkflow).
Doc("deletet workflow").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Returns(200, "", apis.EmptyResponse{}).
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records").To(c.listWorkflowRecords).
Doc("query application workflow execution record").
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Param(ws.QueryParameter("page", "query the page number").DataType("integer")).
Param(ws.QueryParameter("pageSize", "query the page size number").DataType("integer")).
Returns(200, "", apis.ListWorkflowRecordsResponse{}).
Writes(apis.ListWorkflowRecordsResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}").To(c.detailWorkflowRecord).
Doc("query application workflow execution record detail").
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Returns(200, "", apis.DetailWorkflowRecordResponse{}).
Writes(apis.DetailWorkflowRecordResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/resume").To(c.resumeWorkflowRecord).
Doc("resume suspend workflow record").
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/terminate").To(c.terminateWorkflowRecord).
Doc("terminate suspend workflow record").
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/rollback").To(c.rollbackWorkflowRecord).
Doc("rollback suspend application record").
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Param(ws.QueryParameter("rollbackVersion", "identifier of the rollback revision").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(c.appCheckFilter).
Filter(c.workflowCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
return ws
}
@@ -465,6 +624,30 @@ func (c *applicationWebService) detailComponent(req *restful.Request, res *restf
}
}
func (c *applicationWebService) updateComponent(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
component := req.Request.Context().Value(&apis.CtxKeyApplicationComponent).(*model.ApplicationComponent)
// Verify the validity of parameters
var updateReq apis.UpdateApplicationComponentRequest
if err := req.ReadEntity(&updateReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := validate.Struct(&updateReq); err != nil {
bcode.ReturnError(req, res, err)
return
}
base, err := c.applicationUsecase.UpdateComponent(req.Request.Context(), app, component, updateReq)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(base); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (c *applicationWebService) createApplicationPolicy(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
// Verify the validity of parameters
@@ -641,7 +824,7 @@ func (c *applicationWebService) getApplicationStatus(req *restful.Request, res *
return
}
if err := res.WriteEntity(apis.ApplicationStatusResponse{Status: status}); err != nil {
if err := res.WriteEntity(apis.ApplicationStatusResponse{Status: status, EnvName: req.PathParameter("envName")}); err != nil {
bcode.ReturnError(req, res, err)
return
}
@@ -759,6 +942,17 @@ func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restfu
chain.ProcessFilter(req, res)
}
func (c *applicationWebService) componentCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
component, err := c.applicationUsecase.GetApplicationComponent(req.Request.Context(), app, req.PathParameter("compName"))
if err != nil {
bcode.ReturnError(req, res, err)
return
}
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyApplicationComponent, component))
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)
@@ -775,3 +969,30 @@ func (c *applicationWebService) envCheckFilter(req *restful.Request, res *restfu
}
bcode.ReturnError(req, res, bcode.ErrApplicationNotEnv)
}
func (c *applicationWebService) applicationStatistics(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
detail, err := c.applicationUsecase.Statistics(req.Request.Context(), app)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(detail); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (c *applicationWebService) recycleApplicationEnv(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
env := req.Request.Context().Value(&apis.CtxKeyApplicationEnvBinding).(*model.EnvBinding)
err := c.envBindingUsecase.ApplicationEnvRecycle(req.Request.Context(), app, env)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
bcode.ReturnError(req, res, err)
return
}
}
@@ -177,7 +177,7 @@ func (dt *DeliveryTargetWebService) updateDeliveryTarget(req *restful.Request, r
func (dt *DeliveryTargetWebService) deleteDeliveryTarget(req *restful.Request, res *restful.Response) {
deliveryTargetName := req.PathParameter("name")
// deliveryTarget in use, can't be deleted
applications, err := dt.applicationUsecase.ListApplications(context.TODO(), apis.ListApplicatioOptions{TargetName: deliveryTargetName})
applications, err := dt.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicatioOptions{TargetName: deliveryTargetName})
if err != nil {
if !errors.Is(err, datastore.ErrRecordNotExist) {
bcode.ReturnError(req, res, err)
+1 -2
View File
@@ -69,14 +69,13 @@ func Init(ds datastore.DataStore) {
envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase)
applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, deliveryTargetUsecase)
RegistWebService(NewClusterWebService(clusterUsecase))
RegistWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase))
RegistWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase))
RegistWebService(NewNamespaceWebService(namespaceUsecase))
RegistWebService(NewDefinitionWebservice(definitionUsecase))
RegistWebService(NewAddonWebService(addonUsecase))
RegistWebService(NewAddonRegistryWebService(addonUsecase))
RegistWebService(NewOAMApplication(oamApplicationUsecase))
RegistWebService(&policyDefinitionWebservice{})
RegistWebService(NewWorkflowWebService(workflowUsecase, applicationUsecase))
RegistWebService(NewDeliveryTargetWebService(deliveryTargetUsecase, applicationUsecase))
RegistWebService(NewVelaQLWebService(velaQLUsecase))
}
+19 -142
View File
@@ -18,9 +18,7 @@ package webservice
import (
"context"
"strconv"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
restful "github.com/emicklei/go-restful/v3"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
@@ -31,122 +29,14 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
)
// NewWorkflowWebService new workflow webservice
func NewWorkflowWebService(workflowUsecase usecase.WorkflowUsecase, applicationUsecase usecase.ApplicationUsecase) WebService {
return &workflowWebService{
workflowUsecase: workflowUsecase,
applicationUsecase: applicationUsecase,
}
}
type workflowWebService struct {
workflowUsecase usecase.WorkflowUsecase
applicationUsecase usecase.ApplicationUsecase
}
func (w *workflowWebService) GetWebService() *restful.WebService {
ws := new(restful.WebService)
ws.Path(versionPrefix+"/workflows").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML).
Doc("api for cluster manage")
tags := []string{"workflow"}
ws.Route(ws.GET("/").To(w.listApplicationWorkflows).
Doc("list application workflow").
Param(ws.QueryParameter("appName", "identifier of the application.").DataType("string").Required(true)).
Param(ws.QueryParameter("enable", "query based on enable status").DataType("boolean")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "", apis.ListWorkflowResponse{}).
Writes(apis.ListWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.POST("/").To(w.createApplicationWorkflow).
Doc("create application workflow").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(apis.CreateWorkflowRequest{}).
Returns(200, "create success", apis.DetailWorkflowResponse{}).
Returns(400, "create failure", bcode.Bcode{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}").To(w.detailWorkflow).
Doc("detail application workflow").
Param(ws.PathParameter("name", "identifier of the workflow.").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.workflowCheckFilter).
Returns(200, "create success", apis.DetailWorkflowResponse{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.PUT("/{name}").To(w.updateWorkflow).
Doc("update application workflow config").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.workflowCheckFilter).
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Reads(apis.UpdateWorkflowRequest{}).
Returns(200, "", apis.DetailWorkflowResponse{}).
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
ws.Route(ws.DELETE("/{name}").To(w.deleteWorkflow).
Doc("deletet workflow").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.workflowCheckFilter).
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Returns(200, "", apis.EmptyResponse{}).
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/records").To(w.listWorkflowRecords).
Doc("query application workflow execution record").
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.workflowCheckFilter).
Param(ws.QueryParameter("page", "query the page number").DataType("integer")).
Param(ws.QueryParameter("pageSize", "query the page size number").DataType("integer")).
Returns(200, "", apis.ListWorkflowRecordsResponse{}).
Writes(apis.ListWorkflowRecordsResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/records/{record}").To(w.detailWorkflowRecord).
Doc("query application workflow execution record detail").
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Returns(200, "", apis.DetailWorkflowRecordResponse{}).
Writes(apis.DetailWorkflowRecordResponse{}).Do(returns200, returns500))
ws.Route(ws.GET("/{name}/records/{record}/resume").To(w.resumeWorkflowRecord).
Doc("resume suspend workflow record").
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.applicationCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
ws.Route(ws.GET("/{name}/records/{record}/terminate").To(w.terminateWorkflowRecord).
Doc("terminate suspend workflow record").
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.applicationCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
ws.Route(ws.GET("/{name}/records/{record}/rollback").To(w.rollbackWorkflowRecord).
Doc("rollback suspend application record").
Param(ws.PathParameter("name", "identifier of the workflow").DataType("string")).
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
Param(ws.QueryParameter("rollbackVersion", "identifier of the rollback revision").DataType("string")).
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(w.applicationCheckFilter).
Returns(200, "", nil).
Returns(400, "", bcode.Bcode{}).
Writes(apis.DetailWorkflowRecordResponse{}))
return ws
}
func (w *workflowWebService) workflowCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
workflow, err := w.workflowUsecase.GetWorkflow(req.Request.Context(), req.PathParameter("name"))
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
workflow, err := w.workflowUsecase.GetWorkflow(req.Request.Context(), app, req.PathParameter("workflowName"))
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -156,37 +46,19 @@ func (w *workflowWebService) workflowCheckFilter(req *restful.Request, res *rest
}
func (w *workflowWebService) applicationCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
workflow, err := w.workflowUsecase.GetWorkflow(req.Request.Context(), req.PathParameter("name"))
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
workflow, err := w.workflowUsecase.GetWorkflow(req.Request.Context(), app, req.PathParameter("workflowName"))
if err != nil {
bcode.ReturnError(req, res, err)
return
}
app, err := w.applicationUsecase.GetApplication(req.Request.Context(), workflow.AppPrimaryKey)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyApplication, app))
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyWorkflow, workflow))
chain.ProcessFilter(req, res)
}
func (w *workflowWebService) listApplicationWorkflows(req *restful.Request, res *restful.Response) {
if req.QueryParameter("appName") == "" {
bcode.ReturnError(req, res, bcode.ErrMustQueryByApp)
return
}
app, err := w.applicationUsecase.GetApplication(req.Request.Context(), req.QueryParameter("appName"))
if err != nil {
bcode.ReturnError(req, res, err)
return
}
var enableQuery *bool
enable, err := strconv.ParseBool(req.QueryParameter("enable"))
if err == nil {
enableQuery = &enable
}
workflows, err := w.workflowUsecase.ListApplicationWorkflow(req.Request.Context(), app, enableQuery)
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
workflows, err := w.workflowUsecase.ListApplicationWorkflow(req.Request.Context(), app)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -265,7 +137,8 @@ func (w *workflowWebService) updateWorkflow(req *restful.Request, res *restful.R
}
func (w *workflowWebService) deleteWorkflow(req *restful.Request, res *restful.Response) {
if err := w.workflowUsecase.DeleteWorkflow(req.Request.Context(), req.PathParameter("name")); err != nil {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
if err := w.workflowUsecase.DeleteWorkflow(req.Request.Context(), app, req.PathParameter("workflowName")); err != nil {
bcode.ReturnError(req, res, err)
return
}
@@ -281,8 +154,8 @@ func (w *workflowWebService) listWorkflowRecords(req *restful.Request, res *rest
bcode.ReturnError(req, res, err)
return
}
records, err := w.workflowUsecase.ListWorkflowRecords(req.Request.Context(), req.PathParameter("name"), page, pageSize)
workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow)
records, err := w.workflowUsecase.ListWorkflowRecords(req.Request.Context(), workflow, page, pageSize)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -295,7 +168,8 @@ func (w *workflowWebService) listWorkflowRecords(req *restful.Request, res *rest
}
func (w *workflowWebService) detailWorkflowRecord(req *restful.Request, res *restful.Response) {
record, err := w.workflowUsecase.DetailWorkflowRecord(req.Request.Context(), req.PathParameter("name"), req.PathParameter("record"))
workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow)
record, err := w.workflowUsecase.DetailWorkflowRecord(req.Request.Context(), workflow, req.PathParameter("record"))
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -309,7 +183,8 @@ func (w *workflowWebService) detailWorkflowRecord(req *restful.Request, res *res
func (w *workflowWebService) resumeWorkflowRecord(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
err := w.workflowUsecase.ResumeRecord(req.Request.Context(), app, req.PathParameter("record"))
workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow)
err := w.workflowUsecase.ResumeRecord(req.Request.Context(), app, workflow, req.PathParameter("record"))
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -319,7 +194,8 @@ func (w *workflowWebService) resumeWorkflowRecord(req *restful.Request, res *res
func (w *workflowWebService) terminateWorkflowRecord(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
err := w.workflowUsecase.TerminateRecord(req.Request.Context(), app, req.PathParameter("record"))
workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow)
err := w.workflowUsecase.TerminateRecord(req.Request.Context(), app, workflow, req.PathParameter("record"))
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -329,7 +205,8 @@ func (w *workflowWebService) terminateWorkflowRecord(req *restful.Request, res *
func (w *workflowWebService) rollbackWorkflowRecord(req *restful.Request, res *restful.Response) {
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
err := w.workflowUsecase.RollbackRecord(req.Request.Context(), app, req.PathParameter("record"), req.QueryParameter("rollbackVersion"))
workflow := req.Request.Context().Value(&apis.CtxKeyWorkflow).(*model.Workflow)
err := w.workflowUsecase.RollbackRecord(req.Request.Context(), app, workflow, req.PathParameter("record"), req.QueryParameter("rollbackVersion"))
if err != nil {
bcode.ReturnError(req, res, err)
return
+7 -1
View File
@@ -133,11 +133,17 @@ const (
AnnotationDeployVersion = "app.oam.dev/deployVersion"
// AnnotationPublishVersion is annotation that record the application workflow version.
AnnotationPublishVersion = "vela.io/publish-version"
AnnotationPublishVersion = "app.oam.dev/publishVersion"
// AnnotationWorkflowName specifies the workflow name for execution.
AnnotationWorkflowName = "app.oam.dev/workflowName"
// AnnotationAppName specifies the name for application in db.
AnnotationAppName = "app.oam.dev/appName"
// AnnotationAppAlias specifies the alias for application in db.
AnnotationAppAlias = "app.oam.dev/appAlias"
// AnnotationWorkloadGVK indicates the managed workload's GVK by trait
AnnotationWorkloadGVK = "trait.oam.dev/workload-gvk"
+62 -53
View File
@@ -33,12 +33,15 @@ import (
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var appName = "app-e2e"
var appProject = "test-app-project"
var _ = Describe("Test application rest api", func() {
It("Test create app", func() {
defer GinkgoRecover()
var req = apisv1.CreateApplicationRequest{
Name: "test-app-sadasd",
Namespace: "test-app-namespace",
Name: appName,
Namespace: appProject,
Description: "this is a test app",
Icon: "",
Labels: map[string]string{"test": "true"},
@@ -63,7 +66,7 @@ var _ = Describe("Test application rest api", func() {
It("Test delete app", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/test-app-sadasd", nil)
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+appName, nil)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.DefaultClient.Do(req)
Expect(err).ShouldNot(HaveOccurred())
@@ -76,8 +79,8 @@ var _ = Describe("Test application rest api", func() {
bs, err := ioutil.ReadFile("./testdata/example-app.yaml")
Expect(err).Should(Succeed())
var req = apisv1.CreateApplicationRequest{
Name: "test-app-sadasd",
Namespace: "test-app-namespace",
Name: appName,
Namespace: appProject,
Description: "this is a test app",
Icon: "",
Labels: map[string]string{"test": "true"},
@@ -102,7 +105,7 @@ var _ = Describe("Test application rest api", func() {
It("Test list components", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components")
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/components")
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -114,37 +117,9 @@ var _ = Describe("Test application rest api", func() {
Expect(cmp.Diff(len(components.Components), 2)).Should(BeEmpty())
})
It("Test list policies", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies")
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
Expect(res.Body).ShouldNot(BeNil())
defer res.Body.Close()
var policies apisv1.ListApplicationPolicy
err = json.NewDecoder(res.Body).Decode(&policies)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(len(policies.Policies), 1)).Should(BeEmpty())
})
It("Test get workflow", func() {
// defer GinkgoRecover()
// res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies")
// Expect(err).ShouldNot(HaveOccurred())
// Expect(res).ShouldNot(BeNil())
// Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
// Expect(res.Body).ShouldNot(BeNil())
// defer res.Body.Close()
// var policies apisv1.ListApplicationPolicy
// err = json.NewDecoder(res.Body).Decode(&policies)
// Expect(err).ShouldNot(HaveOccurred())
// Expect(cmp.Diff(len(policies.Policies), 1)).Should(BeEmpty())
})
It("Test detail application", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd")
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName)
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -153,19 +128,53 @@ var _ = Describe("Test application rest api", func() {
var detail apisv1.DetailApplicationResponse
err = json.NewDecoder(res.Body).Decode(&detail)
Expect(err).ShouldNot(HaveOccurred())
Expect(cmp.Diff(len(detail.Policies), 1)).Should(BeEmpty())
Expect(cmp.Diff(len(detail.Policies), 0)).Should(BeEmpty())
})
It("Test deploy application", func() {
defer GinkgoRecover()
var req = apisv1.ApplicationDeployRequest{
Note: "test apply",
TriggerType: "web",
Force: false,
var targetName = "dev-default"
var envName = "dev"
var namespace = "default"
// create target
var createTarget = apisv1.CreateDeliveryTargetRequest{
Name: targetName,
Namespace: appProject,
Cluster: &apisv1.ClusterTarget{
ClusterName: "local",
Namespace: namespace,
},
}
bodyByte, err := json.Marshal(req)
bodyByte, err := json.Marshal(createTarget)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/deploy", "application/json", bytes.NewBuffer(bodyByte))
res, err := http.Post("http://127.0.0.1:8000/api/v1/deliveryTargets", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
// create env
var createEnvReq = apisv1.CreateApplicationEnvRequest{
EnvBinding: apisv1.EnvBinding{
Name: envName,
TargetNames: []string{targetName},
},
}
bodyByte, err = json.Marshal(createEnvReq)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/envs", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
// deploy app
var req = apisv1.ApplicationDeployRequest{
Note: "test apply",
TriggerType: "web",
WorkflowName: "dev",
Force: false,
}
bodyByte, err = json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/deploy", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -177,7 +186,7 @@ var _ = Describe("Test application rest api", func() {
Expect(cmp.Diff(response.Status, model.RevisionStatusRunning)).Should(BeEmpty())
var oam v1beta1.Application
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: "test-app-sadasd", Namespace: "test-app-namespace"}, &oam)
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: appName + "-" + envName, Namespace: appProject}, &oam)
Expect(err).Should(BeNil())
Expect(cmp.Diff(len(oam.Spec.Components), 2)).Should(BeEmpty())
Expect(cmp.Diff(len(oam.Spec.Policies), 1)).Should(BeEmpty())
@@ -195,7 +204,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components", "application/json", bytes.NewBuffer(bodyByte))
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/components", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -209,7 +218,7 @@ var _ = Describe("Test application rest api", func() {
It("Test detail component", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components/test2")
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/components/test2")
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -229,7 +238,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components/test2/traits", "application/json", bytes.NewBuffer(bodyByte))
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -249,7 +258,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components/test2/traits/ingress", bytes.NewBuffer(bodyByte))
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits/ingress", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
@@ -266,7 +275,7 @@ var _ = Describe("Test application rest api", func() {
It("Test delete trait", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/components/test2/traits/ingress", nil)
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/components/test2/traits/ingress", nil)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.DefaultClient.Do(req)
Expect(err).ShouldNot(HaveOccurred())
@@ -283,7 +292,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte, err := json.Marshal(req)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies", "application/json", bytes.NewBuffer(bodyByte))
res, err := http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies", "application/json", bytes.NewBuffer(bodyByte))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 400)).Should(BeEmpty())
@@ -295,7 +304,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte2, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies", "application/json", bytes.NewBuffer(bodyByte2))
res, err = http.Post("http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies", "application/json", bytes.NewBuffer(bodyByte2))
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -310,7 +319,7 @@ var _ = Describe("Test application rest api", func() {
It("Test detail application policy", func() {
defer GinkgoRecover()
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies/test2")
res, err := http.Get("http://127.0.0.1:8000/api/v1/applications/" + appName + "/policies/test2")
Expect(err).ShouldNot(HaveOccurred())
Expect(res).ShouldNot(BeNil())
Expect(cmp.Diff(res.StatusCode, 200)).Should(BeEmpty())
@@ -330,7 +339,7 @@ var _ = Describe("Test application rest api", func() {
}
bodyByte2, err := json.Marshal(req2)
Expect(err).ShouldNot(HaveOccurred())
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies/test2", bytes.NewBuffer(bodyByte2))
req, err := http.NewRequest(http.MethodPut, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies/test2", bytes.NewBuffer(bodyByte2))
Expect(err).ShouldNot(HaveOccurred())
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
@@ -348,7 +357,7 @@ var _ = Describe("Test application rest api", func() {
It("Test delete application policy", func() {
defer GinkgoRecover()
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/test-app-sadasd/policies/test2", nil)
req, err := http.NewRequest(http.MethodDelete, "http://127.0.0.1:8000/api/v1/applications/"+appName+"/policies/test2", nil)
Expect(err).ShouldNot(HaveOccurred())
res, err := http.DefaultClient.Do(req)
Expect(err).ShouldNot(HaveOccurred())
+1 -1
View File
@@ -60,7 +60,7 @@ var _ = BeforeSuite(func() {
}
cfg.LeaderConfig.ID = uuid.New().String()
cfg.LeaderConfig.LockName = "apiserver-lock"
cfg.LeaderConfig.Duration = time.Second * 5
cfg.LeaderConfig.Duration = time.Second * 10
server, err := arest.New(cfg)
Expect(err).ShouldNot(HaveOccurred())