Skip to content

Commit

Permalink
feat(cdsctl): pipeline import --force to detach from repo (#5575)
Browse files Browse the repository at this point in the history
* feat(cdsctl): pipeline import --force to detach from repo

Signed-off-by: Yvonnick Esnault <yvonnick.esnault@corp.ovh.com>
  • Loading branch information
yesnault committed Dec 2, 2020
1 parent 4195f31 commit 05a32fa
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 9 deletions.
7 changes: 6 additions & 1 deletion cli/cdsctl/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,12 @@ func pipelineExportRun(v cli.Values) error {
var pipelineImportCmd = cli.Command{
Name: "import",
Short: "Import CDS pipeline",
Long: "PATH: Path or URL of pipeline to import",
Long: `PATH: Path or URL of pipeline to import
Without --force, CDS won't update an existing pipeline.
With --force, CDS will allow you to update an existing pipeline. If this pipeline is managed 'as-code', CDS will
override it. This pipeline will be detached from the repository, until it is re-imported again following a commit on the repo.
`,
Ctx: []cli.Arg{
{Name: _ProjectKey},
},
Expand Down
2 changes: 1 addition & 1 deletion engine/api/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (api *API) postPipelineRollbackHandler() service.Handler {
}
}(&msgList)

if err := pipeline.ImportUpdate(ctx, tx, *proj, audit.Pipeline, msgChan); err != nil {
if err := pipeline.ImportUpdate(ctx, tx, *proj, audit.Pipeline, msgChan, pipeline.ImportOptions{}); err != nil {
return sdk.WrapError(err, "cannot import pipeline")
}

Expand Down
6 changes: 3 additions & 3 deletions engine/api/pipeline/pipeline_importer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
"github.com/go-gorp/gorp"

"github.com/ovh/cds/engine/api/action"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/engine/api/group"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/log"
)

//ImportUpdate import and update the pipeline in the project
func ImportUpdate(ctx context.Context, db gorp.SqlExecutor, proj sdk.Project, pip *sdk.Pipeline, msgChan chan<- sdk.Message) error {
func ImportUpdate(ctx context.Context, db gorp.SqlExecutor, proj sdk.Project, pip *sdk.Pipeline, msgChan chan<- sdk.Message, opts ImportOptions) error {
t := time.Now()
log.Debug("ImportUpdate> Begin")
defer log.Debug("ImportUpdate> End (%d ns)", time.Since(t).Nanoseconds())
Expand All @@ -26,7 +26,7 @@ func ImportUpdate(ctx context.Context, db gorp.SqlExecutor, proj sdk.Project, pi
return sdk.WrapError(err, "Unable to load pipeline %s %s", proj.Key, pip.Name)
}

if oldPipeline.FromRepository != "" && pip.FromRepository != oldPipeline.FromRepository {
if !opts.Force && oldPipeline.FromRepository != "" && pip.FromRepository != oldPipeline.FromRepository {
return sdk.WrapError(sdk.ErrPipelineAsCodeOverride, "unable to update as code pipeline %s/%s.", oldPipeline.FromRepository, pip.FromRepository)
}

Expand Down
92 changes: 89 additions & 3 deletions engine/api/pipeline/pipeline_importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"github.com/stretchr/testify/assert"

"github.com/ovh/cds/engine/api/bootstrap"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/engine/api/event"
"github.com/ovh/cds/engine/api/pipeline"
"github.com/ovh/cds/engine/api/project"
"github.com/ovh/cds/engine/api/test"
"github.com/ovh/cds/engine/api/test/assets"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/log"
)
Expand All @@ -22,6 +22,7 @@ type args struct {
pkey string
pip *sdk.Pipeline
u *sdk.AuthentifiedUser
opts pipeline.ImportOptions
}

type testcase struct {
Expand Down Expand Up @@ -55,7 +56,7 @@ func testImportUpdate(t *testing.T, db gorp.SqlExecutor, store cache.Store, tt t
proj, err := project.Load(context.TODO(), db, tt.args.pip.ProjectKey, nil)
test.NoError(t, err)

if err := pipeline.ImportUpdate(context.TODO(), db, *proj, tt.args.pip, msgChan); (err != nil) != tt.wantErr {
if err := pipeline.ImportUpdate(context.TODO(), db, *proj, tt.args.pip, msgChan, tt.args.opts); (err != nil) != tt.wantErr {
t.Errorf("%q. ImportUpdate() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}

Expand Down Expand Up @@ -651,8 +652,93 @@ func TestImportUpdate(t *testing.T) {
},
}

var test10 = testcase{
name: "import an ascode pipeline with force update",
wantErr: false,
args: args{
u: u,
pkey: sdk.RandomString(7),
pip: &sdk.Pipeline{},
opts: pipeline.ImportOptions{Force: true},
},
setup: func(t *testing.T, args args) {
proj := assets.InsertTestProject(t, db, cache, args.pkey, args.pkey)

pipExisting := sdk.Pipeline{
Name: sdk.RandomString(10),
ProjectID: proj.ID,
FromRepository: "myrepofrom",
}
assert.NoError(t, pipeline.InsertPipeline(db, &pipExisting))

args.pip.Name = pipExisting.Name
args.pip.ProjectID = proj.ID
args.pip.ProjectKey = proj.Key
args.pip.Stages = []sdk.Stage{
{
BuildOrder: 1,
Enabled: true,
Jobs: []sdk.Job{
{
Enabled: false,
Action: sdk.Action{
Name: "Job 1",
Description: "This is the first job",
},
},
},
Name: "This is the first stage",
},
}
},
asserts: func(t *testing.T, pip sdk.Pipeline) {
assert.Equal(t, 1, len(pip.Stages))
assert.Equal(t, 1, len(pip.Stages[0].Jobs))
},
}

var test11 = testcase{
name: "import an ascode pipeline without force update",
wantErr: true,
args: args{
u: u,
pkey: sdk.RandomString(7),
pip: &sdk.Pipeline{},
},
setup: func(t *testing.T, args args) {
proj := assets.InsertTestProject(t, db, cache, args.pkey, args.pkey)

pipExisting := sdk.Pipeline{
Name: sdk.RandomString(10),
ProjectID: proj.ID,
FromRepository: "myrepofrom",
}
assert.NoError(t, pipeline.InsertPipeline(db, &pipExisting))

args.pip.Name = pipExisting.Name
args.pip.ProjectID = proj.ID
args.pip.ProjectKey = proj.Key
args.pip.Stages = []sdk.Stage{
{
BuildOrder: 1,
Enabled: true,
Jobs: []sdk.Job{
{
Enabled: false,
Action: sdk.Action{
Name: "Job 1",
Description: "This is the first job",
},
},
},
Name: "This is the first stage",
},
}
},
}

//Run the tests
var tests = []testcase{test1, test2, test3, test4, test5, test6, test7, test8, test9}
var tests = []testcase{test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11}
for _, tt := range tests {
testImportUpdate(t, db, cache, tt)
}
Expand Down
2 changes: 1 addition & 1 deletion engine/api/pipeline/pipeline_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func ParseAndImport(ctx context.Context, db gorp.SqlExecutor, cache cache.Store,
if exist && !opts.Force {
return pip, nil, sdk.ErrPipelineAlreadyExists
} else if exist {
globalError = ImportUpdate(ctx, db, proj, pip, msgChan)
globalError = ImportUpdate(ctx, db, proj, pip, msgChan, opts)
} else {
globalError = Import(ctx, db, cache, proj, pip, msgChan, u)
}
Expand Down

0 comments on commit 05a32fa

Please sign in to comment.