From 3a231b90e640bc9e680e196d74426b1028da4528 Mon Sep 17 00:00:00 2001 From: Shawna Monero <66325812+smonero@users.noreply.github.com> Date: Fri, 5 Apr 2024 09:27:40 -0700 Subject: [PATCH 1/4] [DEVCON-6918] Helper funcs for getting the github repo (#743) --- cmd/adhoc.go | 8 ++- .../adhoc_execution_params.go | 25 ++++++++-- .../adhoc_github_helpers.go | 50 +++++++++++++++++++ server/neptune/adhoc/server.go | 16 ++++++ 4 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 server/neptune/adhoc/adhocgithubhelpers/adhoc_github_helpers.go diff --git a/cmd/adhoc.go b/cmd/adhoc.go index 4a90d8499..cd22bb6e7 100644 --- a/cmd/adhoc.go +++ b/cmd/adhoc.go @@ -66,11 +66,9 @@ func (a *Adhoc) NewServer(userConfig legacy.UserConfig, config legacy.Config) (S StatsNamespace: userConfig.StatsNamespace, Metrics: globalCfg.Metrics, AdhocExecutionParams: adhocHelpers.AdhocTerraformWorkflowExecutionParams{ - AtlantisRoot: globalCfg.AdhocMode.Root, - AtlantisRepo: globalCfg.AdhocMode.Repo, - Revision: "", - TerraformRoot: terraform.Root{}, - GithubRepo: github.Repo{}, + Revision: "", + TerraformRoots: []terraform.Root{}, + GithubRepo: github.Repo{}, }, GithubHostname: userConfig.GithubHostname, GithubAppID: userConfig.GithubAppID, diff --git a/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go b/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go index c21423ebf..bbdb19b1a 100644 --- a/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go +++ b/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go @@ -1,15 +1,30 @@ package adhoc import ( + "context" + + "github.com/pkg/errors" + "github.com/runatlantis/atlantis/server/neptune/adhoc/adhocgithubhelpers" "github.com/runatlantis/atlantis/server/neptune/workflows/activities/github" "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform" ) type AdhocTerraformWorkflowExecutionParams struct { - AtlantisRoot string - AtlantisRepo string - Revision string - TerraformRoot terraform.Root - GithubRepo github.Repo + Revision string + TerraformRoots []terraform.Root + GithubRepo github.Repo // Note that deploymentID is used in NewWorkflowStore(), but we don't care about that in adhoc mode so can leave it blank } + +func ConstructAdhocExecParamsWithRootCfgBuilderAndRepoRetriever(ctx context.Context, repoName string, revision string, githubRetriever *adhocgithubhelpers.AdhocGithubRetriever) (AdhocTerraformWorkflowExecutionParams, error) { + // TODO: in the future, could potentially pass in the owner instead of hardcoding lyft + repo, err := githubRetriever.GetRepository(ctx, "lyft", repoName) + if err != nil { + return AdhocTerraformWorkflowExecutionParams{}, errors.Wrap(err, "getting repo") + } + + return AdhocTerraformWorkflowExecutionParams{ + Revision: revision, + GithubRepo: repo, + }, nil +} diff --git a/server/neptune/adhoc/adhocgithubhelpers/adhoc_github_helpers.go b/server/neptune/adhoc/adhocgithubhelpers/adhoc_github_helpers.go new file mode 100644 index 000000000..fb0854c57 --- /dev/null +++ b/server/neptune/adhoc/adhocgithubhelpers/adhoc_github_helpers.go @@ -0,0 +1,50 @@ +package adhocgithubhelpers + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/runatlantis/atlantis/server/models" + "github.com/runatlantis/atlantis/server/neptune/workflows/activities/github" + internal "github.com/runatlantis/atlantis/server/vcs/provider/github" +) + +type repoRetriever interface { + Get(ctx context.Context, installationToken int64, owner, repo string) (models.Repo, error) +} + +type installationRetriever interface { + FindOrganizationInstallation(ctx context.Context, org string) (internal.Installation, error) +} + +type AdhocGithubRetriever struct { + RepoRetriever repoRetriever + InstallationRetriever installationRetriever +} + +func (r *AdhocGithubRetriever) GetRepository(ctx context.Context, owner string, repoName string) (github.Repo, error) { + installation, err := r.InstallationRetriever.FindOrganizationInstallation(ctx, owner) + if err != nil { + return github.Repo{}, errors.Wrap(err, "finding installation") + } + + repo, err := r.RepoRetriever.Get(ctx, installation.Token, owner, repoName) + if err != nil { + return github.Repo{}, errors.Wrap(err, "getting repo") + } + + if len(repo.DefaultBranch) == 0 { + return github.Repo{}, fmt.Errorf("default branch was nil, this is a bug on github's side") + } + + return github.Repo{ + Owner: repo.Owner, + Name: repo.Name, + URL: repo.CloneURL, + DefaultBranch: repo.DefaultBranch, + Credentials: github.AppCredentials{ + InstallationToken: installation.Token, + }, + }, nil +} diff --git a/server/neptune/adhoc/server.go b/server/neptune/adhoc/server.go index 7c78f31c5..ed3d21529 100644 --- a/server/neptune/adhoc/server.go +++ b/server/neptune/adhoc/server.go @@ -26,6 +26,7 @@ import ( "github.com/runatlantis/atlantis/server/logging" "github.com/runatlantis/atlantis/server/metrics" adhoc "github.com/runatlantis/atlantis/server/neptune/adhoc/adhocexecutionhelpers" + adhocGithubHelpers "github.com/runatlantis/atlantis/server/neptune/adhoc/adhocgithubhelpers" adhocconfig "github.com/runatlantis/atlantis/server/neptune/adhoc/config" root_config "github.com/runatlantis/atlantis/server/neptune/gateway/config" "github.com/runatlantis/atlantis/server/neptune/gateway/deploy" @@ -37,6 +38,7 @@ import ( "github.com/runatlantis/atlantis/server/neptune/workflows" "github.com/runatlantis/atlantis/server/neptune/workflows/activities" "github.com/runatlantis/atlantis/server/static" + github_converter "github.com/runatlantis/atlantis/server/vcs/provider/github/converter" "github.com/uber-go/tally/v4" "github.com/urfave/negroni" "go.temporal.io/sdk/interceptor" @@ -57,6 +59,7 @@ type Server struct { AdhocExecutionParams adhoc.AdhocTerraformWorkflowExecutionParams TerraformTaskQueue string RootConfigBuilder *root_config.Builder + GithubRetriever *adhocGithubHelpers.AdhocGithubRetriever } func NewServer(config *adhocconfig.Config) (*Server, error) { @@ -196,6 +199,18 @@ func NewServer(config *adhocconfig.Config) (*Server, error) { Scope: scope.SubScope("event.filters.root"), } + repoConverter := github_converter.RepoConverter{} + repoRetriever := &github.RepoRetriever{ + ClientCreator: clientCreator, + RepoConverter: repoConverter, + } + + // This exists to convert a repo name to a repo object + githubRetriever := &adhocGithubHelpers.AdhocGithubRetriever{ + RepoRetriever: repoRetriever, + InstallationRetriever: installationFetcher, + } + server := Server{ Logger: config.CtxLogger, CronScheduler: cronScheduler, @@ -215,6 +230,7 @@ func NewServer(config *adhocconfig.Config) (*Server, error) { GithubActivities: githubActivities, AdhocExecutionParams: config.AdhocExecutionParams, RootConfigBuilder: rootConfigBuilder, + GithubRetriever: githubRetriever, } return &server, nil } From bcf01ec20c2167d1ba115f12f5ea01dd992903dc Mon Sep 17 00:00:00 2001 From: Shawna Monero <66325812+smonero@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:19:16 -0700 Subject: [PATCH 2/4] use simple path for cloning in adhoc flow (#737) --- server/vcs/provider/github/repo_fetcher.go | 11 +++++ .../vcs/provider/github/repo_fetcher_test.go | 43 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/server/vcs/provider/github/repo_fetcher.go b/server/vcs/provider/github/repo_fetcher.go index 15a54e850..5b487ee65 100644 --- a/server/vcs/provider/github/repo_fetcher.go +++ b/server/vcs/provider/github/repo_fetcher.go @@ -37,6 +37,8 @@ type RepoFetcher struct { type RepoFetcherOptions struct { CloneDepth int + // Use simple path for adhoc mode, where there is only 1 repo so we can use a simpler path rather than one with UUID and repos prefix + SimplePath bool } func (g *RepoFetcher) Fetch(ctx context.Context, repo models.Repo, branch string, sha string, options RepoFetcherOptions) (string, func(ctx context.Context, filePath string), error) { @@ -71,6 +73,11 @@ func (g *RepoFetcher) Fetch(ctx context.Context, repo models.Repo, branch string func (g *RepoFetcher) clone(ctx context.Context, repo models.Repo, branch string, sha string, options RepoFetcherOptions) (string, func(ctx context.Context, filePath string), error) { destinationPath := g.generateDirPath(repo.Name) + // If simple path is enabled, we don't need a prefix and UUID + if options.SimplePath { + destinationPath = g.generateSimpleDirPath(repo.Name) + } + // Create the directory and parents if necessary. if err := os.MkdirAll(destinationPath, 0700); err != nil { return "", nil, errors.Wrap(err, "creating new directory") @@ -112,6 +119,10 @@ func (g *RepoFetcher) generateDirPath(repoName string) string { return filepath.Join(g.DataDir, workingDirPrefix, repoName, uuid.New().String()) } +func (g *RepoFetcher) generateSimpleDirPath(repoName string) string { + return filepath.Join(g.DataDir, repoName) +} + func (g *RepoFetcher) run(ctx context.Context, args []string, destinationPath string) ([]byte, error) { cmd := subprocess_exec.Command(g.Logger, args[0], args[1:]...) // nolint: gosec cmd.Dir = destinationPath diff --git a/server/vcs/provider/github/repo_fetcher_test.go b/server/vcs/provider/github/repo_fetcher_test.go index 9fd0fcf72..a93d36bc0 100644 --- a/server/vcs/provider/github/repo_fetcher_test.go +++ b/server/vcs/provider/github/repo_fetcher_test.go @@ -290,6 +290,48 @@ func TestFetch_ErrorGettingGHToken(t *testing.T) { assert.ErrorContains(t, err, "error") } +func TestDirPaths(t *testing.T) { + // Initialize the git repo. + repoDir, cleanupRepo := initRepo(t) + defer cleanupRepo() + sha1 := appendCommit(t, repoDir, ".gitkeep", "initial commit") + _ = runCmd(t, repoDir, "git", "rev-parse", "HEAD") + sha2 := appendCommit(t, repoDir, ".gitignore", "second commit") + expCommit2 := runCmd(t, repoDir, "git", "rev-parse", "HEAD") + + dataDir, cleanupDataDir := tempDir(t) + defer cleanupDataDir() + defer disableSSLVerification()() + testServer, err := fixtures.GithubAppTestServer(t) + assert.NoError(t, err) + logger := logging.NewNoopCtxLogger(t) + fetcher := &github.RepoFetcher{ + DataDir: dataDir, + GithubHostname: testServer, + Logger: logger, + GithubCredentials: &testTokenGetter{}, + Scope: tally.NewTestScope("test", map[string]string{}), + } + repo := newBaseRepo(repoDir) + optionsSimplePathTrue := github.RepoFetcherOptions{ + SimplePath: true, + } + destinationPath, _, err := fetcher.Fetch(context.Background(), repo, repo.DefaultBranch, sha2, optionsSimplePathTrue) + assert.NoError(t, err) + + // Use rev-parse to verify at correct commit. + actCommit := runCmd(t, destinationPath, "git", "rev-parse", "HEAD") + assert.Equal(t, expCommit2, actCommit) + + cpCmd := exec.Command("git", "checkout", sha1) + cpCmd.Dir = destinationPath + _, err = cpCmd.CombinedOutput() + assert.NoError(t, err) + + // make sure the simple file path is correct + assert.Equal(t, fmt.Sprintf("%s/%s", dataDir, "repo"), destinationPath) +} + func newBaseRepo(repoDir string) models.Repo { return models.Repo{ VCSHost: models.VCSHost{ @@ -298,6 +340,7 @@ func newBaseRepo(repoDir string) models.Repo { FullName: "nish/repo", DefaultBranch: "branch", CloneURL: fmt.Sprintf("file://%s", repoDir), + Name: "repo", } } From b511d420320b25817cd8939da4b40466bac13e0c Mon Sep 17 00:00:00 2001 From: Shawna Monero <66325812+smonero@users.noreply.github.com> Date: Tue, 9 Apr 2024 10:24:45 -0700 Subject: [PATCH 3/4] simple path for terraform fetch (#745) --- server/neptune/workflows/activities/github.go | 5 +++++ .../neptune/workflows/internal/terraform/root_fetcher.go | 1 + .../neptune/workflows/internal/terraform/workflow_test.go | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/server/neptune/workflows/activities/github.go b/server/neptune/workflows/activities/github.go index 792f2fc2f..66101ecf8 100644 --- a/server/neptune/workflows/activities/github.go +++ b/server/neptune/workflows/activities/github.go @@ -249,6 +249,7 @@ type FetchRootRequest struct { Root terraform.Root DeploymentID string Revision string + WorkflowMode terraform.WorkflowMode } type FetchRootResponse struct { @@ -265,6 +266,10 @@ func (a *githubActivities) GithubFetchRoot(ctx context.Context, request FetchRoo defer cancel() deployBasePath := filepath.Join(a.DataDir, deploymentsDirName, request.DeploymentID) + // if we are in Adhoc mode, we can use a simple file path without a UUID since there will only be one repository + if request.WorkflowMode == terraform.Adhoc { + deployBasePath = filepath.Join(a.DataDir) + } repositoryPath := filepath.Join(deployBasePath, "repo") opts := &github.RepositoryContentGetOptions{ Ref: request.Revision, diff --git a/server/neptune/workflows/internal/terraform/root_fetcher.go b/server/neptune/workflows/internal/terraform/root_fetcher.go index d714ab889..1026657f7 100644 --- a/server/neptune/workflows/internal/terraform/root_fetcher.go +++ b/server/neptune/workflows/internal/terraform/root_fetcher.go @@ -21,6 +21,7 @@ func (r *RootFetcher) Fetch(ctx workflow.Context) (*terraform.LocalRoot, func(wo Root: r.Request.Root, DeploymentID: r.Request.DeploymentID, Revision: r.Request.Revision, + WorkflowMode: r.Request.WorkflowMode, }).Get(ctx, &fetchRootResponse) if err != nil { diff --git a/server/neptune/workflows/internal/terraform/workflow_test.go b/server/neptune/workflows/internal/terraform/workflow_test.go index e1af5d7d5..450310cda 100644 --- a/server/neptune/workflows/internal/terraform/workflow_test.go +++ b/server/neptune/workflows/internal/terraform/workflow_test.go @@ -33,6 +33,7 @@ const ( testDeploymentID = "123" testPath = "rel/path" DeployDir = "deployments/123" + testWorkflowMode = terraformModel.Deploy ) var testGithubRepo = github.Repo{ @@ -287,6 +288,7 @@ func TestSuccess_DeployMode(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: testWorkflowMode, }).Return(activities.FetchRootResponse{ LocalRoot: testLocalRoot, DeployDirectory: DeployDir, @@ -495,6 +497,7 @@ func TestSuccess_PRMode(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: terraformModel.PR, }).Return(activities.FetchRootResponse{ LocalRoot: testLocalRoot, DeployDirectory: DeployDir, @@ -643,6 +646,7 @@ func TestSuccess_AdminMode(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: terraformModel.Adhoc, }).Return(activities.FetchRootResponse{ LocalRoot: testLocalRoot, DeployDirectory: DeployDir, @@ -725,6 +729,7 @@ func TestSuccess_PRMode_FailedPolicy(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: terraformModel.PR, }).Return(activities.FetchRootResponse{ LocalRoot: testLocalRoot, DeployDirectory: DeployDir, @@ -867,6 +872,7 @@ func TestUpdateJobError(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: testWorkflowMode, }).Return(activities.FetchRootResponse{ DeployDirectory: DeployDir, LocalRoot: testLocalRoot, @@ -913,6 +919,7 @@ func TestPlanRejection(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: testWorkflowMode, }).Return(activities.FetchRootResponse{ DeployDirectory: DeployDir, LocalRoot: testLocalRoot, @@ -1081,6 +1088,7 @@ func TestFetchRootError(t *testing.T) { Repo: testGithubRepo, Root: testLocalRoot.Root, DeploymentID: testDeploymentID, + WorkflowMode: testWorkflowMode, }).Return(activities.FetchRootResponse{ DeployDirectory: DeployDir, LocalRoot: testLocalRoot, From 723f4d63d42d55adfa74eaec0d8922af8ffe2d24 Mon Sep 17 00:00:00 2001 From: Shawna Monero <66325812+smonero@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:27:31 -0700 Subject: [PATCH 4/4] helper funcs for converting rootcfgs to terraform roots (#744) --- .../adhoc_execution_params.go | 24 ++- .../adhocexecutionhelpers/root_helpers.go | 99 +++++++++++ .../root_helpers_test.go | 162 ++++++++++++++++++ 3 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 server/neptune/adhoc/adhocexecutionhelpers/root_helpers.go create mode 100644 server/neptune/adhoc/adhocexecutionhelpers/root_helpers_test.go diff --git a/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go b/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go index bbdb19b1a..ba22b0afa 100644 --- a/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go +++ b/server/neptune/adhoc/adhocexecutionhelpers/adhoc_execution_params.go @@ -5,8 +5,11 @@ import ( "github.com/pkg/errors" "github.com/runatlantis/atlantis/server/neptune/adhoc/adhocgithubhelpers" + "github.com/runatlantis/atlantis/server/neptune/gateway/config" + root_config "github.com/runatlantis/atlantis/server/neptune/gateway/config" "github.com/runatlantis/atlantis/server/neptune/workflows/activities/github" "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform" + internal_gh "github.com/runatlantis/atlantis/server/vcs/provider/github" ) type AdhocTerraformWorkflowExecutionParams struct { @@ -16,15 +19,30 @@ type AdhocTerraformWorkflowExecutionParams struct { // Note that deploymentID is used in NewWorkflowStore(), but we don't care about that in adhoc mode so can leave it blank } -func ConstructAdhocExecParamsWithRootCfgBuilderAndRepoRetriever(ctx context.Context, repoName string, revision string, githubRetriever *adhocgithubhelpers.AdhocGithubRetriever) (AdhocTerraformWorkflowExecutionParams, error) { +func ConstructAdhocExecParamsWithRootCfgBuilderAndRepoRetriever(ctx context.Context, repoName string, revision string, githubRetriever *adhocgithubhelpers.AdhocGithubRetriever, rootCfgBuilder *root_config.Builder) (AdhocTerraformWorkflowExecutionParams, error) { // TODO: in the future, could potentially pass in the owner instead of hardcoding lyft repo, err := githubRetriever.GetRepository(ctx, "lyft", repoName) if err != nil { return AdhocTerraformWorkflowExecutionParams{}, errors.Wrap(err, "getting repo") } + opts := config.BuilderOptions{ + RepoFetcherOptions: &internal_gh.RepoFetcherOptions{ + CloneDepth: 1, + SimplePath: true, + }, + } + + rootCfgs, err := rootCfgBuilder.Build(ctx, &root_config.RepoCommit{}, repo.Credentials.InstallationToken, opts) + if err != nil { + return AdhocTerraformWorkflowExecutionParams{}, errors.Wrap(err, "building root cfgs") + } + + roots := getRootsFromMergedProjectCfgs(rootCfgs) + return AdhocTerraformWorkflowExecutionParams{ - Revision: revision, - GithubRepo: repo, + Revision: revision, + GithubRepo: repo, + TerraformRoots: roots, }, nil } diff --git a/server/neptune/adhoc/adhocexecutionhelpers/root_helpers.go b/server/neptune/adhoc/adhocexecutionhelpers/root_helpers.go new file mode 100644 index 000000000..8e38bf58a --- /dev/null +++ b/server/neptune/adhoc/adhocexecutionhelpers/root_helpers.go @@ -0,0 +1,99 @@ +package adhoc + +import ( + "github.com/runatlantis/atlantis/server/config/valid" + "github.com/runatlantis/atlantis/server/neptune/gateway/deploy" + "github.com/runatlantis/atlantis/server/neptune/workflows" + "github.com/runatlantis/atlantis/server/neptune/workflows/activities/execute" + "github.com/runatlantis/atlantis/server/neptune/workflows/activities/terraform" +) + +func getRootsFromMergedProjectCfgs(rootCfgs []*valid.MergedProjectCfg) []terraform.Root { + roots := make([]terraform.Root, 0, len(rootCfgs)) + for _, rootCfg := range rootCfgs { + root := convertMergedProjectCfgToRoot(rootCfg) + terraformRoot := convertToTerraformRoot(root) + roots = append(roots, terraformRoot) + } + return roots +} + +func convertMergedProjectCfgToRoot(rootCfg *valid.MergedProjectCfg) workflows.Root { + var tfVersion string + if rootCfg.TerraformVersion != nil { + tfVersion = rootCfg.TerraformVersion.String() + } + + return workflows.Root{ + Name: rootCfg.Name, + Plan: workflows.Job{ + Steps: prependPlanEnvSteps(rootCfg), + }, + Apply: workflows.Job{ + Steps: generateSteps(rootCfg.DeploymentWorkflow.Apply.Steps), + }, + RepoRelPath: rootCfg.RepoRelDir, + TrackedFiles: rootCfg.WhenModified, + TfVersion: tfVersion, + // note we don't set TriggerInfo or PlanMode + } +} + +func convertToTerraformRoot(root workflows.Root) terraform.Root { + return terraform.Root{ + Name: root.Name, + Apply: execute.Job{ + Steps: steps(root.Apply.Steps), + }, + Plan: terraform.PlanJob{ + Job: execute.Job{ + Steps: steps(root.Plan.Steps)}, + }, + // Note we don't have mode, nor PlanApproval + Path: root.RepoRelPath, + TfVersion: root.TfVersion, + } +} + +// These are copied here so that we don't have to use a workflowsignaler +func prependPlanEnvSteps(cfg *valid.MergedProjectCfg) []workflows.Step { + var steps []workflows.Step + if t, ok := cfg.Tags[deploy.Manifest]; ok { + //this is a Lyft specific env var + steps = append(steps, workflows.Step{ + StepName: deploy.EnvStep, + EnvVarName: "MANIFEST_FILEPATH", + EnvVarValue: t, + }) + } + steps = append(steps, generateSteps(cfg.DeploymentWorkflow.Plan.Steps)...) + return steps +} + +func generateSteps(steps []valid.Step) []workflows.Step { + var workflowSteps []workflows.Step + for _, step := range steps { + workflowSteps = append(workflowSteps, workflows.Step{ + StepName: step.StepName, + ExtraArgs: step.ExtraArgs, + RunCommand: step.RunCommand, + EnvVarName: step.EnvVarName, + EnvVarValue: step.EnvVarValue, + }) + } + return workflowSteps +} + +func steps(requestSteps []workflows.Step) []execute.Step { + var terraformSteps []execute.Step + for _, step := range requestSteps { + terraformSteps = append(terraformSteps, execute.Step{ + StepName: step.StepName, + ExtraArgs: step.ExtraArgs, + RunCommand: step.RunCommand, + EnvVarName: step.EnvVarName, + EnvVarValue: step.EnvVarValue, + }) + } + return terraformSteps +} diff --git a/server/neptune/adhoc/adhocexecutionhelpers/root_helpers_test.go b/server/neptune/adhoc/adhocexecutionhelpers/root_helpers_test.go new file mode 100644 index 000000000..f818ef172 --- /dev/null +++ b/server/neptune/adhoc/adhocexecutionhelpers/root_helpers_test.go @@ -0,0 +1,162 @@ +package adhoc + +import ( + "testing" + + "github.com/runatlantis/atlantis/server/config/valid" + v "github.com/runatlantis/atlantis/server/config/valid" + "github.com/runatlantis/atlantis/server/neptune/workflows" + "github.com/runatlantis/atlantis/server/neptune/workflows/activities/execute" + "github.com/stretchr/testify/assert" +) + +func TestPrependPlanEnvSteps(t *testing.T) { + tests := []struct { + cfg *valid.MergedProjectCfg + expectedSteps []workflows.Step + }{ + { + cfg: &valid.MergedProjectCfg{ + Tags: map[string]string{"manifest_path": "manifest_path"}, + DeploymentWorkflow: v.Workflow{ + Plan: v.Stage{ + Steps: []v.Step{ + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + }, + }, + }, + expectedSteps: []workflows.Step{ + { + StepName: "env", + EnvVarName: "MANIFEST_FILEPATH", + EnvVarValue: "manifest_path", + }, + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + }, + { + cfg: &valid.MergedProjectCfg{ + Tags: map[string]string{"foo": "foo"}, + DeploymentWorkflow: v.Workflow{ + Plan: v.Stage{ + Steps: []v.Step{ + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + }, + }, + }, + expectedSteps: []workflows.Step{ + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + }, + } + + for _, tt := range tests { + res := prependPlanEnvSteps(tt.cfg) + assert.True(t, compareSteps(res, tt.expectedSteps)) + } +} + +func compareSteps(a []workflows.Step, b []workflows.Step) bool { + if len(a) != len(b) { + return false + } + + for i, step := range a { + if step.StepName != b[i].StepName { + return false + } + if step.RunCommand != b[i].RunCommand { + return false + } + if step.EnvVarName != b[i].EnvVarName { + return false + } + if step.EnvVarValue != b[i].EnvVarValue { + return false + } + } + + return true +} + +func compareExecuteSteps(a []execute.Step, b []execute.Step) bool { + if len(a) != len(b) { + return false + } + + for i, step := range a { + if step.StepName != b[i].StepName { + return false + } + if step.RunCommand != b[i].RunCommand { + return false + } + if step.EnvVarName != b[i].EnvVarName { + return false + } + if step.EnvVarValue != b[i].EnvVarValue { + return false + } + } + + return true +} + +func TestSteps(t *testing.T) { + tests := []struct { + requestSteps []workflows.Step + expectedSteps []execute.Step + }{ + { + requestSteps: []workflows.Step{ + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + expectedSteps: []execute.Step{ + { + StepName: "step1", + ExtraArgs: []string{"arg1", "arg2"}, + RunCommand: "run", + EnvVarName: "env1", + EnvVarValue: "value1", + }, + }, + }, + } + + for _, tt := range tests { + res := steps(tt.requestSteps) + assert.True(t, compareExecuteSteps(res, tt.expectedSteps)) + } +}