Skip to content

Commit

Permalink
Merge branch 'main' of github.com:lyft/atlantis
Browse files Browse the repository at this point in the history
  • Loading branch information
smonero committed Apr 10, 2024
2 parents 61c1029 + 723f4d6 commit 357bae4
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 16 deletions.
8 changes: 3 additions & 5 deletions cmd/adhoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions server/config/raw/global_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ type GlobalCfg struct {
}

type AdhocMode struct {
Repo string `yaml:"repo" json:"repo"`
Root string `yaml:"root" json:"root"`
Repo string `yaml:"repo" json:"repo"`
Root string `yaml:"root" json:"root"`
Revision string `yaml:"revision" json:"revision"`
}

func (t AdhocMode) ToValid() valid.AdhocMode {
return valid.AdhocMode{
Repo: t.Repo,
Root: t.Root,
Repo: t.Repo,
Root: t.Root,
Revision: t.Revision,
}
}

Expand Down
5 changes: 3 additions & 2 deletions server/config/valid/global_cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ type GlobalCfg struct {
}

type AdhocMode struct {
Repo string
Root string
Repo string
Root string
Revision string
}

type GithubTeam struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
package adhoc

import (
"context"

"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 {
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, 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,
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

0 comments on commit 357bae4

Please sign in to comment.