Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

helper funcs for converting rootcfgs to terraform roots #744

Merged
merged 12 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
smonero marked this conversation as resolved.
Show resolved Hide resolved
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
}
99 changes: 99 additions & 0 deletions server/neptune/adhoc/adhocexecutionhelpers/root_helpers.go
Original file line number Diff line number Diff line change
@@ -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
}
162 changes: 162 additions & 0 deletions server/neptune/adhoc/adhocexecutionhelpers/root_helpers_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
}
Loading