Skip to content

Commit 0834d56

Browse files
authored
feat(api): lock update ascode with template, workflow pull with template (#5134)
1 parent 724564e commit 0834d56

File tree

17 files changed

+86
-157
lines changed

17 files changed

+86
-157
lines changed

engine/api/environment/environment.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func LoadEnvironmentByID(db gorp.SqlExecutor, ID int64) (*sdk.Environment, error
7878
WHERE id = $1`
7979
if err := db.QueryRow(query, ID).Scan(&env.ID, &env.Name, &env.ProjectID, &env.FromRepository); err != nil {
8080
if err == sql.ErrNoRows {
81-
return nil, sdk.ErrEnvironmentNotFound
81+
return nil, sdk.WithStack(sdk.ErrEnvironmentNotFound)
8282
}
8383
return nil, err
8484
}
@@ -99,7 +99,7 @@ func LoadEnvironmentByName(db gorp.SqlExecutor, projectKey, envName string) (*sd
9999
var lastModified time.Time
100100
if err := db.QueryRow(query, projectKey, envName).Scan(&env.ID, &env.Name, &env.ProjectID, &env.FromRepository, &lastModified); err != nil {
101101
if err == sql.ErrNoRows {
102-
return nil, sdk.ErrorWithData(sdk.ErrEnvironmentNotFound, envName)
102+
return nil, sdk.WithData(sdk.ErrEnvironmentNotFound, envName)
103103
}
104104
return nil, sdk.WithStack(err)
105105
}

engine/api/pipeline/pipeline.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func LoadPipeline(ctx context.Context, db gorp.SqlExecutor, projectKey, name str
3131

3232
if err := db.SelectOne(&p, query, name, projectKey); err != nil {
3333
if err == sql.ErrNoRows {
34-
return nil, sdk.WithStack(sdk.ErrorWithData(sdk.ErrPipelineNotFound, name))
34+
return nil, sdk.WithData(sdk.ErrPipelineNotFound, name)
3535
}
3636
return nil, sdk.WithStack(err)
3737
}

engine/api/workflow.go

+1-16
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/ovh/cds/engine/api/project"
2222
"github.com/ovh/cds/engine/api/services"
2323
"github.com/ovh/cds/engine/api/workflow"
24-
"github.com/ovh/cds/engine/api/workflowtemplate"
2524
"github.com/ovh/cds/engine/service"
2625
"github.com/ovh/cds/sdk"
2726
"github.com/ovh/cds/sdk/exportentities"
@@ -86,6 +85,7 @@ func (api *API) getWorkflowHandler() service.Handler {
8685
WithLabels: withLabels,
8786
WithAsCodeUpdateEvent: withAsCodeEvents,
8887
WithIntegrations: true,
88+
WithTemplate: withTemplate,
8989
}
9090
w1, err := workflow.Load(ctx, api.mustDB(), api.Cache, *proj, name, opts)
9191
if err != nil {
@@ -113,21 +113,6 @@ func (api *API) getWorkflowHandler() service.Handler {
113113
w1.Audits = audits
114114
}
115115

116-
if withTemplate {
117-
if err := workflowtemplate.AggregateTemplateInstanceOnWorkflow(ctx, api.mustDB(), w1); err != nil {
118-
return err
119-
}
120-
if w1.TemplateInstance != nil {
121-
if err := workflowtemplate.LoadInstanceOptions.WithTemplate(ctx, api.mustDB(), w1.TemplateInstance); err != nil {
122-
return err
123-
}
124-
if w1.TemplateInstance.Template != nil {
125-
w1.FromTemplate = fmt.Sprintf("%s@%d", w1.TemplateInstance.Template.Path(), w1.TemplateInstance.WorkflowTemplateVersion)
126-
w1.TemplateUpToDate = w1.TemplateInstance.Template.Version == w1.TemplateInstance.WorkflowTemplateVersion
127-
}
128-
}
129-
}
130-
131116
if isAdmin(ctx) {
132117
w1.Permissions = sdk.Permissions{Readable: true, Writable: true, Executable: true}
133118
} else {

engine/api/workflow/dao.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/ovh/cds/engine/api/keys"
2525
"github.com/ovh/cds/engine/api/observability"
2626
"github.com/ovh/cds/engine/api/pipeline"
27+
"github.com/ovh/cds/engine/api/workflowtemplate"
2728
"github.com/ovh/cds/sdk"
2829
"github.com/ovh/cds/sdk/log"
2930
)
@@ -71,6 +72,7 @@ type LoadOptions struct {
7172
WithIcon bool
7273
WithAsCodeUpdateEvent bool
7374
WithIntegrations bool
75+
WithTemplate bool
7476
}
7577

7678
// UpdateOptions is option to parse a workflow
@@ -587,6 +589,18 @@ func load(ctx context.Context, db gorp.SqlExecutor, proj sdk.Project, opts LoadO
587589
res.EventIntegrations = integrations
588590
}
589591

592+
if opts.WithTemplate {
593+
wti, err := workflowtemplate.LoadInstanceByWorkflowID(ctx, db, res.ID, workflowtemplate.LoadInstanceOptions.WithTemplate)
594+
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
595+
return nil, err
596+
}
597+
if wti != nil {
598+
res.TemplateInstance = wti
599+
res.FromTemplate = fmt.Sprintf("%s@%d", wti.Template.Path(), wti.WorkflowTemplateVersion)
600+
res.TemplateUpToDate = wti.Template.Version == wti.WorkflowTemplateVersion
601+
}
602+
}
603+
590604
_, next = observability.Span(ctx, "workflow.load.loadNotifications")
591605
notifs, errN := loadNotifications(db, &res)
592606
next()
@@ -1256,7 +1270,7 @@ func checkProjectIntegration(proj sdk.Project, w *sdk.Workflow, n *sdk.Node) err
12561270
}
12571271
}
12581272
if ppProj.ID == 0 {
1259-
return sdk.WithStack(sdk.ErrorWithData(sdk.ErrIntegrationtNotFound, n.Context.ProjectIntegrationName))
1273+
return sdk.WithData(sdk.ErrIntegrationtNotFound, n.Context.ProjectIntegrationName)
12601274
}
12611275
w.ProjectIntegrations[ppProj.ID] = ppProj
12621276
n.Context.ProjectIntegrationID = ppProj.ID
@@ -1338,7 +1352,7 @@ func checkApplication(store cache.Store, db gorp.SqlExecutor, proj sdk.Project,
13381352
appDB, err := application.LoadByName(db, proj.Key, n.Context.ApplicationName, application.LoadOptions.WithDeploymentStrategies, application.LoadOptions.WithVariables)
13391353
if err != nil {
13401354
if sdk.ErrorIs(err, sdk.ErrNotFound) {
1341-
return sdk.WithStack(sdk.ErrorWithData(sdk.ErrNotFound, n.Context.ApplicationName))
1355+
return sdk.WithData(sdk.ErrNotFound, n.Context.ApplicationName)
13421356
}
13431357
return sdk.WrapError(err, "unable to load application %s", n.Context.ApplicationName)
13441358
}

engine/api/workflow/workflow_exporter.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package workflow
22

33
import (
44
"context"
5+
"fmt"
56

67
"github.com/go-gorp/gorp"
78

@@ -19,9 +20,9 @@ func Export(ctx context.Context, db gorp.SqlExecutor, cache cache.Store, proj sd
1920
ctx, end := observability.Span(ctx, "workflow.Export")
2021
defer end()
2122

22-
wf, errload := Load(ctx, db, cache, proj, name, LoadOptions{})
23-
if errload != nil {
24-
return v2.Workflow{}, sdk.WrapError(errload, "workflow.Export> Cannot load workflow %s", name)
23+
wf, err := Load(ctx, db, cache, proj, name, LoadOptions{})
24+
if err != nil {
25+
return v2.Workflow{}, sdk.WrapError(err, "cannot load workflow %s", name)
2526
}
2627

2728
// If repo is from as-code do not export WorkflowSkipIfOnlyOneRepoWebhook
@@ -48,11 +49,22 @@ func Pull(ctx context.Context, db gorp.SqlExecutor, cache cache.Store, proj sdk.
4849

4950
wf, err := Load(ctx, db, cache, proj, name, LoadOptions{
5051
DeepPipeline: true,
52+
WithTemplate: true,
5153
})
5254
if err != nil {
5355
return wp, sdk.WrapError(err, "cannot load workflow %s", name)
5456
}
5557

58+
if wf.TemplateInstance != nil {
59+
return exportentities.WorkflowComponents{
60+
Template: exportentities.TemplateInstance{
61+
Name: wf.Name,
62+
From: fmt.Sprintf("%s@%d", wf.TemplateInstance.Template.Path(), wf.TemplateInstance.WorkflowTemplateVersion),
63+
Parameters: wf.TemplateInstance.Request.Parameters,
64+
},
65+
}, nil
66+
}
67+
5668
// Reload app to retrieve secrets
5769
for i := range wf.Applications {
5870
app := wf.Applications[i]

engine/api/workflow/workflow_parser.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ func ParseAndImport(ctx context.Context, db gorp.SqlExecutor, store cache.Store,
6969
// Get spawn infos from error
7070
msg, ok := sdk.ErrorToMessage(err)
7171
if ok {
72-
return nil, []sdk.Message{msg}, sdk.WrapError(err, "Workflow is not valid")
72+
return nil, []sdk.Message{msg}, sdk.WrapError(err, "workflow is not valid")
7373
}
74-
return nil, nil, sdk.WrapError(err, "Workflow is not valid")
74+
return nil, nil, sdk.WrapError(err, "workflow is not valid")
7575
}
7676

7777
if err := RenameNode(ctx, db, w); err != nil {

engine/api/workflow_ascode.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,13 @@ func (api *API) getWorkflowAsCodeHandler() service.Handler {
3737
}
3838
}
3939

40-
// postWorkflowAsCodeHandler Update an as code workflow
41-
// @title Make the workflow as code
42-
// @title Update an as code workflow
40+
// postWorkflowAsCodeHandler update an ascode workflow, this will create a pull request to target repository.
4341
func (api *API) postWorkflowAsCodeHandler() service.Handler {
4442
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
4543
vars := mux.Vars(r)
4644
key := vars["key"]
4745
workflowName := vars["permWorkflowName"]
46+
4847
migrate := FormBool(r, "migrate")
4948
branch := FormString(r, "branch")
5049
message := FormString(r, "message")
@@ -64,6 +63,7 @@ func (api *API) postWorkflowAsCodeHandler() service.Handler {
6463
wfDB, err := workflow.Load(ctx, api.mustDB(), api.Cache, *p, workflowName, workflow.LoadOptions{
6564
DeepPipeline: migrate,
6665
WithAsCodeUpdateEvent: migrate,
66+
WithTemplate: true,
6767
})
6868
if err != nil {
6969
return err
@@ -84,7 +84,11 @@ func (api *API) postWorkflowAsCodeHandler() service.Handler {
8484

8585
// UPDATE EXISTING AS CODE WORKFLOW
8686
if wfDB.FromRepository == "" {
87-
return sdk.WithStack(sdk.ErrForbidden)
87+
return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot update a workflow that is not ascode")
88+
}
89+
90+
if wfDB.TemplateInstance != nil {
91+
return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot update a workflow that was generated by a template")
8892
}
8993

9094
// Get workflow from body

engine/api/workflow_import.go

+7-9
Original file line numberDiff line numberDiff line change
@@ -217,25 +217,23 @@ func (api *API) putWorkflowImportHandler() service.Handler {
217217

218218
// if workflow is as-code, we can't save it from edit as yml
219219
if wf.FromRepository != "" {
220-
return sdk.WithStack(sdk.ErrForbidden)
220+
return sdk.NewErrorFrom(sdk.ErrForbidden, "can't edit a workflow that is ascode")
221221
}
222222

223-
tx, errtx := api.mustDB().Begin()
224-
if errtx != nil {
225-
return sdk.WrapError(errtx, "Unable to start transaction")
223+
tx, err := api.mustDB().Begin()
224+
if err != nil {
225+
return sdk.WrapError(err, "unable to start transaction")
226226
}
227-
defer func() {
228-
_ = tx.Rollback()
229-
}()
227+
defer tx.Rollback() //nolint
230228

231229
wrkflw, msgList, globalError := workflow.ParseAndImport(ctx, tx, api.Cache, *proj, wf, ew, u, workflow.ImportOptions{Force: true, WorkflowName: wfName})
232230
msgListString := translate(r, msgList)
233231
if globalError != nil {
234232
if len(msgListString) != 0 {
235233
sdkErr := sdk.ExtractHTTPError(globalError, r.Header.Get("Accept-Language"))
236-
return service.WriteJSON(w, append(msgListString, sdkErr.Message), sdkErr.Status)
234+
return service.WriteJSON(w, append(msgListString, sdkErr.Error()), sdkErr.Status)
237235
}
238-
return sdk.WrapError(globalError, "Unable to import workflow %s", ew.GetName())
236+
return sdk.WrapError(globalError, "unable to import workflow %s", ew.GetName())
239237
}
240238

241239
if err := tx.Commit(); err != nil {

engine/api/workflow_run.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/ovh/cds/engine/api/permission"
2424
"github.com/ovh/cds/engine/api/project"
2525
"github.com/ovh/cds/engine/api/workflow"
26-
"github.com/ovh/cds/engine/api/workflowtemplate"
2726
"github.com/ovh/cds/engine/service"
2827
"github.com/ovh/cds/sdk"
2928
"github.com/ovh/cds/sdk/log"
@@ -894,15 +893,12 @@ func (api *API) postWorkflowRunHandler() service.Handler {
894893
WithAsCodeUpdateEvent: true,
895894
WithIcon: true,
896895
WithIntegrations: true,
896+
WithTemplate: true,
897897
})
898898
if errWf != nil {
899899
return sdk.WrapError(errWf, "unable to load workflow %s", name)
900900
}
901901

902-
if err := workflowtemplate.AggregateTemplateInstanceOnWorkflow(ctx, api.mustDB(), wf); err != nil {
903-
return sdk.WrapError(err, "cannot load workflow template")
904-
}
905-
906902
// Check node permission
907903
if isService := isService(ctx); !isService && !permission.AccessToWorkflowNode(ctx, api.mustDB(), wf, &wf.WorkflowData.Node, getAPIConsumer(ctx), sdk.PermissionReadExecute) {
908904
return sdk.WrapError(sdk.ErrNoPermExecution, "not enough right on node %s", wf.WorkflowData.Node.Name)

engine/api/workflowtemplate/aggregate.go

-39
This file was deleted.

engine/api/workflowtemplate/aggregate_test.go

-51
This file was deleted.

engine/repositories/processor.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error {
5555
}
5656
log.ErrorWithFields(ctx, fields, "%s", err)
5757

58-
op.Error = sdk.Cause(err).Error()
58+
op.Error = sdk.ExtractHTTPError(err, "").Error()
5959
op.Status = sdk.OperationStatusError
6060
} else {
6161
op.Error = ""
@@ -70,7 +70,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error {
7070
}
7171
log.ErrorWithFields(ctx, fields, "%s", err)
7272

73-
op.Error = sdk.Cause(err).Error()
73+
op.Error = sdk.ExtractHTTPError(err, "").Error()
7474
op.Status = sdk.OperationStatusError
7575
} else {
7676
op.Error = ""
@@ -91,7 +91,7 @@ func (s *Service) do(ctx context.Context, op sdk.Operation) error {
9191
}
9292
log.ErrorWithFields(ctx, fields, "%s", err)
9393

94-
op.Error = sdk.Cause(err).Error()
94+
op.Error = sdk.ExtractHTTPError(err, "").Error()
9595
op.Status = sdk.OperationStatusError
9696
} else {
9797
op.Error = ""

0 commit comments

Comments
 (0)