diff --git a/dao/workflowTemplate.go b/dao/workflowTemplate.go index 24ed285..9ec6c8b 100644 --- a/dao/workflowTemplate.go +++ b/dao/workflowTemplate.go @@ -25,8 +25,9 @@ func WorkflowTemplateGet(id string) (*model.GetWorkflowTemplate, error) { } return &model.GetWorkflowTemplate{ - Name: workflowTemplate.Name, - Data: workflowTemplate.Data, + SpecVersion: workflowTemplate.SpecVersion, + Name: workflowTemplate.Name, + Data: workflowTemplate.Data, }, nil } diff --git a/lib/airflow/example/workflow_template/migrate_infra_workflow.json b/lib/airflow/example/workflow_template/migrate_infra_workflow.json index 5654e6d..b338b92 100644 --- a/lib/airflow/example/workflow_template/migrate_infra_workflow.json +++ b/lib/airflow/example/workflow_template/migrate_infra_workflow.json @@ -1,4 +1,5 @@ { + "spec_version": "1.0", "name": "migrate_infra_workflow", "data": { "description": "Migrate Server", diff --git a/pkg/api/rest/controller/workflow.go b/pkg/api/rest/controller/workflow.go index c07a2c3..e6fa2ce 100644 --- a/pkg/api/rest/controller/workflow.go +++ b/pkg/api/rest/controller/workflow.go @@ -7,6 +7,7 @@ import ( "net/http" "reflect" "strconv" + "strings" "time" "github.com/cloud-barista/cm-cicada/dao" @@ -42,54 +43,78 @@ func toTimeHookFunc() mapstructure.DecodeHookFunc { } } -func createDataReqToData(createDataReq model.CreateDataReq) (model.Data, error) { - var taskGroups []model.TaskGroup - var allTasks []model.Task +func createDataReqToData(specVersion string, createDataReq model.CreateDataReq) (model.Data, error) { + specVersionSpilit := strings.Split(specVersion, ".") + if len(specVersionSpilit) != 2 { + return model.Data{}, errors.New("invalid workflow spec version: " + specVersion) + } - for _, tgReq := range createDataReq.TaskGroups { - var tasks []model.Task - for _, tReq := range tgReq.Tasks { - tasks = append(tasks, model.Task{ - ID: uuid.New().String(), - Name: tReq.Name, - TaskComponent: tReq.TaskComponent, - RequestBody: tReq.RequestBody, - PathParams: tReq.PathParams, - Dependencies: tReq.Dependencies, - }) - } + specVersionMajor, err := strconv.Atoi(specVersionSpilit[0]) + if err != nil { + return model.Data{}, errors.New("invalid workflow spec version: " + specVersion) + } - allTasks = append(allTasks, tasks...) - taskGroups = append(taskGroups, model.TaskGroup{ - ID: uuid.New().String(), - Name: tgReq.Name, - Description: tgReq.Description, - Tasks: tasks, - }) + specVersionMinor, err := strconv.Atoi(specVersionSpilit[1]) + if err != nil { + return model.Data{}, errors.New("invalid workflow spec version: " + specVersion) } - for i, tgReq := range createDataReq.TaskGroups { - for j, tg := range taskGroups { - if tgReq.Name == tg.Name { - if i == j { - continue + var taskGroups []model.TaskGroup + var allTasks []model.Task + + if specVersionMajor > 0 && specVersionMajor <= 1 { + if specVersionMinor == 0 { + // v1.0 + for _, tgReq := range createDataReq.TaskGroups { + var tasks []model.Task + for _, tReq := range tgReq.Tasks { + tasks = append(tasks, model.Task{ + ID: uuid.New().String(), + Name: tReq.Name, + TaskComponent: tReq.TaskComponent, + RequestBody: tReq.RequestBody, + PathParams: tReq.PathParams, + Dependencies: tReq.Dependencies, + }) } - return model.Data{}, errors.New("Duplicated task group name: " + tg.Name) + allTasks = append(allTasks, tasks...) + taskGroups = append(taskGroups, model.TaskGroup{ + ID: uuid.New().String(), + Name: tgReq.Name, + Description: tgReq.Description, + Tasks: tasks, + }) } - } - } - for i, tCheck := range allTasks { - for j, t := range allTasks { - if tCheck.Name == t.Name { - if i == j { - continue + for i, tgReq := range createDataReq.TaskGroups { + for j, tg := range taskGroups { + if tgReq.Name == tg.Name { + if i == j { + continue + } + + return model.Data{}, errors.New("Duplicated task group name: " + tg.Name) + } } + } + + for i, tCheck := range allTasks { + for j, t := range allTasks { + if tCheck.Name == t.Name { + if i == j { + continue + } - return model.Data{}, errors.New("Duplicated task name: " + t.Name) + return model.Data{}, errors.New("Duplicated task name: " + t.Name) + } + } } + } else { + return model.Data{}, errors.New("Unsupported workflow spec version: " + specVersion) } + } else { + return model.Data{}, errors.New("Unsupported workflow spec version: " + specVersion) } return model.Data{ @@ -138,13 +163,19 @@ func CreateWorkflow(c echo.Context) error { return common.ReturnErrorMsg(c, "Please provide the name.") } - workflowData, err := createDataReqToData(createWorkflowReq.Data) + var specVersion = model.WorkflowSpecVersion_LATEST + if createWorkflowReq.SpecVersion != "" { + specVersion = createWorkflowReq.SpecVersion + } + + workflowData, err := createDataReqToData(specVersion, createWorkflowReq.Data) if err != nil { return common.ReturnErrorMsg(c, err.Error()) } var workflow model.Workflow workflow.ID = uuid.New().String() + workflow.SpecVersion = specVersion workflow.Name = createWorkflowReq.Name workflow.Data = workflowData @@ -418,7 +449,12 @@ func UpdateWorkflow(c echo.Context) error { oldWorkflow.Name = updateWorkflowReq.Name } - workflowData, err := createDataReqToData(updateWorkflowReq.Data) + var specVersion = model.WorkflowSpecVersion_LATEST + if updateWorkflowReq.SpecVersion != "" { + specVersion = updateWorkflowReq.SpecVersion + } + + workflowData, err := createDataReqToData(specVersion, updateWorkflowReq.Data) if err != nil { return common.ReturnErrorMsg(c, err.Error()) } diff --git a/pkg/api/rest/controller/workflowTemplate.go b/pkg/api/rest/controller/workflowTemplate.go index 8b76fbb..501a164 100644 --- a/pkg/api/rest/controller/workflowTemplate.go +++ b/pkg/api/rest/controller/workflowTemplate.go @@ -58,8 +58,9 @@ func GetWorkflowTemplateByName(c echo.Context) error { return common.ReturnErrorMsg(c, "workflow template not found with the provided name") } return c.JSONPretty(http.StatusOK, model.GetWorkflowTemplate{ - Name: workflowTemplate.Name, - Data: workflowTemplate.Data, + SpecVersion: workflowTemplate.SpecVersion, + Name: workflowTemplate.Name, + Data: workflowTemplate.Data, }, "") } diff --git a/pkg/api/rest/docs/docs.go b/pkg/api/rest/docs/docs.go index 0b8a1e1..183e0e5 100644 --- a/pkg/api/rest/docs/docs.go +++ b/pkg/api/rest/docs/docs.go @@ -1805,6 +1805,9 @@ const docTemplate = `{ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -1858,7 +1861,8 @@ const docTemplate = `{ "type": "object", "required": [ "data", - "name" + "name", + "spec_version" ], "properties": { "data": { @@ -1866,6 +1870,9 @@ const docTemplate = `{ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -2173,7 +2180,8 @@ const docTemplate = `{ "required": [ "data", "id", - "name" + "name", + "spec_version" ], "properties": { "created_at": { @@ -2188,6 +2196,9 @@ const docTemplate = `{ "name": { "type": "string" }, + "spec_version": { + "type": "string" + }, "updated_at": { "type": "string" } @@ -2249,7 +2260,8 @@ const docTemplate = `{ "required": [ "data", "id", - "name" + "name", + "spec_version" ], "properties": { "data": { @@ -2260,6 +2272,9 @@ const docTemplate = `{ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -2268,6 +2283,7 @@ const docTemplate = `{ "required": [ "action", "id", + "spec_version", "workflowId" ], "properties": { @@ -2283,6 +2299,9 @@ const docTemplate = `{ "id": { "type": "string" }, + "spec_version": { + "type": "string" + }, "workflowId": { "type": "string" } diff --git a/pkg/api/rest/docs/swagger.json b/pkg/api/rest/docs/swagger.json index 134708f..a417709 100644 --- a/pkg/api/rest/docs/swagger.json +++ b/pkg/api/rest/docs/swagger.json @@ -1798,6 +1798,9 @@ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -1851,7 +1854,8 @@ "type": "object", "required": [ "data", - "name" + "name", + "spec_version" ], "properties": { "data": { @@ -1859,6 +1863,9 @@ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -2166,7 +2173,8 @@ "required": [ "data", "id", - "name" + "name", + "spec_version" ], "properties": { "created_at": { @@ -2181,6 +2189,9 @@ "name": { "type": "string" }, + "spec_version": { + "type": "string" + }, "updated_at": { "type": "string" } @@ -2242,7 +2253,8 @@ "required": [ "data", "id", - "name" + "name", + "spec_version" ], "properties": { "data": { @@ -2253,6 +2265,9 @@ }, "name": { "type": "string" + }, + "spec_version": { + "type": "string" } } }, @@ -2261,6 +2276,7 @@ "required": [ "action", "id", + "spec_version", "workflowId" ], "properties": { @@ -2276,6 +2292,9 @@ "id": { "type": "string" }, + "spec_version": { + "type": "string" + }, "workflowId": { "type": "string" } diff --git a/pkg/api/rest/docs/swagger.yaml b/pkg/api/rest/docs/swagger.yaml index 149db97..8d9e443 100644 --- a/pkg/api/rest/docs/swagger.yaml +++ b/pkg/api/rest/docs/swagger.yaml @@ -99,6 +99,8 @@ definitions: $ref: '#/definitions/github_com_cloud-barista_cm-cicada_pkg_api_rest_model.CreateDataReq' name: type: string + spec_version: + type: string required: - data - name @@ -139,9 +141,12 @@ definitions: $ref: '#/definitions/github_com_cloud-barista_cm-cicada_pkg_api_rest_model.CreateDataReq' name: type: string + spec_version: + type: string required: - data - name + - spec_version type: object github_com_cloud-barista_cm-cicada_pkg_api_rest_model.ParameterStructure: properties: @@ -355,12 +360,15 @@ definitions: type: string name: type: string + spec_version: + type: string updated_at: type: string required: - data - id - name + - spec_version type: object github_com_cloud-barista_cm-cicada_pkg_api_rest_model.WorkflowRun: properties: @@ -404,10 +412,13 @@ definitions: type: string name: type: string + spec_version: + type: string required: - data - id - name + - spec_version type: object github_com_cloud-barista_cm-cicada_pkg_api_rest_model.WorkflowVersion: properties: @@ -419,11 +430,14 @@ definitions: $ref: '#/definitions/github_com_cloud-barista_cm-cicada_pkg_api_rest_model.Workflow' id: type: string + spec_version: + type: string workflowId: type: string required: - action - id + - spec_version - workflowId type: object info: diff --git a/pkg/api/rest/model/workflow.go b/pkg/api/rest/model/workflow.go index c03b359..0c50c8d 100644 --- a/pkg/api/rest/model/workflow.go +++ b/pkg/api/rest/model/workflow.go @@ -9,6 +9,14 @@ import ( "gorm.io/gorm" ) +const ( + WorkflowSpecVersion_1_0 = "1.0" +) + +const ( + WorkflowSpecVersion_LATEST = WorkflowSpecVersion_1_0 +) + type Task struct { ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` Name string `json:"name" mapstructure:"name" validate:"required"` @@ -84,24 +92,27 @@ type CreateDataReq struct { } type Workflow struct { - ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` - Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` - Data Data `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` - CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:false" json:"created_at" mapstructure:"created_at"` - UpdatedAt time.Time `gorm:"column:updated_at;autoCreateTime:false" json:"updated_at" mapstructure:"updated_at"` + ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` + SpecVersion string `gorm:"column:spec_version" json:"spec_version" mapstructure:"spec_version" validate:"required"` + Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` + Data Data `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime:false" json:"created_at" mapstructure:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;autoCreateTime:false" json:"updated_at" mapstructure:"updated_at"` } type WorkflowVersion struct { - ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` - WorkflowID string `gorm:"column:workflowId" json:"workflowId" mapstructure:"workflowId" validate:"required"` - Data Workflow `gorm:"column:data" json:"data" mapstructure:"data"` - Action string `gorm:"column:action" json:"action" mapstructure:"action" validate:"required"` - CreatedAt time.Time `gorm:"column:created_at" json:"created_at" mapstructure:"created_at"` + ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` + SpecVersion string `gorm:"column:spec_version" json:"spec_version" mapstructure:"spec_version" validate:"required"` + WorkflowID string `gorm:"column:workflowId" json:"workflowId" mapstructure:"workflowId" validate:"required"` + Data Workflow `gorm:"column:data" json:"data" mapstructure:"data"` + Action string `gorm:"column:action" json:"action" mapstructure:"action" validate:"required"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at" mapstructure:"created_at"` } type CreateWorkflowReq struct { - Name string `gorm:"column:name" json:"name" mapstructure:"name" validate:"required"` - Data CreateDataReq `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` + SpecVersion string `json:"spec_version" mapstructure:"spec_version"` + Name string `json:"name" mapstructure:"name" validate:"required"` + Data CreateDataReq `json:"data" mapstructure:"data" validate:"required"` } type WorkflowRun struct { diff --git a/pkg/api/rest/model/workflowTemplate.go b/pkg/api/rest/model/workflowTemplate.go index 38846ad..991acca 100644 --- a/pkg/api/rest/model/workflowTemplate.go +++ b/pkg/api/rest/model/workflowTemplate.go @@ -1,12 +1,14 @@ package model type GetWorkflowTemplate struct { - Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` - Data CreateDataReq `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` + SpecVersion string `gorm:"column:spec_version" json:"spec_version" mapstructure:"spec_version" validate:"required"` + Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` + Data CreateDataReq `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` } type WorkflowTemplate struct { - ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` - Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` - Data CreateDataReq `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` + ID string `gorm:"primaryKey" json:"id" mapstructure:"id" validate:"required"` + SpecVersion string `gorm:"column:spec_version" json:"spec_version" mapstructure:"spec_version" validate:"required"` + Name string `gorm:"index:,column:name,unique;type:text collate nocase" json:"name" mapstructure:"name" validate:"required"` + Data CreateDataReq `gorm:"column:data" json:"data" mapstructure:"data" validate:"required"` }