diff --git a/server/events/apply_executor.go b/server/events/apply_executor.go index 6562d8ab8..dbacf35a6 100644 --- a/server/events/apply_executor.go +++ b/server/events/apply_executor.go @@ -16,10 +16,10 @@ package events import ( "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/run" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform" "github.com/runatlantis/atlantis/server/events/vcs" "github.com/runatlantis/atlantis/server/events/webhooks" - "github.com/runatlantis/atlantis/server/events/yaml" ) // ApplyExecutor handles executing terraform apply. @@ -31,7 +31,7 @@ type ApplyExecutor struct { AtlantisWorkspace AtlantisWorkspace ProjectLocker *DefaultProjectLocker Webhooks webhooks.Sender - ExecutionPlanner *yaml.ExecutionPlanner + ExecutionPlanner *runtime.ExecutionPlanner } // Execute executes apply for the ctx. diff --git a/server/events/plan_executor.go b/server/events/plan_executor.go index a620f2cb4..7e9291755 100644 --- a/server/events/plan_executor.go +++ b/server/events/plan_executor.go @@ -19,9 +19,9 @@ import ( "github.com/runatlantis/atlantis/server/events/locking" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/run" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform" "github.com/runatlantis/atlantis/server/events/vcs" - "github.com/runatlantis/atlantis/server/events/yaml" ) //go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_lock_url_generator.go LockURLGenerator @@ -39,7 +39,7 @@ type PlanExecutor struct { Workspace AtlantisWorkspace ProjectFinder ProjectFinder ProjectLocker ProjectLocker - ExecutionPlanner *yaml.ExecutionPlanner + ExecutionPlanner *runtime.ExecutionPlanner LockURLGenerator LockURLGenerator } diff --git a/server/events/project_locker_test.go b/server/events/project_locker_test.go index fd9550348..cd6c80d91 100644 --- a/server/events/project_locker_test.go +++ b/server/events/project_locker_test.go @@ -55,8 +55,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{}, errors.New("err")) +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{}, errors.New("err")) // // res := p.Execute(&ctx, "", project) // Equals(t, "err", res.ProjectResult.Error.Error()) @@ -68,8 +68,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{ +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{ // PreInit: []string{"pre-init"}, // }, nil) // tfVersion, _ := version.NewVersion("0.9.0") @@ -86,8 +86,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{}, nil) +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{}, nil) // tfVersion, _ := version.NewVersion("0.9.0") // When(tm.Version()).ThenReturn(tfVersion) // When(tm.Init(ctx.Log, "", "", nil, tfVersion)).ThenReturn(nil, errors.New("err")) @@ -102,8 +102,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{ +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{ // PreGet: []string{"pre-get"}, // }, nil) // tfVersion, _ := version.NewVersion("0.8") @@ -120,8 +120,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{}, nil) +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{}, nil) // tfVersion, _ := version.NewVersion("0.8") // When(tm.Version()).ThenReturn(tfVersion) // When(tm.RunCommandWithVersion(ctx.Log, "", []string{"get", "-no-color"}, tfVersion, "")).ThenReturn("", errors.New("err")) @@ -136,8 +136,8 @@ package events_test // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(locking.TryLockResponse{ // LockAcquired: true, // }, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) -// When(p.ConfigReader.Read("")).ThenReturn(events.ProjectConfig{ +// When(p.ParserValidator.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Read("")).ThenReturn(events.ProjectConfig{ // PrePlan: []string{"command"}, // }, nil) // tfVersion, _ := version.NewVersion("0.9") @@ -156,11 +156,11 @@ package events_test // LockAcquired: true, // } // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(lockResponse, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Exists("")).ThenReturn(true) // config := events.ProjectConfig{ // PreInit: []string{"pre-init"}, // } -// When(p.ConfigReader.Read("")).ThenReturn(config, nil) +// When(p.ParserValidator.Read("")).ThenReturn(config, nil) // tfVersion, _ := version.NewVersion("0.9") // When(tm.Version()).ThenReturn(tfVersion) // When(tm.Init(ctx.Log, "", "", nil, tfVersion)).ThenReturn(nil, nil) @@ -182,11 +182,11 @@ package events_test // LockAcquired: true, // } // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(lockResponse, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Exists("")).ThenReturn(true) // config := events.ProjectConfig{ // PreGet: []string{"pre-get"}, // } -// When(p.ConfigReader.Read("")).ThenReturn(config, nil) +// When(p.ParserValidator.Read("")).ThenReturn(config, nil) // tfVersion, _ := version.NewVersion("0.8") // When(tm.Version()).ThenReturn(tfVersion) // @@ -207,11 +207,11 @@ package events_test // LockAcquired: true, // } // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(lockResponse, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Exists("")).ThenReturn(true) // config := events.ProjectConfig{ // PrePlan: []string{"command"}, // } -// When(p.ConfigReader.Read("")).ThenReturn(config, nil) +// When(p.ParserValidator.Read("")).ThenReturn(config, nil) // tfVersion, _ := version.NewVersion("0.9") // When(tm.Version()).ThenReturn(tfVersion) // @@ -231,11 +231,11 @@ package events_test // LockAcquired: true, // } // When(l.TryLock(project, "", ctx.Pull, ctx.User)).ThenReturn(lockResponse, nil) -// When(p.ConfigReader.Exists("")).ThenReturn(true) +// When(p.ParserValidator.Exists("")).ThenReturn(true) // config := events.ProjectConfig{ // PreApply: []string{"command"}, // } -// When(p.ConfigReader.Read("")).ThenReturn(config, nil) +// When(p.ParserValidator.Read("")).ThenReturn(config, nil) // tfVersion, _ := version.NewVersion("0.9") // When(tm.Version()).ThenReturn(tfVersion) // @@ -262,7 +262,7 @@ package events_test // r := rmocks.NewMockRunner() // return &events.DefaultProjectLocker{ // Locker: l, -// ConfigReader: cr, +// ParserValidator: cr, // Terraform: tm, // Run: r, // }, l, tm, r diff --git a/server/events/yaml/apply_step.go b/server/events/runtime/apply_step.go similarity index 97% rename from server/events/yaml/apply_step.go rename to server/events/runtime/apply_step.go index 4b6a36061..359141481 100644 --- a/server/events/yaml/apply_step.go +++ b/server/events/runtime/apply_step.go @@ -1,4 +1,4 @@ -package yaml +package runtime import ( "fmt" diff --git a/server/events/yaml/apply_step_test.go b/server/events/runtime/apply_step_test.go similarity index 90% rename from server/events/yaml/apply_step_test.go rename to server/events/runtime/apply_step_test.go index f2066140e..862f5e10e 100644 --- a/server/events/yaml/apply_step_test.go +++ b/server/events/runtime/apply_step_test.go @@ -1,4 +1,4 @@ -package yaml_test +package runtime_test import ( "io/ioutil" @@ -9,14 +9,14 @@ import ( . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" - "github.com/runatlantis/atlantis/server/events/yaml" . "github.com/runatlantis/atlantis/testing" ) func TestRun_NoDir(t *testing.T) { - s := yaml.ApplyStep{ - Meta: yaml.StepMeta{ + s := runtime.ApplyStep{ + Meta: runtime.StepMeta{ Workspace: "workspace", AbsolutePath: "nonexistent/path", DirRelativeToRepoRoot: ".", @@ -33,8 +33,8 @@ func TestRun_NoPlanFile(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - s := yaml.ApplyStep{ - Meta: yaml.StepMeta{ + s := runtime.ApplyStep{ + Meta: runtime.StepMeta{ Workspace: "workspace", AbsolutePath: tmpDir, DirRelativeToRepoRoot: ".", @@ -58,8 +58,8 @@ func TestRun_Success(t *testing.T) { terraform := mocks.NewMockClient() tfVersion, _ := version.NewVersion("0.11.4") - s := yaml.ApplyStep{ - Meta: yaml.StepMeta{ + s := runtime.ApplyStep{ + Meta: runtime.StepMeta{ Workspace: "workspace", AbsolutePath: tmpDir, DirRelativeToRepoRoot: ".", diff --git a/server/events/yaml/execution_planner.go b/server/events/runtime/execution_planner.go similarity index 93% rename from server/events/yaml/execution_planner.go rename to server/events/runtime/execution_planner.go index c34903400..012d6b008 100644 --- a/server/events/yaml/execution_planner.go +++ b/server/events/runtime/execution_planner.go @@ -1,17 +1,23 @@ -package yaml +package runtime import ( "fmt" + "os" "path/filepath" "github.com/hashicorp/go-version" + "github.com/runatlantis/atlantis/server/events/yaml" "github.com/runatlantis/atlantis/server/logging" ) +const PlanStageName = "plan" +const ApplyStageName = "apply" +const AtlantisYAMLFilename = "atlantis.yaml" + type ExecutionPlanner struct { TerraformExecutor TerraformExec DefaultTFVersion *version.Version - ConfigReader *Reader + ParserValidator *yaml.ParserValidator } type TerraformExec interface { @@ -30,17 +36,18 @@ func (s *ExecutionPlanner) BuildPlanStage(log *logging.SimpleLogger, repoDir str } func (s *ExecutionPlanner) buildStage(stageName string, log *logging.SimpleLogger, repoDir string, workspace string, relProjectPath string, extraCommentArgs []string, username string, defaults []Step) ([]Step, error) { - config, err := s.ConfigReader.ReadConfig(repoDir) - if err != nil { - return nil, err - } + config, err := s.ParserValidator.ReadConfig(repoDir) // If there's no config file, use defaults. - if config == nil { + if os.IsNotExist(err) { log.Info("no %s file found––continuing with defaults", AtlantisYAMLFilename) return defaults, nil } + if err != nil { + return nil, err + } + // Get this project's configuration. for _, p := range config.Projects { if p.Dir == relProjectPath && p.Workspace == workspace { @@ -61,7 +68,7 @@ func (s *ExecutionPlanner) buildStage(stageName string, log *logging.SimpleLogge // We have a workflow defined, so now we need to build it. meta := s.buildMeta(log, repoDir, workspace, relProjectPath, extraCommentArgs, username) var steps []Step - var stepsConfig []StepConfig + var stepsConfig []yaml.StepConfig if stageName == PlanStageName { stepsConfig = workflow.Plan.Steps } else { diff --git a/server/events/yaml/init_step.go b/server/events/runtime/init_step.go similarity index 98% rename from server/events/yaml/init_step.go rename to server/events/runtime/init_step.go index 235b90244..1bf5b8734 100644 --- a/server/events/yaml/init_step.go +++ b/server/events/runtime/init_step.go @@ -1,4 +1,4 @@ -package yaml +package runtime // InitStep runs `terraform init`. type InitStep struct { diff --git a/server/events/yaml/init_step_test.go b/server/events/runtime/init_step_test.go similarity index 92% rename from server/events/yaml/init_step_test.go rename to server/events/runtime/init_step_test.go index bfdbd84b5..911bcb233 100644 --- a/server/events/yaml/init_step_test.go +++ b/server/events/runtime/init_step_test.go @@ -1,4 +1,4 @@ -package yaml_test +package runtime_test import ( "testing" @@ -7,8 +7,8 @@ import ( . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" - "github.com/runatlantis/atlantis/server/events/yaml" "github.com/runatlantis/atlantis/server/logging" . "github.com/runatlantis/atlantis/testing" ) @@ -43,8 +43,8 @@ func TestRun_UsesGetOrInitForRightVersion(t *testing.T) { tfVersion, _ := version.NewVersion(c.version) logger := logging.NewNoopLogger() - s := yaml.InitStep{ - Meta: yaml.StepMeta{ + s := runtime.InitStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: "workspace", AbsolutePath: "/path", diff --git a/server/events/yaml/plan_step.go b/server/events/runtime/plan_step.go similarity index 99% rename from server/events/yaml/plan_step.go rename to server/events/runtime/plan_step.go index 99890e62e..b8d6b3ace 100644 --- a/server/events/yaml/plan_step.go +++ b/server/events/runtime/plan_step.go @@ -1,4 +1,4 @@ -package yaml +package runtime import ( "fmt" diff --git a/server/events/yaml/plan_step_test.go b/server/events/runtime/plan_step_test.go similarity index 96% rename from server/events/yaml/plan_step_test.go rename to server/events/runtime/plan_step_test.go index 91e9372d3..53b45c2e6 100644 --- a/server/events/yaml/plan_step_test.go +++ b/server/events/runtime/plan_step_test.go @@ -1,4 +1,4 @@ -package yaml_test +package runtime_test import ( "errors" @@ -11,8 +11,8 @@ import ( . "github.com/petergtz/pegomock" "github.com/runatlantis/atlantis/server/events/mocks/matchers" matchers2 "github.com/runatlantis/atlantis/server/events/run/mocks/matchers" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform/mocks" - "github.com/runatlantis/atlantis/server/events/yaml" "github.com/runatlantis/atlantis/server/logging" . "github.com/runatlantis/atlantis/testing" ) @@ -25,8 +25,8 @@ func TestRun_NoWorkspaceIn08(t *testing.T) { tfVersion, _ := version.NewVersion("0.8") logger := logging.NewNoopLogger() workspace := "default" - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: workspace, AbsolutePath: "/path", @@ -61,8 +61,8 @@ func TestRun_ErrWorkspaceIn08(t *testing.T) { tfVersion, _ := version.NewVersion("0.8") logger := logging.NewNoopLogger() workspace := "notdefault" - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: workspace, AbsolutePath: "/path", @@ -112,8 +112,8 @@ func TestRun_SwitchesWorkspace(t *testing.T) { tfVersion, _ := version.NewVersion(c.tfVersion) logger := logging.NewNoopLogger() - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: "workspace", AbsolutePath: "/path", @@ -170,8 +170,8 @@ func TestRun_CreatesWorkspace(t *testing.T) { terraform := mocks.NewMockClient() tfVersion, _ := version.NewVersion(c.tfVersion) logger := logging.NewNoopLogger() - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: "workspace", AbsolutePath: "/path", @@ -212,8 +212,8 @@ func TestRun_NoWorkspaceSwitchIfNotNecessary(t *testing.T) { terraform := mocks.NewMockClient() tfVersion, _ := version.NewVersion("0.10.0") logger := logging.NewNoopLogger() - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: "workspace", AbsolutePath: "/path", @@ -258,8 +258,8 @@ func TestRun_AddsEnvVarFile(t *testing.T) { // Using version >= 0.10 here so we don't expect any env commands. tfVersion, _ := version.NewVersion("0.10.0") logger := logging.NewNoopLogger() - s := yaml.PlanStep{ - Meta: yaml.StepMeta{ + s := runtime.PlanStep{ + Meta: runtime.StepMeta{ Log: logger, Workspace: "workspace", AbsolutePath: tmpDir, diff --git a/server/events/yaml/repoconfig.go b/server/events/runtime/repoconfig.go similarity index 99% rename from server/events/yaml/repoconfig.go rename to server/events/runtime/repoconfig.go index 7af982436..138f73f0d 100644 --- a/server/events/yaml/repoconfig.go +++ b/server/events/runtime/repoconfig.go @@ -1,4 +1,4 @@ -package yaml +package runtime import ( "github.com/hashicorp/go-version" diff --git a/server/events/yaml/run_step.go b/server/events/runtime/run_step.go similarity index 97% rename from server/events/yaml/run_step.go rename to server/events/runtime/run_step.go index e4794a22c..ba6dbe385 100644 --- a/server/events/yaml/run_step.go +++ b/server/events/runtime/run_step.go @@ -1,4 +1,4 @@ -package yaml +package runtime import ( "fmt" diff --git a/server/events/runtime/run_step_test.go b/server/events/runtime/run_step_test.go new file mode 100644 index 000000000..899327a69 --- /dev/null +++ b/server/events/runtime/run_step_test.go @@ -0,0 +1,3 @@ +package runtime_test + +// todo diff --git a/server/events/runtime/runtime.go b/server/events/runtime/runtime.go new file mode 100644 index 000000000..ea70550a6 --- /dev/null +++ b/server/events/runtime/runtime.go @@ -0,0 +1,4 @@ +// Package runtime handles constructing an execution graph for each action +// based on configuration and defaults. The handlers can then execute this +// graph. +package runtime diff --git a/server/events/yaml/reader.go b/server/events/yaml/parser_validator.go similarity index 59% rename from server/events/yaml/reader.go rename to server/events/yaml/parser_validator.go index eaea83539..861201504 100644 --- a/server/events/yaml/reader.go +++ b/server/events/yaml/parser_validator.go @@ -5,49 +5,46 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) +// AtlantisYAMLFilename is the name of the config file for each repo. const AtlantisYAMLFilename = "atlantis.yaml" -const PlanStageName = "plan" -const ApplyStageName = "apply" -type Reader struct{} +type ParserValidator struct{} -// ReadConfig returns the parsed and validated config for repoDir. -// If there was no config, it returns a nil pointer. -func (r *Reader) ReadConfig(repoDir string) (*Config, error) { +// ReadConfig returns the parsed and validated atlantis.yaml config for repoDir. +// If there was no config file, then this can be detected by checking the type +// of error: os.IsNotExist(error). +func (r *ParserValidator) ReadConfig(repoDir string) (Config, error) { configFile := filepath.Join(repoDir, AtlantisYAMLFilename) configData, err := ioutil.ReadFile(configFile) - // If the file doesn't exist return nil. + // NOTE: the error we return here must also be os.IsNotExist since that's + // what our callers use to detect a missing config file. if err != nil && os.IsNotExist(err) { - return nil, nil + return Config{}, err } // If it exists but we couldn't read it return an error. if err != nil { - return nil, errors.Wrapf(err, "unable to read %s file", AtlantisYAMLFilename) + return Config{}, errors.Wrapf(err, "unable to read %s file", AtlantisYAMLFilename) } // If the config file exists, parse it. config, err := r.parseAndValidate(configData) if err != nil { - return nil, errors.Wrapf(err, "parsing %s", AtlantisYAMLFilename) + return Config{}, errors.Wrapf(err, "parsing %s", AtlantisYAMLFilename) } - return &config, err + return config, err } -func (r *Reader) parseAndValidate(configData []byte) (Config, error) { +func (r *ParserValidator) parseAndValidate(configData []byte) (Config, error) { var repoConfig Config if err := yaml.UnmarshalStrict(configData, &repoConfig); err != nil { - // Unmarshal error messages aren't fit for user output. We need to - // massage them. - // todo: fix "field autoplan not found in struct yaml.alias" errors - return repoConfig, errors.New(strings.Replace(err.Error(), " into yaml.Config", "", -1)) + return repoConfig, err } // Validate version. diff --git a/server/events/yaml/reader_test.go b/server/events/yaml/parser_validator_test.go similarity index 71% rename from server/events/yaml/reader_test.go rename to server/events/yaml/parser_validator_test.go index 7dc4b25b4..4adc8ca34 100644 --- a/server/events/yaml/reader_test.go +++ b/server/events/yaml/parser_validator_test.go @@ -2,6 +2,7 @@ package yaml_test import ( "io/ioutil" + "os" "path/filepath" "testing" @@ -10,20 +11,18 @@ import ( ) func TestReadConfig_DirDoesNotExist(t *testing.T) { - r := yaml.Reader{} - conf, err := r.ReadConfig("/not/exist") - Ok(t, err) - Assert(t, conf == nil, "exp nil ptr") + r := yaml.ParserValidator{} + _, err := r.ReadConfig("/not/exist") + Assert(t, os.IsNotExist(err), "exp nil ptr") } func TestReadConfig_FileDoesNotExist(t *testing.T) { tmpDir, cleanup := TempDir(t) defer cleanup() - r := yaml.Reader{} - conf, err := r.ReadConfig(tmpDir) - Ok(t, err) - Assert(t, conf == nil, "exp nil ptr") + r := yaml.ParserValidator{} + _, err := r.ReadConfig(tmpDir) + Assert(t, os.IsNotExist(err), "exp nil ptr") } func TestReadConfig_BadPermissions(t *testing.T) { @@ -32,7 +31,7 @@ func TestReadConfig_BadPermissions(t *testing.T) { err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), nil, 0000) Ok(t, err) - r := yaml.Reader{} + r := yaml.ParserValidator{} _, err = r.ReadConfig(tmpDir) ErrContains(t, "unable to read atlantis.yaml file: ", err) } @@ -48,7 +47,7 @@ func TestReadConfig_UnmarshalErrors(t *testing.T) { { "random characters", "slkjds", - "parsing atlantis.yaml: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `slkjds`", + "parsing atlantis.yaml: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `slkjds` into yaml.Config", }, { "just a colon", @@ -64,7 +63,7 @@ func TestReadConfig_UnmarshalErrors(t *testing.T) { t.Run(c.description, func(t *testing.T) { err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0600) Ok(t, err) - r := yaml.Reader{} + r := yaml.ParserValidator{} _, err = r.ReadConfig(tmpDir) ErrEquals(t, c.expErr, err) }) @@ -161,175 +160,6 @@ projects: - unknown: value`, expErr: "yaml: unmarshal errors:\n line 4: field unknown not found in struct yaml.alias", }, - - // project workflow doesn't exist - // workflow has plan and apply keys (otherwise no point specifying it) - // plan/apply stages must have non-empty steps key - - // Test the steps key. - { - description: "unsupported step type", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - unsupported`, - expErr: "unsupported step type: \"unsupported\"", - }, - - // Init step. - { - description: "unsupported arg to init step", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - init: - extra_args: ["hi"] - hi: bye -`, - expErr: "unsupported key \"hi\" for step init – the only supported key is extra_args", - }, - { - description: "invalid value type to init step's extra_args", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - init: - extra_args: arg -`, - expErr: "expected array of strings as value of extra_args, not \"arg\"", - }, - - // Plan step. - { - description: "unsupported arg to plan step", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - plan: - extra_args: ["hi"] - hi: bye -`, - expErr: "unsupported key \"hi\" for step plan – the only supported key is extra_args", - }, - { - description: "invalid value type to plan step's extra_args", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - plan: - extra_args: arg -`, - expErr: "expected array of strings as value of extra_args, not \"arg\"", - }, - - // Apply step. - { - description: "unsupported arg to apply step", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - apply: - extra_args: ["hi"] - hi: bye -`, - expErr: "unsupported key \"hi\" for step apply – the only supported key is extra_args", - }, - { - description: "invalid value type to apply step's extra_args", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - apply: - extra_args: arg -`, - expErr: "expected array of strings as value of extra_args, not \"arg\"", - }, - { - description: "invalid step type", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - steps: - - rn: echo should fail -`, - expErr: "yaml: unmarshal errors:\n line 9: field rn not found in struct struct { Run string \"yaml:\\\"run\\\"\" }", - }, - { - description: "missed the steps key and just set an array directly", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: - - init -`, - expErr: "missing \"steps\" key", - }, - { - description: "no value after plan:", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: -`, - expErr: "missing \"steps\" key", - }, - { - description: "no value after apply:", - input: ` -version: 2 -projects: -- dir: "." -workflows: - default: - plan: -`, - expErr: "missing \"steps\" key", - }, } tmpDir, cleanup := TempDir(t) @@ -340,7 +170,7 @@ workflows: err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0600) Ok(t, err) - r := yaml.Reader{} + r := yaml.ParserValidator{} _, err = r.ReadConfig(tmpDir) ErrEquals(t, "parsing atlantis.yaml: "+c.expErr, err) }) @@ -595,7 +425,7 @@ workflows: Steps: []yaml.StepConfig{ { StepType: "init", - ExtraArgs: nil, + ExtraArgs: []string{}, }, { StepType: "plan", @@ -642,18 +472,16 @@ workflows: Plan: &yaml.Stage{ Steps: []yaml.StepConfig{ { - StepType: "run", - ExtraArgs: nil, - Run: []string{"echo", "plan hi"}, + StepType: "run", + Run: []string{"echo", "plan hi"}, }, }, }, Apply: &yaml.Stage{ Steps: []yaml.StepConfig{ { - StepType: "run", - ExtraArgs: nil, - Run: []string{"echo", "apply", "arg 2"}, + StepType: "run", + Run: []string{"echo", "apply", "arg 2"}, }, }, }, @@ -671,10 +499,10 @@ workflows: err := ioutil.WriteFile(filepath.Join(tmpDir, "atlantis.yaml"), []byte(c.input), 0600) Ok(t, err) - r := yaml.Reader{} + r := yaml.ParserValidator{} act, err := r.ReadConfig(tmpDir) Ok(t, err) - Equals(t, &c.expOutput, act) + Equals(t, c.expOutput, act) }) } } diff --git a/server/events/yaml/project.go b/server/events/yaml/project.go index b2c136e59..dade99bae 100644 --- a/server/events/yaml/project.go +++ b/server/events/yaml/project.go @@ -9,12 +9,14 @@ type Project struct { ApplyRequirements []string `yaml:"apply_requirements"` } +const DefaultWorkspace = "default" + func (p *Project) UnmarshalYAML(unmarshal func(interface{}) error) error { // Use a type alias so unmarshal doesn't get into an infinite loop. type alias Project // Set up defaults. defaults := alias{ - Workspace: defaultWorkspace, + Workspace: DefaultWorkspace, AutoPlan: &AutoPlan{ Enabled: true, WhenModified: []string{"**/*.tf"}, diff --git a/server/events/yaml/run_step_test.go b/server/events/yaml/run_step_test.go deleted file mode 100644 index d00e3ec78..000000000 --- a/server/events/yaml/run_step_test.go +++ /dev/null @@ -1,3 +0,0 @@ -package yaml - -// todo diff --git a/server/events/yaml/yaml.go b/server/events/yaml/yaml.go new file mode 100644 index 000000000..08b884cae --- /dev/null +++ b/server/events/yaml/yaml.go @@ -0,0 +1,4 @@ +// Package yaml contains the golang representations of the YAML elements +// supported in atlantis.yaml. Many of the elements implement UnmarshalYAML +// in order to set defaults. +package yaml diff --git a/server/server.go b/server/server.go index 9381d7469..8ae0e5b6e 100644 --- a/server/server.go +++ b/server/server.go @@ -37,6 +37,7 @@ import ( "github.com/runatlantis/atlantis/server/events/locking/boltdb" "github.com/runatlantis/atlantis/server/events/models" "github.com/runatlantis/atlantis/server/events/run" + "github.com/runatlantis/atlantis/server/events/runtime" "github.com/runatlantis/atlantis/server/events/terraform" "github.com/runatlantis/atlantis/server/events/vcs" "github.com/runatlantis/atlantis/server/events/webhooks" @@ -200,8 +201,8 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) { ConfigReader: configReader, Terraform: terraformClient, } - executionPlanner := &yaml.ExecutionPlanner{ - ConfigReader: &yaml.Reader{}, + executionPlanner := &runtime.ExecutionPlanner{ + ParserValidator: &yaml.ParserValidator{}, DefaultTFVersion: terraformClient.Version(), TerraformExecutor: terraformClient, }