Skip to content

Commit

Permalink
feat: detach fromRepository with force import
Browse files Browse the repository at this point in the history
Signed-off-by: Yvonnick Esnault <yvonnick.esnault@corp.ovh.com>
  • Loading branch information
yesnault committed Dec 2, 2020
1 parent cc5bc23 commit 920bc09
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 6 deletions.
6 changes: 6 additions & 0 deletions cli/cdsctl/application.go
Expand Up @@ -121,6 +121,12 @@ func applicationDeleteRun(v cli.Values) error {
var applicationImportCmd = cli.Command{
Name: "import",
Short: "Import an application with a local filepath or an URL",
Long: `PATH: Path or URL of application to import
Without --force, CDS won't update an existing application.
With --force, CDS will allow you to update an existing application. If this application is managed 'as-code', CDS will
override it. This application 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
6 changes: 6 additions & 0 deletions cli/cdsctl/environment.go
Expand Up @@ -90,6 +90,12 @@ func environmentDeleteRun(v cli.Values) error {
var environmentImportCmd = cli.Command{
Name: "import",
Short: "Import an environment with local filepath or URL",
Long: `PATH: Path or URL of environment to import
Without --force, CDS won't update an existing environment.
With --force, CDS will allow you to update an existing environment. If this environment is managed 'as-code', CDS will
override it. This environment 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
3 changes: 3 additions & 0 deletions cli/cdsctl/workflow_import.go
Expand Up @@ -16,6 +16,9 @@ In case you want to import just your workflow. Instead of use a local file you c
If you want to update also dependencies likes pipelines, applications or environments at same time you have to use workflow push instead workflow import.
Without --force, CDS won't update an existing workflow.
With --force, CDS will allow you to update an existing workflow. If this workflow is managed 'as-code', CDS will
override it. This workflow 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
10 changes: 6 additions & 4 deletions engine/api/application/application_parser.go
Expand Up @@ -6,8 +6,8 @@ import (
"strings"
"sync"

"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/engine/api/keys"
"github.com/ovh/cds/engine/cache"
"github.com/ovh/cds/engine/gorpmapper"
"github.com/ovh/cds/sdk"
"github.com/ovh/cds/sdk/exportentities"
Expand All @@ -32,7 +32,7 @@ func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, cache
return nil, nil, msgList, sdk.WrapError(sdk.ErrInvalidApplicationPattern, "application name %s do not respect pattern %s", eapp.Name, sdk.NamePattern)
}

//Check if app exist
//Check if app exists
oldApp, err := LoadByName(db, proj.Key, eapp.Name,
LoadOptions.WithVariablesWithClearPassword,
LoadOptions.WithClearKeys,
Expand All @@ -42,12 +42,14 @@ func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, cache
return nil, nil, msgList, sdk.WrapError(err, "unable to load application")
}

//If the application exist and we don't want to force, raise an error
//If the application exists and we don't want to force, raise an error
if oldApp != nil && !opts.Force {
return nil, nil, msgList, sdk.WithStack(sdk.ErrApplicationExist)
}

if oldApp != nil && oldApp.FromRepository != "" && opts.FromRepository != oldApp.FromRepository {
if opts.Force && opts.FromRepository == "" {
log.Debug("ParseAndImport>> Force import application %s in project %s without fromRepository", eapp.Name, proj.Key)
} else if oldApp != nil && oldApp.FromRepository != "" && opts.FromRepository != oldApp.FromRepository {
return nil, nil, msgList, sdk.NewErrorFrom(sdk.ErrApplicationAsCodeOverride, "unable to update existing ascode application from %s", oldApp.FromRepository)
}

Expand Down
46 changes: 46 additions & 0 deletions engine/api/application/application_parser_test.go
@@ -0,0 +1,46 @@
package application_test

import (
"context"
"testing"

"github.com/ovh/cds/engine/api/application"
"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/exportentities"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

func TestParseAndImport(t *testing.T) {
db, cache := test.SetupPG(t)
u, _ := assets.InsertAdminUser(t, db)

key := sdk.RandomString(10)
appName := sdk.RandomString(10)
proj := assets.InsertTestProject(t, db, cache, key, key)
app1 := sdk.Application{
Name: appName,
FromRepository: "foo",
}
require.NoError(t, application.Insert(db, *proj, &app1))

var eapp = new(exportentities.Application)

body := []byte(`
version: v1.0
name: ` + appName + `
`)
errapp := yaml.Unmarshal(body, eapp)
require.NoError(t, errapp)

_, _, _, globalError := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: false}, nil, u)
require.Error(t, globalError)

_, _, _, globalError2 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true, FromRepository: "bar"}, nil, u)
require.Error(t, globalError2)

_, _, _, globalError3 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true}, nil, u)
require.NoError(t, globalError3)
}
4 changes: 3 additions & 1 deletion engine/api/environment/environment_parser.go
Expand Up @@ -44,7 +44,9 @@ func ParseAndImport(db gorpmapper.SqlExecutorWithTx, proj sdk.Project, eenv expo
exist = true
}

if oldEnv != nil && oldEnv.FromRepository != "" && opts.FromRepository != oldEnv.FromRepository {
if opts.Force && opts.FromRepository == "" {
log.Debug("ParseAndImport>> Force import environment %s in project %s without fromRepository", eenv.Name, proj.Key)
} else if oldEnv != nil && oldEnv.FromRepository != "" && opts.FromRepository != oldEnv.FromRepository {
return nil, nil, nil, sdk.NewErrorFrom(sdk.ErrEnvironmentAsCodeOverride, "unable to update existing ascode environment from %s", oldEnv.FromRepository)
}

Expand Down
47 changes: 47 additions & 0 deletions engine/api/environment/environment_parser_test.go
@@ -0,0 +1,47 @@
package environment_test

import (
"testing"

"github.com/ovh/cds/engine/api/environment"
"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/exportentities"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

func TestParseAndImport(t *testing.T) {
db, cache := test.SetupPG(t)
u, _ := assets.InsertAdminUser(t, db)

key := sdk.RandomString(10)
envName := sdk.RandomString(10)
proj := assets.InsertTestProject(t, db, cache, key, key)
env1 := sdk.Environment{
Name: envName,
FromRepository: "foo",
ProjectID: proj.ID,
ProjectKey: proj.Key,
}
require.NoError(t, environment.InsertEnvironment(db, &env1))

var eenv = new(exportentities.Environment)

body := []byte(`
version: v1.0
name: ` + envName + `
`)
errenv := yaml.Unmarshal(body, eenv)
require.NoError(t, errenv)

_, _, _, globalError := environment.ParseAndImport(db, *proj, *eenv, environment.ImportOptions{Force: false}, nil, u)
require.Error(t, globalError)

_, _, _, globalError2 := environment.ParseAndImport(db, *proj, *eenv, environment.ImportOptions{Force: true, FromRepository: "bar"}, nil, u)
require.Error(t, globalError2)

_, _, _, globalError3 := environment.ParseAndImport(db, *proj, *eenv, environment.ImportOptions{Force: true}, nil, u)
require.NoError(t, globalError3)
}
4 changes: 3 additions & 1 deletion engine/api/pipeline/pipeline_importer.go
Expand Up @@ -26,7 +26,9 @@ 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 !opts.Force && oldPipeline.FromRepository != "" && pip.FromRepository != oldPipeline.FromRepository {
if opts.Force && opts.FromRepository == "" {
log.Debug("ImportUpdate>> Force import pipeline %s in project %s without fromRepository", pip.Name, proj.Key)
} else if 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

0 comments on commit 920bc09

Please sign in to comment.