Skip to content

Commit

Permalink
feat(ui,api): disable ascode edit for template generated components (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlt authored May 5, 2020
1 parent 8303caa commit 15a5574
Show file tree
Hide file tree
Showing 50 changed files with 394 additions and 385 deletions.
10 changes: 7 additions & 3 deletions cli/cdsctl/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/ovh/cds/cli"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/cdsclient"
"github.com/ovh/cds/sdk/exportentities"
)

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

// try to get an existing template instance for current workflow
wti, err := client.WorkflowTemplateInstanceGet(projectKey, workflowName)
// try to get workflow with a template instance if exists
wk, err := client.WorkflowGet(projectKey, workflowName, cdsclient.WithTemplate())
if err != nil {
return err
}
if wk.TemplateInstance == nil {
return fmt.Errorf("given workflow is was not generated by a template")
}

if err := client.TemplateDeleteInstance(wti.Template.Group.Name, wti.Template.Slug, wti.ID); err != nil {
if err := client.TemplateDeleteInstance(wk.TemplateInstance.Template.Group.Name, wk.TemplateInstance.Template.Slug, wk.TemplateInstance.ID); err != nil {
return err
}

Expand Down
28 changes: 17 additions & 11 deletions cli/cdsctl/template_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,18 @@ func templateApplyRun(v cli.Values) error {
projectKey := v.GetString(_ProjectKey)
workflowName := v.GetString(_WorkflowName)

var wti *sdk.WorkflowTemplateInstance
var existingWorkflow *sdk.Workflow
var existingWorkflowTemplateInstance *sdk.WorkflowTemplateInstance
var err error
if workflowName != "" {
// try to get an existing template instance for current workflow
wti, err = client.WorkflowTemplateInstanceGet(projectKey, workflowName)
existingWorkflow, err = client.WorkflowGet(projectKey, workflowName, cdsclient.WithTemplate())
if err != nil && !sdk.ErrorIs(err, sdk.ErrNotFound) {
return err
}
if existingWorkflow != nil && existingWorkflow.TemplateInstance != nil {
existingWorkflowTemplateInstance = existingWorkflow.TemplateInstance
}
}

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

// if no template given from args, and exiting instance try to get it's template
if wt == nil && wti != nil {
wt = wti.Template
if wt == nil && existingWorkflowTemplateInstance != nil {
wt = existingWorkflowTemplateInstance.Template
}

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

// init params map from previous template instance if exists
params := make(map[string]string)
if wti != nil {
if existingWorkflowTemplateInstance != nil {
for _, p := range wt.Parameters {
if v, ok := wti.Request.Parameters[p.Key]; ok {
if v, ok := existingWorkflowTemplateInstance.Request.Parameters[p.Key]; ok {
params[p.Key] = v
}
}
Expand All @@ -168,8 +172,9 @@ func templateApplyRun(v cli.Values) error {
params[ps[0]] = strings.Join(ps[1:], "=")
}

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

// try to find existing .git repository
Expand Down Expand Up @@ -295,7 +300,8 @@ func templateApplyRun(v cli.Values) error {
}
}

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

// check request before submit
// Check request before submit
req := sdk.WorkflowTemplateRequest{
ProjectKey: projectKey,
WorkflowName: workflowName,
Expand All @@ -333,7 +339,7 @@ func templateApplyRun(v cli.Values) error {
return err
}

// import or push the generated workflow if one option is set
// Import or push the generated workflow if one option is set
if importAsCode || importPush {
var buf bytes.Buffer
tr, err = teeTarReader(tr, &buf)
Expand Down
2 changes: 0 additions & 2 deletions engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ func (api *API) InitRouter() {
r.Handle("/project/{permProjectKey}/export/application/{applicationName}", Scope(sdk.AuthConsumerScopeProject), r.GET(api.getApplicationExportHandler))

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

//Not Found handler
r.Mux.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
Expand Down
14 changes: 0 additions & 14 deletions engine/api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,6 @@ func (api *API) getApplicationsHandler() service.Handler {
}
}

func (api *API) getAsCodeApplicationHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
projectKey := vars[permProjectKey]
fromRepo := FormString(r, "repo")

apps, err := application.LoadAsCode(api.mustDB(), projectKey, fromRepo)
if err != nil {
return sdk.WrapError(err, "cannot load application from repo %s for project %s from db", fromRepo, projectKey)
}
return service.WriteJSON(w, apps, http.StatusOK)
}
}

func (api *API) getApplicationHandler() service.Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
Expand Down
11 changes: 0 additions & 11 deletions engine/api/application/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,6 @@ func Exists(db gorp.SqlExecutor, projectKey, appName string) (bool, error) {
return count == 1, nil
}

// LoadAsCode load an ascode application from DB
func LoadAsCode(db gorp.SqlExecutor, projectKey, fromRepo string) ([]sdk.Application, error) {
query := gorpmapping.NewQuery(`
SELECT application.*
FROM application
JOIN project ON project.id = application.project_id
WHERE project.projectkey = $1
AND application.from_repository = $2`).Args(projectKey, fromRepo)
return getAll(context.Background(), db, nil, query)
}

// LoadByName load an application from DB
func LoadByName(db gorp.SqlExecutor, projectKey, appName string, opts ...LoadOptionFunc) (*sdk.Application, error) {
query := gorpmapping.NewQuery(`
Expand Down
59 changes: 0 additions & 59 deletions engine/api/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,19 @@ package api

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"

"gopkg.in/yaml.v2"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ovh/cds/engine/api/application"
"github.com/ovh/cds/engine/api/authentication"
"github.com/ovh/cds/engine/api/authentication/builtin"
"github.com/ovh/cds/engine/api/group"
"github.com/ovh/cds/engine/api/repositoriesmanager"
"github.com/ovh/cds/engine/api/test"
"github.com/ovh/cds/engine/api/test/assets"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/cdsclient"
"github.com/ovh/cds/sdk/exportentities"
)

func Test_postApplicationMetadataHandler_AsProvider(t *testing.T) {
Expand Down Expand Up @@ -69,54 +61,3 @@ func Test_postApplicationMetadataHandler_AsProvider(t *testing.T) {
test.NoError(t, err)
assert.Equal(t, 1, len(apps))
}

func Test_getAsCodeApplicationHandler(t *testing.T) {
api, db, _, end := newTestAPI(t)
defer end()

u, pass := assets.InsertAdminUser(t, db)
pkey := sdk.RandomString(10)
p := assets.InsertTestProject(t, db, api.Cache, pkey, pkey)

assert.NoError(t, repositoriesmanager.InsertForProject(db, p, &sdk.ProjectVCSServer{
Name: "github",
Data: map[string]string{
"token": "foo",
"secret": "bar",
},
}))

// Add application
appS := `version: v1.0
name: blabla
vcs_server: github
repo: sguiheux/demo
vcs_ssh_key: proj-blabla
`
var eapp = new(exportentities.Application)
assert.NoError(t, yaml.Unmarshal([]byte(appS), eapp))
app, _, globalError := application.ParseAndImport(context.Background(), db, api.Cache, *p, eapp, application.ImportOptions{Force: true}, nil, u)
assert.NoError(t, globalError)

app.FromRepository = "myrepository"
assert.NoError(t, application.Update(db, app))

uri := api.Router.GetRoute("GET", api.getAsCodeApplicationHandler, map[string]string{
"permProjectKey": pkey,
})
uri = fmt.Sprintf("%s?repo=myrepository", uri)

req, err := http.NewRequest("GET", uri, nil)
test.NoError(t, err)
assets.AuthentifyRequest(t, req, u, pass)

// Do the request
w := httptest.NewRecorder()
api.Router.Mux.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)

var appDB []sdk.Application
assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &appDB))
assert.Equal(t, app.ID, appDB[0].ID)

}
2 changes: 1 addition & 1 deletion engine/api/ascode.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (api *API) postResyncPRAsCodeHandler() service.Handler {
}
app = *appP
case fromRepo != "":
wkf, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, fromRepo)
wkf, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, fromRepo, workflow.LoadOptions{})
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion engine/api/ascode/ascode_pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type EntityData struct {
}

// UpdateAsCodeResult pulls repositories operation and the create pullrequest + update workflow
func UpdateAsCodeResult(ctx context.Context, db *gorp.DbMap, store cache.Store, proj sdk.Project, app *sdk.Application, ed EntityData, u sdk.Identifiable) *sdk.AsCodeEvent {
func UpdateAsCodeResult(ctx context.Context, db *gorp.DbMap, store cache.Store, proj sdk.Project, app sdk.Application, ed EntityData, u sdk.Identifiable) *sdk.AsCodeEvent {
tick := time.NewTicker(2 * time.Second)
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer func() {
Expand Down
42 changes: 34 additions & 8 deletions engine/api/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ func (api *API) updateAsCodePipelineHandler() service.Handler {
vars := mux.Vars(r)
key := vars[permProjectKey]
name := vars["pipelineKey"]

branch := FormString(r, "branch")
message := FormString(r, "message")
fromRepo := FormString(r, "repo")

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

if pipelineDB.FromRepository == "" {
return sdk.WithStack(sdk.ErrForbidden)
return sdk.NewErrorFrom(sdk.ErrForbidden, "current pipeline is not ascode")
}

apps, err := application.LoadAsCode(api.mustDB(), key, fromRepo)
wkHolder, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, pipelineDB.FromRepository, workflow.LoadOptions{
WithTemplate: true,
})
if err != nil {
return err
}
if wkHolder.TemplateInstance != nil {
return sdk.NewErrorFrom(sdk.ErrForbidden, "cannot edit a pipeline that was generated by a template")
}

app, err := application.LoadByIDWithClearVCSStrategyPassword(api.mustDB(), apps[0].ID)
if err != nil {
return err
var rootApp *sdk.Application
if wkHolder.WorkflowData.Node.Context != nil && wkHolder.WorkflowData.Node.Context.ApplicationID != 0 {
rootApp, err = application.LoadByIDWithClearVCSStrategyPassword(api.mustDB(), wkHolder.WorkflowData.Node.Context.ApplicationID)
if err != nil {
return err
}
}
if rootApp == nil {
return sdk.NewErrorFrom(sdk.ErrWrongRequest, "cannot find the root application of the workflow %s that hold the pipeline", wkHolder.Name)
}

u := getAPIConsumer(ctx)
ope, err := pipeline.UpdatePipelineAsCode(ctx, api.Cache, api.mustDB(), *proj, p, app.VCSServer, app.RepositoryFullname, branch, message, app.RepositoryStrategy, u)
ope, err := pipeline.UpdatePipelineAsCode(ctx, api.Cache, api.mustDB(), *proj, p, rootApp.VCSServer, rootApp.RepositoryFullname, branch, message, rootApp.RepositoryStrategy, u)
if err != nil {
return err
}
Expand All @@ -78,7 +89,7 @@ func (api *API) updateAsCodePipelineHandler() service.Handler {
Name: pipelineDB.Name,
Operation: ope,
}
asCodeEvent := ascode.UpdateAsCodeResult(ctx, api.mustDB(), api.Cache, *proj, &apps[0], ed, u)
asCodeEvent := ascode.UpdateAsCodeResult(ctx, api.mustDB(), api.Cache, *proj, *rootApp, ed, u)
if asCodeEvent != nil {
event.PublishAsCodeEvent(ctx, proj.Key, *asCodeEvent, u)
}
Expand Down Expand Up @@ -284,6 +295,11 @@ func (api *API) getPipelineHandler() service.Handler {
withWorkflows := FormBool(r, "withWorkflows")
withAsCodeEvent := FormBool(r, "withAsCodeEvents")

proj, err := project.Load(api.mustDB(), projectKey)
if err != nil {
return err
}

p, err := pipeline.LoadPipeline(ctx, api.mustDB(), projectKey, pipelineName, true)
if err != nil {
return sdk.WrapError(err, "cannot load pipeline %s", pipelineName)
Expand All @@ -306,6 +322,16 @@ func (api *API) getPipelineHandler() service.Handler {
p.Usage.Workflows = wf
}

if p.FromRepository != "" {
wkAscodeHolder, err := workflow.LoadByRepo(ctx, api.Cache, api.mustDB(), *proj, p.FromRepository, workflow.LoadOptions{
WithTemplate: true,
})
if err != nil {
return sdk.NewErrorFrom(err, "cannot found workflow holder of the pipeline")
}
p.WorkflowAscodeHolder = wkAscodeHolder
}

return service.WriteJSON(w, p, http.StatusOK)
}
}
Expand Down
Loading

0 comments on commit 15a5574

Please sign in to comment.