Skip to content

Commit 15a5574

Browse files
authored
feat(ui,api): disable ascode edit for template generated components (#5138)
1 parent 8303caa commit 15a5574

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+394
-385
lines changed

cli/cdsctl/template.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/ovh/cds/cli"
1414
"github.com/ovh/cds/sdk"
15+
"github.com/ovh/cds/sdk/cdsclient"
1516
"github.com/ovh/cds/sdk/exportentities"
1617
)
1718

@@ -304,13 +305,16 @@ func templateDetachRun(v cli.Values) error {
304305
projectKey := v.GetString(_ProjectKey)
305306
workflowName := v.GetString(_WorkflowName)
306307

307-
// try to get an existing template instance for current workflow
308-
wti, err := client.WorkflowTemplateInstanceGet(projectKey, workflowName)
308+
// try to get workflow with a template instance if exists
309+
wk, err := client.WorkflowGet(projectKey, workflowName, cdsclient.WithTemplate())
309310
if err != nil {
310311
return err
311312
}
313+
if wk.TemplateInstance == nil {
314+
return fmt.Errorf("given workflow is was not generated by a template")
315+
}
312316

313-
if err := client.TemplateDeleteInstance(wti.Template.Group.Name, wti.Template.Slug, wti.ID); err != nil {
317+
if err := client.TemplateDeleteInstance(wk.TemplateInstance.Template.Group.Name, wk.TemplateInstance.Template.Slug, wk.TemplateInstance.ID); err != nil {
314318
return err
315319
}
316320

cli/cdsctl/template_apply.go

+17-11
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,18 @@ func templateApplyRun(v cli.Values) error {
117117
projectKey := v.GetString(_ProjectKey)
118118
workflowName := v.GetString(_WorkflowName)
119119

120-
var wti *sdk.WorkflowTemplateInstance
120+
var existingWorkflow *sdk.Workflow
121+
var existingWorkflowTemplateInstance *sdk.WorkflowTemplateInstance
121122
var err error
122123
if workflowName != "" {
123124
// try to get an existing template instance for current workflow
124-
wti, err = client.WorkflowTemplateInstanceGet(projectKey, workflowName)
125+
existingWorkflow, err = client.WorkflowGet(projectKey, workflowName, cdsclient.WithTemplate())
125126
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
126127
return err
127128
}
129+
if existingWorkflow != nil && existingWorkflow.TemplateInstance != nil {
130+
existingWorkflowTemplateInstance = existingWorkflow.TemplateInstance
131+
}
128132
}
129133

130134
wt, err := getTemplateFromCLI(v)
@@ -133,8 +137,8 @@ func templateApplyRun(v cli.Values) error {
133137
}
134138

135139
// if no template given from args, and exiting instance try to get it's template
136-
if wt == nil && wti != nil {
137-
wt = wti.Template
140+
if wt == nil && existingWorkflowTemplateInstance != nil {
141+
wt = existingWorkflowTemplateInstance.Template
138142
}
139143

140144
// if no template found for workflow or no instance, suggest one
@@ -150,9 +154,9 @@ func templateApplyRun(v cli.Values) error {
150154

151155
// init params map from previous template instance if exists
152156
params := make(map[string]string)
153-
if wti != nil {
157+
if existingWorkflowTemplateInstance != nil {
154158
for _, p := range wt.Parameters {
155-
if v, ok := wti.Request.Parameters[p.Key]; ok {
159+
if v, ok := existingWorkflowTemplateInstance.Request.Parameters[p.Key]; ok {
156160
params[p.Key] = v
157161
}
158162
}
@@ -168,8 +172,9 @@ func templateApplyRun(v cli.Values) error {
168172
params[ps[0]] = strings.Join(ps[1:], "=")
169173
}
170174

171-
importPush := v.GetBool("import-push")
172-
importAsCode := v.GetBool("import-as-code")
175+
// Import flags are not allowed if an existing ascode workflow exists
176+
importPush := v.GetBool("import-push") && (existingWorkflow == nil || existingWorkflow.FromRepository == "")
177+
importAsCode := v.GetBool("import-as-code") && (existingWorkflow == nil || existingWorkflow.FromRepository == "")
173178
detached := v.GetBool("detach")
174179

175180
// try to find existing .git repository
@@ -295,7 +300,8 @@ func templateApplyRun(v cli.Values) error {
295300
}
296301
}
297302

298-
if !importAsCode && !importPush {
303+
// We ask for import only if there is no existing workflow or if exists but not ascode
304+
if !importAsCode && !importPush && (existingWorkflow == nil || existingWorkflow.FromRepository == "") {
299305
if localRepoURL != "" {
300306
importAsCode = cli.AskConfirm(fmt.Sprintf("Import the generated workflow as code to the %s project", projectKey))
301307
}
@@ -317,7 +323,7 @@ func templateApplyRun(v cli.Values) error {
317323
return fmt.Errorf("Unable to create directory %s: %v", v.GetString("output-dir"), err)
318324
}
319325

320-
// check request before submit
326+
// Check request before submit
321327
req := sdk.WorkflowTemplateRequest{
322328
ProjectKey: projectKey,
323329
WorkflowName: workflowName,
@@ -333,7 +339,7 @@ func templateApplyRun(v cli.Values) error {
333339
return err
334340
}
335341

336-
// import or push the generated workflow if one option is set
342+
// Import or push the generated workflow if one option is set
337343
if importAsCode || importPush {
338344
var buf bytes.Buffer
339345
tr, err = teeTarReader(tr, &buf)

engine/api/api_routes.go

-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,6 @@ func (api *API) InitRouter() {
191191
r.Handle("/project/{permProjectKey}/export/application/{applicationName}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getApplicationExportHandler))
192192

193193
// Application
194-
r.Handle("/project/{permProjectKey}/ascode/application", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getAsCodeApplicationHandler))
195194
r.Handle("/project/{permProjectKey}/application/{applicationName}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getApplicationHandler), r.PUT(api.updateApplicationHandler), r.DELETE(api.deleteApplicationHandler))
196195
r.Handle("/project/{permProjectKey}/application/{applicationName}/metrics/{metricName}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getApplicationMetricHandler))
197196
r.Handle("/project/{permProjectKey}/application/{applicationName}/keys", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getKeysInApplicationHandler), r.POST(api.addKeyInApplicationHandler))
@@ -438,7 +437,6 @@ func (api *API) InitRouter() {
438437
r.Handle("/template/{groupName}/{templateSlug}/instance", Scope(sdk.AuthConsumerScopeTemplate), r.GET(api.getTemplateInstancesHandler))
439438
r.Handle("/template/{groupName}/{templateSlug}/instance/{instanceID}", Scope(sdk.AuthConsumerScopeTemplate), r.DELETE(api.deleteTemplateInstanceHandler))
440439
r.Handle("/template/{groupName}/{templateSlug}/usage", Scope(sdk.AuthConsumerScopeTemplate), r.GET(api.getTemplateUsageHandler))
441-
r.Handle("/project/{key}/workflow/{permWorkflowName}/templateInstance", Scope(sdk.AuthConsumerScopeTemplate), r.GET(api.getTemplateInstanceHandler))
442440

443441
//Not Found handler
444442
r.Mux.NotFoundHandler = http.HandlerFunc(NotFoundHandler)

engine/api/application.go

-14
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,6 @@ func (api *API) getApplicationsHandler() service.Handler {
106106
}
107107
}
108108

109-
func (api *API) getAsCodeApplicationHandler() service.Handler {
110-
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
111-
vars := mux.Vars(r)
112-
projectKey := vars[permProjectKey]
113-
fromRepo := FormString(r, "repo")
114-
115-
apps, err := application.LoadAsCode(api.mustDB(), projectKey, fromRepo)
116-
if err != nil {
117-
return sdk.WrapError(err, "cannot load application from repo %s for project %s from db", fromRepo, projectKey)
118-
}
119-
return service.WriteJSON(w, apps, http.StatusOK)
120-
}
121-
}
122-
123109
func (api *API) getApplicationHandler() service.Handler {
124110
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
125111
vars := mux.Vars(r)

engine/api/application/dao.go

-11
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,6 @@ func Exists(db gorp.SqlExecutor, projectKey, appName string) (bool, error) {
5959
return count == 1, nil
6060
}
6161

62-
// LoadAsCode load an ascode application from DB
63-
func LoadAsCode(db gorp.SqlExecutor, projectKey, fromRepo string) ([]sdk.Application, error) {
64-
query := gorpmapping.NewQuery(`
65-
SELECT application.*
66-
FROM application
67-
JOIN project ON project.id = application.project_id
68-
WHERE project.projectkey = $1
69-
AND application.from_repository = $2`).Args(projectKey, fromRepo)
70-
return getAll(context.Background(), db, nil, query)
71-
}
72-
7362
// LoadByName load an application from DB
7463
func LoadByName(db gorp.SqlExecutor, projectKey, appName string, opts ...LoadOptionFunc) (*sdk.Application, error) {
7564
query := gorpmapping.NewQuery(`

engine/api/application_test.go

-59
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,19 @@ package api
22

33
import (
44
"context"
5-
"encoding/json"
6-
"fmt"
7-
"net/http"
8-
"net/http/httptest"
95
"testing"
106

11-
"gopkg.in/yaml.v2"
12-
137
"github.com/stretchr/testify/assert"
148
"github.com/stretchr/testify/require"
159

1610
"github.com/ovh/cds/engine/api/application"
1711
"github.com/ovh/cds/engine/api/authentication"
1812
"github.com/ovh/cds/engine/api/authentication/builtin"
1913
"github.com/ovh/cds/engine/api/group"
20-
"github.com/ovh/cds/engine/api/repositoriesmanager"
2114
"github.com/ovh/cds/engine/api/test"
2215
"github.com/ovh/cds/engine/api/test/assets"
2316
"github.com/ovh/cds/sdk"
2417
"github.com/ovh/cds/sdk/cdsclient"
25-
"github.com/ovh/cds/sdk/exportentities"
2618
)
2719

2820
func Test_postApplicationMetadataHandler_AsProvider(t *testing.T) {
@@ -69,54 +61,3 @@ func Test_postApplicationMetadataHandler_AsProvider(t *testing.T) {
6961
test.NoError(t, err)
7062
assert.Equal(t, 1, len(apps))
7163
}
72-
73-
func Test_getAsCodeApplicationHandler(t *testing.T) {
74-
api, db, _, end := newTestAPI(t)
75-
defer end()
76-
77-
u, pass := assets.InsertAdminUser(t, db)
78-
pkey := sdk.RandomString(10)
79-
p := assets.InsertTestProject(t, db, api.Cache, pkey, pkey)
80-
81-
assert.NoError(t, repositoriesmanager.InsertForProject(db, p, &sdk.ProjectVCSServer{
82-
Name: "github",
83-
Data: map[string]string{
84-
"token": "foo",
85-
"secret": "bar",
86-
},
87-
}))
88-
89-
// Add application
90-
appS := `version: v1.0
91-
name: blabla
92-
vcs_server: github
93-
repo: sguiheux/demo
94-
vcs_ssh_key: proj-blabla
95-
`
96-
var eapp = new(exportentities.Application)
97-
assert.NoError(t, yaml.Unmarshal([]byte(appS), eapp))
98-
app, _, globalError := application.ParseAndImport(context.Background(), db, api.Cache, *p, eapp, application.ImportOptions{Force: true}, nil, u)
99-
assert.NoError(t, globalError)
100-
101-
app.FromRepository = "myrepository"
102-
assert.NoError(t, application.Update(db, app))
103-
104-
uri := api.Router.GetRoute("GET", api.getAsCodeApplicationHandler, map[string]string{
105-
"permProjectKey": pkey,
106-
})
107-
uri = fmt.Sprintf("%s?repo=myrepository", uri)
108-
109-
req, err := http.NewRequest("GET", uri, nil)
110-
test.NoError(t, err)
111-
assets.AuthentifyRequest(t, req, u, pass)
112-
113-
// Do the request
114-
w := httptest.NewRecorder()
115-
api.Router.Mux.ServeHTTP(w, req)
116-
assert.Equal(t, 200, w.Code)
117-
118-
var appDB []sdk.Application
119-
assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &appDB))
120-
assert.Equal(t, app.ID, appDB[0].ID)
121-
122-
}

engine/api/ascode.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (api *API) postResyncPRAsCodeHandler() service.Handler {
228228
}
229229
app = *appP
230230
case fromRepo != "":
231-
wkf, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, fromRepo)
231+
wkf, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, fromRepo, workflow.LoadOptions{})
232232
if err != nil {
233233
return err
234234
}

engine/api/ascode/ascode_pr.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type EntityData struct {
2727
}
2828

2929
// UpdateAsCodeResult pulls repositories operation and the create pullrequest + update workflow
30-
func UpdateAsCodeResult(ctx context.Context, db *gorp.DbMap, store cache.Store, proj sdk.Project, app *sdk.Application, ed EntityData, u sdk.Identifiable) *sdk.AsCodeEvent {
30+
func UpdateAsCodeResult(ctx context.Context, db *gorp.DbMap, store cache.Store, proj sdk.Project, app sdk.Application, ed EntityData, u sdk.Identifiable) *sdk.AsCodeEvent {
3131
tick := time.NewTicker(2 * time.Second)
3232
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
3333
defer func() {

engine/api/pipeline.go

+34-8
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func (api *API) updateAsCodePipelineHandler() service.Handler {
2525
vars := mux.Vars(r)
2626
key := vars[permProjectKey]
2727
name := vars["pipelineKey"]
28+
2829
branch := FormString(r, "branch")
2930
message := FormString(r, "message")
30-
fromRepo := FormString(r, "repo")
3131

3232
var p sdk.Pipeline
3333
if err := service.UnmarshalBody(r, &p); err != nil {
@@ -51,21 +51,32 @@ func (api *API) updateAsCodePipelineHandler() service.Handler {
5151
}
5252

5353
if pipelineDB.FromRepository == "" {
54-
return sdk.WithStack(sdk.ErrForbidden)
54+
return sdk.NewErrorFrom(sdk.ErrForbidden, "current pipeline is not ascode")
5555
}
5656

57-
apps, err := application.LoadAsCode(api.mustDB(), key, fromRepo)
57+
wkHolder, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, pipelineDB.FromRepository, workflow.LoadOptions{
58+
WithTemplate: true,
59+
})
5860
if err != nil {
5961
return err
6062
}
63+
if wkHolder.TemplateInstance != nil {
64+
return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot edit a pipeline that was generated by a template")
65+
}
6166

62-
app, err := application.LoadByIDWithClearVCSStrategyPassword(api.mustDB(), apps[0].ID)
63-
if err != nil {
64-
return err
67+
var rootApp *sdk.Application
68+
if wkHolder.WorkflowData.Node.Context != nil && wkHolder.WorkflowData.Node.Context.ApplicationID != 0 {
69+
rootApp, err = application.LoadByIDWithClearVCSStrategyPassword(api.mustDB(), wkHolder.WorkflowData.Node.Context.ApplicationID)
70+
if err != nil {
71+
return err
72+
}
73+
}
74+
if rootApp == nil {
75+
return sdk.NewErrorFrom(sdk.ErrWrongRequest, "cannot find the root application of the workflow %s that hold the pipeline", wkHolder.Name)
6576
}
6677

6778
u := getAPIConsumer(ctx)
68-
ope, err := pipeline.UpdatePipelineAsCode(ctx, api.Cache, api.mustDB(), *proj, p, app.VCSServer, app.RepositoryFullname, branch, message, app.RepositoryStrategy, u)
79+
ope, err := pipeline.UpdatePipelineAsCode(ctx, api.Cache, api.mustDB(), *proj, p, rootApp.VCSServer, rootApp.RepositoryFullname, branch, message, rootApp.RepositoryStrategy, u)
6980
if err != nil {
7081
return err
7182
}
@@ -78,7 +89,7 @@ func (api *API) updateAsCodePipelineHandler() service.Handler {
7889
Name: pipelineDB.Name,
7990
Operation: ope,
8091
}
81-
asCodeEvent := ascode.UpdateAsCodeResult(ctx, api.mustDB(), api.Cache, *proj, &apps[0], ed, u)
92+
asCodeEvent := ascode.UpdateAsCodeResult(ctx, api.mustDB(), api.Cache, *proj, *rootApp, ed, u)
8293
if asCodeEvent != nil {
8394
event.PublishAsCodeEvent(ctx, proj.Key, *asCodeEvent, u)
8495
}
@@ -284,6 +295,11 @@ func (api *API) getPipelineHandler() service.Handler {
284295
withWorkflows := FormBool(r, "withWorkflows")
285296
withAsCodeEvent := FormBool(r, "withAsCodeEvents")
286297

298+
proj, err := project.Load(api.mustDB(), projectKey)
299+
if err != nil {
300+
return err
301+
}
302+
287303
p, err := pipeline.LoadPipeline(ctx, api.mustDB(), projectKey, pipelineName, true)
288304
if err != nil {
289305
return sdk.WrapError(err, "cannot load pipeline %s", pipelineName)
@@ -306,6 +322,16 @@ func (api *API) getPipelineHandler() service.Handler {
306322
p.Usage.Workflows = wf
307323
}
308324

325+
if p.FromRepository != "" {
326+
wkAscodeHolder, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, p.FromRepository, workflow.LoadOptions{
327+
WithTemplate: true,
328+
})
329+
if err != nil {
330+
return sdk.NewErrorFrom(err, "cannot found workflow holder of the pipeline")
331+
}
332+
p.WorkflowAscodeHolder = wkAscodeHolder
333+
}
334+
309335
return service.WriteJSON(w, p, http.StatusOK)
310336
}
311337
}

0 commit comments

Comments
 (0)