From a55f7480de5e782185a6110dbeb0bd598f6d1a24 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 31 Mar 2021 21:30:40 -0400 Subject: [PATCH 01/12] apply: to check module requirements before run modules must now provide a `make check` in their root makefile make check must exit 0 on all modules before proceeding to apply this allows checks for module specific binaries or check tokens permissions --- internal/apply/apply.go | 49 +++++++++++++++++++ internal/apply/apply_test.go | 30 ++++++++---- internal/util/util.go | 4 +- .../test_data/apply-failing/project1/Makefile | 7 +++ .../apply-failing/project1/zero-module.yml | 19 +++++++ .../test_data/apply-failing/project2/Makefile | 7 +++ .../apply-failing/project2/zero-module.yml | 19 +++++++ .../test_data/apply-failing/zero-project.yml | 17 +++++++ tests/test_data/apply/project1/Makefile | 2 + tests/test_data/apply/project2/Makefile | 2 + 10 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 tests/test_data/apply-failing/project1/Makefile create mode 100644 tests/test_data/apply-failing/project1/zero-module.yml create mode 100644 tests/test_data/apply-failing/project2/Makefile create mode 100644 tests/test_data/apply-failing/project2/zero-module.yml create mode 100644 tests/test_data/apply-failing/zero-project.yml diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 782153369..2c76558bc 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -33,6 +33,9 @@ Only a single environment may be suitable for an initial test, but for a real sy environments = promptEnvironments() } + flog.Infof(":tada: checking project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) + checkAll(rootDir, *projectConfig, environments) + flog.Infof(":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) flog.Infof("Cloud provider: %s", "AWS") // will this come from the config? @@ -169,3 +172,49 @@ func summarizeAll(dir string, projectConfig projectconfig.ZeroProjectConfig, app flog.Infof("Happy coding! :smile:") } + +func checkAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEnvironments []string) { + flog.Infof("Checking module requirements.") + + graph := projectConfig.GetDAG() + + // Walk the graph of modules and run `make summary` + root := []dag.Vertex{projectconfig.GraphRootName} + graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { + // Don't process the root + if depth == 0 { + return nil + } + + name := v.(string) + mod := projectConfig.Modules[name] + // Add env vars for the makefile + envList := []string{ + fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")), + fmt.Sprintf("REPOSITORY=%s", mod.Files.Repository), + fmt.Sprintf("PROJECT_NAME=%s", projectConfig.Name), + } + + modulePath := module.GetSourceDir(mod.Files.Source) + // Passed in `dir` will only be used to find the project path, not the module path, + // unless the module path is relative + if module.IsLocal(mod.Files.Source) && !filepath.IsAbs(modulePath) { + modulePath = filepath.Join(dir, modulePath) + } + flog.Debugf("Loaded module: %s from %s", name, modulePath) + + modConfig, err := module.ParseModuleConfig(modulePath) + if err != nil { + exit.Fatal("Failed to load module config, credentials cannot be injected properly") + } + + envVarTranslationMap := modConfig.GetParamEnvVarTranslationMap() + envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList, envVarTranslationMap) + flog.Debugf("Env injected: %#v", envList) + + util.ExecuteCommand(exec.Command("make", "check"), modulePath, envList) + return nil + }) + + flog.Infof("Happy coding! :smile:") +} diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index a4c1b41e4..68174aec6 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -13,19 +13,12 @@ import ( ) func TestApply(t *testing.T) { - dir := "../../tests/test_data/apply/" applyConfigPath := constants.ZeroProjectYml applyEnvironments := []string{"staging", "production"} - - tmpDir := filepath.Join(os.TempDir(), "apply") - - err := os.RemoveAll(tmpDir) - assert.NoError(t, err) - - err = shutil.CopyTree(dir, tmpDir, nil) - assert.NoError(t, err) + var tmpDir string t.Run("Should run apply and execute make on each folder module", func(t *testing.T) { + tmpDir = setupTmpDir(t, "../../tests/test_data/apply/") apply.Apply(tmpDir, applyConfigPath, applyEnvironments) assert.FileExists(t, filepath.Join(tmpDir, "project1/project.out")) assert.FileExists(t, filepath.Join(tmpDir, "project2/project.out")) @@ -45,4 +38,23 @@ func TestApply(t *testing.T) { assert.Equal(t, "envVarName of viaEnvVarName: baz\n", string(content)) }) + t.Run("Moudles with failing checks should return error", func(t *testing.T) { + tmpDir = setupTmpDir(t, "../../tests/test_data/apply-failing/") + + assert.Panics(t, func() { + apply.Apply(tmpDir, applyConfigPath, applyEnvironments) + }) + }) +} + +func setupTmpDir(t *testing.T, exampleDirPath string) string { + var err error + tmpDir := filepath.Join(os.TempDir(), "apply") + + err = os.RemoveAll(tmpDir) + assert.NoError(t, err) + + err = shutil.CopyTree(exampleDirPath, tmpDir, nil) + assert.NoError(t, err) + return tmpDir } diff --git a/internal/util/util.go b/internal/util/util.go index 8dc5826c4..dfafefe8d 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -68,7 +68,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { err := cmd.Start() if err != nil { - log.Fatalf("Starting command failed: %v\n", err) + panic(fmt.Sprintf("Starting command failed: %v\n", err)) } go func() { @@ -80,7 +80,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { err = cmd.Wait() if err != nil { - log.Fatalf("Executing command failed: %v\n", err) + panic(fmt.Sprintf("Executing command failed: %v\n", err)) } if errStdout != nil { diff --git a/tests/test_data/apply-failing/project1/Makefile b/tests/test_data/apply-failing/project1/Makefile new file mode 100644 index 000000000..882a1e8e8 --- /dev/null +++ b/tests/test_data/apply-failing/project1/Makefile @@ -0,0 +1,7 @@ +current_dir: + @echo "foo: ${foo}" > project.out + @echo "repo: ${REPOSITORY}" >> project.out + +summary: + +check: diff --git a/tests/test_data/apply-failing/project1/zero-module.yml b/tests/test_data/apply-failing/project1/zero-module.yml new file mode 100644 index 000000000..5d6914f9b --- /dev/null +++ b/tests/test_data/apply-failing/project1/zero-module.yml @@ -0,0 +1,19 @@ +name: project1 +description: 'project1' +author: 'Commit' + +template: + strictMode: true + delimiters: + - "<%" + - "%>" + inputDir: '.' + outputDir: 'test' + +requiredCredentials: + - aws + - github + +parameters: + - field: foo + label: foo diff --git a/tests/test_data/apply-failing/project2/Makefile b/tests/test_data/apply-failing/project2/Makefile new file mode 100644 index 000000000..348f9867a --- /dev/null +++ b/tests/test_data/apply-failing/project2/Makefile @@ -0,0 +1,7 @@ +current_dir: + @echo "baz: ${baz}" > project.out + +summary: + +check: + exit 1 diff --git a/tests/test_data/apply-failing/project2/zero-module.yml b/tests/test_data/apply-failing/project2/zero-module.yml new file mode 100644 index 000000000..c50d5df76 --- /dev/null +++ b/tests/test_data/apply-failing/project2/zero-module.yml @@ -0,0 +1,19 @@ +name: project2 +description: 'project2' +author: 'Commit' + +template: + strictMode: true + delimiters: + - "<%" + - "%>" + inputDir: '.' + outputDir: 'test' + +requiredCredentials: + - aws + - github + +parameters: + - field: baz + label: baz diff --git a/tests/test_data/apply-failing/zero-project.yml b/tests/test_data/apply-failing/zero-project.yml new file mode 100644 index 000000000..09c239cfc --- /dev/null +++ b/tests/test_data/apply-failing/zero-project.yml @@ -0,0 +1,17 @@ +name: sample_project + +modules: + project1: + parameters: + foo: bar + files: + dir: project1 + repo: github.com/commitdev/project1 + source: project1 + project2: + parameters: + baz: qux + files: + dir: project2 + repo: github.com/commitdev/project2 + source: project2 diff --git a/tests/test_data/apply/project1/Makefile b/tests/test_data/apply/project1/Makefile index 78158f243..d7b3251d8 100644 --- a/tests/test_data/apply/project1/Makefile +++ b/tests/test_data/apply/project1/Makefile @@ -4,3 +4,5 @@ current_dir: @echo "envVarName of viaEnvVarName: ${viaEnvVarName}" >> feature.out summary: + +check: diff --git a/tests/test_data/apply/project2/Makefile b/tests/test_data/apply/project2/Makefile index 41f42f07b..d824e2d06 100644 --- a/tests/test_data/apply/project2/Makefile +++ b/tests/test_data/apply/project2/Makefile @@ -2,3 +2,5 @@ current_dir: @echo "baz: ${baz}" > project.out summary: + +check: From 6dac07051935a12d3d6151c327f74d3845625d64 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Thu, 1 Apr 2021 14:39:17 -0400 Subject: [PATCH 02/12] combine the module walk methods and return err --- cmd/apply.go | 5 +- internal/apply/apply.go | 135 +++++------------- internal/apply/apply_test.go | 7 +- internal/util/util.go | 7 +- .../test_data/apply-failing/project2/Makefile | 3 +- 5 files changed, 46 insertions(+), 111 deletions(-) diff --git a/cmd/apply.go b/cmd/apply.go index 32d86ec48..705eff747 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -29,6 +29,9 @@ var applyCmd = &cobra.Command{ log.Println(err) rootDir = projectconfig.RootDir } - apply.Apply(rootDir, applyConfigPath, applyEnvironments) + applyErr := apply.Apply(rootDir, applyConfigPath, applyEnvironments) + if applyErr != nil { + log.Fatal(applyErr) + } }, } diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 2c76558bc..0e0c21630 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -1,6 +1,7 @@ package apply import ( + "errors" "fmt" "path/filepath" @@ -19,7 +20,8 @@ import ( "github.com/manifoldco/promptui" ) -func Apply(rootDir string, configPath string, environments []string) { +func Apply(rootDir string, configPath string, environments []string) error { + var err error if strings.Trim(configPath, " ") == "" { exit.Fatal("config path cannot be empty!") } @@ -34,7 +36,11 @@ Only a single environment may be suitable for an initial test, but for a real sy } flog.Infof(":tada: checking project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) - checkAll(rootDir, *projectConfig, environments) + + err = modulesWalkCmd("check", rootDir, projectConfig, []string{"make", "check"}, environments) + if err != nil { + return errors.New(fmt.Sprintf("Module checks failed: %s", err.Error())) + } flog.Infof(":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) @@ -44,21 +50,26 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof("Infrastructure executor: %s", "Terraform") - applyAll(rootDir, *projectConfig, environments) + err = modulesWalkCmd("apply", rootDir, projectConfig, []string{"make"}, environments) + if err != nil { + return errors.New(fmt.Sprintf("Module Apply failed: %s", err.Error())) + } flog.Infof(":check_mark_button: Done.") - summarizeAll(rootDir, *projectConfig, environments) + flog.Infof("Your projects and infrastructure have been successfully created. Here are some useful links and commands to get you started:") + err = modulesWalkCmd("summary", rootDir, projectConfig, []string{"make", "summary"}, environments) + if err != nil { + return errors.New(fmt.Sprintf("Module summary failed: %s", err.Error())) + } + return err } -func applyAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEnvironments []string) { - environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")) - +func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, cmdArgs []string, applyEnvironments []string) error { graph := projectConfig.GetDAG() - - // Walk the graph of modules and run `make` root := []dag.Vertex{projectconfig.GraphRootName} - graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { + environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")) + err := graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { // Don't process the root if depth == 0 { return nil @@ -92,10 +103,20 @@ func applyAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEn envVarTranslationMap := modConfig.GetParamEnvVarTranslationMap() envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList, envVarTranslationMap) flog.Debugf("Env injected: %#v", envList) - flog.Infof("Executing apply command for %s...", modConfig.Name) - util.ExecuteCommand(exec.Command("make"), modulePath, envList) + + // only print msg for apply, or else it gets a little spammy + if lifecycleName == "apply" { + flog.Infof("Executing %s command for %s...", lifecycleName, modConfig.Name) + } + + execErr := util.ExecuteCommand(exec.Command(cmdArgs[0], cmdArgs[1:]...), modulePath, envList) + if execErr != nil { + return errors.New(fmt.Sprintf("Module (%s) %s", modConfig.Name, execErr.Error())) + } return nil }) + + return err } // promptEnvironments Prompts the user for the environments to apply against and returns a slice of strings representing the environments @@ -128,93 +149,3 @@ func validateEnvironments(applyEnvironments []string) { } } } - -func summarizeAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEnvironments []string) { - flog.Infof("Your projects and infrastructure have been successfully created. Here are some useful links and commands to get you started:") - - graph := projectConfig.GetDAG() - - // Walk the graph of modules and run `make summary` - root := []dag.Vertex{projectconfig.GraphRootName} - graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { - // Don't process the root - if depth == 0 { - return nil - } - - name := v.(string) - mod := projectConfig.Modules[name] - // Add env vars for the makefile - envList := []string{ - fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")), - fmt.Sprintf("REPOSITORY=%s", mod.Files.Repository), - fmt.Sprintf("PROJECT_NAME=%s", projectConfig.Name), - } - - modulePath := module.GetSourceDir(mod.Files.Source) - // Passed in `dir` will only be used to find the project path, not the module path, - // unless the module path is relative - if module.IsLocal(mod.Files.Source) && !filepath.IsAbs(modulePath) { - modulePath = filepath.Join(dir, modulePath) - } - flog.Debugf("Loaded module: %s from %s", name, modulePath) - - modConfig, err := module.ParseModuleConfig(modulePath) - if err != nil { - exit.Fatal("Failed to load module config, credentials cannot be injected properly") - } - envVarTranslationMap := modConfig.GetParamEnvVarTranslationMap() - envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList, envVarTranslationMap) - flog.Debugf("Env injected: %#v", envList) - util.ExecuteCommand(exec.Command("make", "summary"), modulePath, envList) - return nil - }) - - flog.Infof("Happy coding! :smile:") -} - -func checkAll(dir string, projectConfig projectconfig.ZeroProjectConfig, applyEnvironments []string) { - flog.Infof("Checking module requirements.") - - graph := projectConfig.GetDAG() - - // Walk the graph of modules and run `make summary` - root := []dag.Vertex{projectconfig.GraphRootName} - graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { - // Don't process the root - if depth == 0 { - return nil - } - - name := v.(string) - mod := projectConfig.Modules[name] - // Add env vars for the makefile - envList := []string{ - fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")), - fmt.Sprintf("REPOSITORY=%s", mod.Files.Repository), - fmt.Sprintf("PROJECT_NAME=%s", projectConfig.Name), - } - - modulePath := module.GetSourceDir(mod.Files.Source) - // Passed in `dir` will only be used to find the project path, not the module path, - // unless the module path is relative - if module.IsLocal(mod.Files.Source) && !filepath.IsAbs(modulePath) { - modulePath = filepath.Join(dir, modulePath) - } - flog.Debugf("Loaded module: %s from %s", name, modulePath) - - modConfig, err := module.ParseModuleConfig(modulePath) - if err != nil { - exit.Fatal("Failed to load module config, credentials cannot be injected properly") - } - - envVarTranslationMap := modConfig.GetParamEnvVarTranslationMap() - envList = util.AppendProjectEnvToCmdEnv(mod.Parameters, envList, envVarTranslationMap) - flog.Debugf("Env injected: %#v", envList) - - util.ExecuteCommand(exec.Command("make", "check"), modulePath, envList) - return nil - }) - - flog.Infof("Happy coding! :smile:") -} diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index 68174aec6..6f1775ce2 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -19,7 +19,7 @@ func TestApply(t *testing.T) { t.Run("Should run apply and execute make on each folder module", func(t *testing.T) { tmpDir = setupTmpDir(t, "../../tests/test_data/apply/") - apply.Apply(tmpDir, applyConfigPath, applyEnvironments) + err := apply.Apply(tmpDir, applyConfigPath, applyEnvironments) assert.FileExists(t, filepath.Join(tmpDir, "project1/project.out")) assert.FileExists(t, filepath.Join(tmpDir, "project2/project.out")) @@ -41,9 +41,8 @@ func TestApply(t *testing.T) { t.Run("Moudles with failing checks should return error", func(t *testing.T) { tmpDir = setupTmpDir(t, "../../tests/test_data/apply-failing/") - assert.Panics(t, func() { - apply.Apply(tmpDir, applyConfigPath, applyEnvironments) - }) + err := apply.Apply(tmpDir, applyConfigPath, applyEnvironments) + assert.Regexp(t, "^Module checks failed:", err.Error()) }) } diff --git a/internal/util/util.go b/internal/util/util.go index dfafefe8d..974426ac5 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -48,7 +48,7 @@ func GetCwd() string { return dir } -func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { +func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { cmd.Dir = pathPrefix if !filepath.IsAbs(pathPrefix) { @@ -68,7 +68,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { err := cmd.Start() if err != nil { - panic(fmt.Sprintf("Starting command failed: %v\n", err)) + return err } go func() { @@ -80,7 +80,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { err = cmd.Wait() if err != nil { - panic(fmt.Sprintf("Executing command failed: %v\n", err)) + return err } if errStdout != nil { @@ -90,6 +90,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) { if errStderr != nil { log.Printf("Failed to capture stderr: %v\n", errStderr) } + return nil } // ExecuteCommandOutput runs the command and returns its diff --git a/tests/test_data/apply-failing/project2/Makefile b/tests/test_data/apply-failing/project2/Makefile index 348f9867a..523e365b8 100644 --- a/tests/test_data/apply-failing/project2/Makefile +++ b/tests/test_data/apply-failing/project2/Makefile @@ -4,4 +4,5 @@ current_dir: summary: check: - exit 1 + @echo "Expected failure from test fixture" + @exit 1 From 4ac9a6557f02f7338dea70bc5d373ee9ad55b46b Mon Sep 17 00:00:00 2001 From: David Cheung Date: Thu, 1 Apr 2021 18:54:05 -0400 Subject: [PATCH 03/12] fixup! combine the module walk methods and return err --- internal/apply/apply.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 0e0c21630..72eb42375 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -35,7 +35,7 @@ Only a single environment may be suitable for an initial test, but for a real sy environments = promptEnvironments() } - flog.Infof(":tada: checking project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) + flog.Infof(":mag: checking project %s's module requirements.", projectConfig.Name) err = modulesWalkCmd("check", rootDir, projectConfig, []string{"make", "check"}, environments) if err != nil { @@ -65,10 +65,10 @@ Only a single environment may be suitable for an initial test, but for a real sy return err } -func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, cmdArgs []string, applyEnvironments []string) error { +func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, cmdArgs []string, environments []string) error { graph := projectConfig.GetDAG() root := []dag.Vertex{projectconfig.GraphRootName} - environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(applyEnvironments, ",")) + environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(environments, ",")) err := graph.DepthFirstWalk(root, func(v dag.Vertex, depth int) error { // Don't process the root if depth == 0 { From b03ea8c5cbac65baca4d53ba215efe759efb219f Mon Sep 17 00:00:00 2001 From: David Cheung Date: Mon, 5 Apr 2021 17:37:49 -0400 Subject: [PATCH 04/12] custom makefile error logic --- internal/util/util.go | 23 ++++++++++++++++++- .../test_data/apply-failing/project2/Makefile | 6 +++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/util/util.go b/internal/util/util.go index 974426ac5..398453189 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -3,6 +3,8 @@ package util // @TODO split up and move into /pkg directory import ( + "bytes" + "errors" "fmt" "io" "log" @@ -11,8 +13,10 @@ import ( "path" "path/filepath" "reflect" + "regexp" "strconv" "strings" + "syscall" "text/template" "github.com/google/uuid" @@ -60,6 +64,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { stderrPipe, _ := cmd.StderrPipe() var errStdout, errStderr error + errContent := new(bytes.Buffer) cmd.Env = os.Environ() if envars != nil { @@ -75,11 +80,27 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { _, errStdout = io.Copy(os.Stdout, stdoutPipe) }() go func() { - _, errStderr = io.Copy(os.Stderr, stderrPipe) + stdErr := io.MultiWriter(errContent, os.Stderr) + _, errStderr = io.Copy(stdErr, stderrPipe) }() err = cmd.Wait() if err != nil { + // Detecting and returning the makefile error to cmd + // Passing alone makefile stderr as error message, otherwise it just says "exit status 2" + if exitError, ok := err.(*exec.ExitError); ok { + ws := exitError.Sys().(syscall.WaitStatus) + exitCode := ws.ExitStatus() + if exitCode == 2 { + stderrOut := errContent.String() + isMissingTarget, _ := regexp.MatchString("No rule to make target", stderrOut) + if isMissingTarget { + return errors.New("Module missing mandatory targets, this is likely an issue with the module itself.") + } + return errors.New(stderrOut) + } + } + return err } diff --git a/tests/test_data/apply-failing/project2/Makefile b/tests/test_data/apply-failing/project2/Makefile index 523e365b8..92b233330 100644 --- a/tests/test_data/apply-failing/project2/Makefile +++ b/tests/test_data/apply-failing/project2/Makefile @@ -1,8 +1,10 @@ +REQUIRED_BINS := ls nonexisting-binary + current_dir: @echo "baz: ${baz}" > project.out summary: check: - @echo "Expected failure from test fixture" - @exit 1 + $(foreach bin, $(REQUIRED_BINS),\ + $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`))) From 979de081823a355809ea6fdd852834d415da8932 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Tue, 6 Apr 2021 20:10:38 -0400 Subject: [PATCH 05/12] allow overriding commands for apply and check --- internal/apply/apply.go | 77 +++++++++++++++---- internal/apply/apply_test.go | 11 +++ internal/config/moduleconfig/module_config.go | 9 ++- internal/module/module_test.go | 5 ++ internal/util/util.go | 2 +- .../test_data/apply-failing/project1/Makefile | 1 + .../apply-failing/project1/project.out | 2 + .../test_data/apply-failing/project2/Makefile | 4 +- .../apply-failing/project2/project.out | 1 + .../test_data/apply-failing/project3/Makefile | 5 ++ .../test_data/apply-failing/project3/check.sh | 1 + .../apply-failing/project3/project.out | 1 + .../apply-failing/project3/zero-module.yml | 21 +++++ .../test_data/apply-failing/zero-project.yml | 5 ++ tests/test_data/apply/project2/check.sh | 2 + .../test_data/apply/project2/zero-module.yml | 3 +- tests/test_data/modules/ci/zero-module.yml | 2 + 17 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 tests/test_data/apply-failing/project1/project.out create mode 100644 tests/test_data/apply-failing/project2/project.out create mode 100644 tests/test_data/apply-failing/project3/Makefile create mode 100644 tests/test_data/apply-failing/project3/check.sh create mode 100644 tests/test_data/apply-failing/project3/project.out create mode 100644 tests/test_data/apply-failing/project3/zero-module.yml create mode 100644 tests/test_data/apply/project2/check.sh diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 72eb42375..37da95475 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -14,6 +14,7 @@ import ( "github.com/commitdev/zero/internal/util" "github.com/hashicorp/terraform/dag" + "github.com/commitdev/zero/internal/config/moduleconfig" "github.com/commitdev/zero/internal/config/projectconfig" "github.com/commitdev/zero/pkg/util/exit" "github.com/commitdev/zero/pkg/util/flog" @@ -21,7 +22,7 @@ import ( ) func Apply(rootDir string, configPath string, environments []string) error { - var err error + var errs []error if strings.Trim(configPath, " ") == "" { exit.Fatal("config path cannot be empty!") } @@ -37,9 +38,14 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof(":mag: checking project %s's module requirements.", projectConfig.Name) - err = modulesWalkCmd("check", rootDir, projectConfig, []string{"make", "check"}, environments) - if err != nil { - return errors.New(fmt.Sprintf("Module checks failed: %s", err.Error())) + errs = modulesWalkCmd("check", rootDir, projectConfig, "check", environments, false) + // Check operation walks through all modules and can return multiple errors + if len(errs) > 0 { + msg := "" + for i := 0; i < len(errs); i++ { + msg += "\t" + errs[i].Error() + } + return errors.New(fmt.Sprintf("Module checks failed: \n%s", msg)) } flog.Infof(":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) @@ -50,22 +56,23 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof("Infrastructure executor: %s", "Terraform") - err = modulesWalkCmd("apply", rootDir, projectConfig, []string{"make"}, environments) - if err != nil { - return errors.New(fmt.Sprintf("Module Apply failed: %s", err.Error())) + errs = modulesWalkCmd("apply", rootDir, projectConfig, "apply", environments, true) + if len(errs) > 0 { + return errors.New(fmt.Sprintf("Module Apply failed: %s", errs[0])) } flog.Infof(":check_mark_button: Done.") flog.Infof("Your projects and infrastructure have been successfully created. Here are some useful links and commands to get you started:") - err = modulesWalkCmd("summary", rootDir, projectConfig, []string{"make", "summary"}, environments) - if err != nil { - return errors.New(fmt.Sprintf("Module summary failed: %s", err.Error())) + errs = modulesWalkCmd("summary", rootDir, projectConfig, "summary", environments, true) + if len(errs) > 0 { + return errors.New(fmt.Sprintf("Module summary failed: %s", errs[0])) } - return err + return nil } -func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, cmdArgs []string, environments []string) error { +func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, operation string, environments []string, bailOnError bool) []error { + var moduleErrors []error graph := projectConfig.GetDAG() root := []dag.Vertex{projectconfig.GraphRootName} environmentArg := fmt.Sprintf("ENVIRONMENT=%s", strings.Join(environments, ",")) @@ -108,15 +115,53 @@ func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconf if lifecycleName == "apply" { flog.Infof("Executing %s command for %s...", lifecycleName, modConfig.Name) } - - execErr := util.ExecuteCommand(exec.Command(cmdArgs[0], cmdArgs[1:]...), modulePath, envList) + operationCommand := getModuleOperationCommand(modConfig, operation) + execErr := util.ExecuteCommand(exec.Command(operationCommand[0], operationCommand[1:]...), modulePath, envList) if execErr != nil { - return errors.New(fmt.Sprintf("Module (%s) %s", modConfig.Name, execErr.Error())) + formatedErr := errors.New(fmt.Sprintf("Module (%s) %s", modConfig.Name, execErr.Error())) + if bailOnError { + return formatedErr + } else { + moduleErrors = append(moduleErrors, formatedErr) + } } return nil }) + if err != nil { + moduleErrors = append(moduleErrors, err) + } - return err + return moduleErrors +} + +func getModuleOperationCommand(mod moduleconfig.ModuleConfig, operation string) (operationCommand []string) { + defaultCheck := []string{"make", "check"} + defaultApply := []string{"make"} + defaultSummary := []string{"make", "summary"} + + switch operation { + case "check": + if mod.Commands.Check != "" { + operationCommand = strings.Split(mod.Commands.Check, " ") + } else { + operationCommand = defaultCheck + } + case "apply": + if mod.Commands.Apply != "" { + operationCommand = strings.Split(mod.Commands.Apply, " ") + } else { + operationCommand = defaultApply + } + case "summary": + if mod.Commands.Summary != "" { + operationCommand = strings.Split(mod.Commands.Summary, " ") + } else { + operationCommand = defaultSummary + } + default: + panic("Unexpected operation") + } + return operationCommand } // promptEnvironments Prompts the user for the environments to apply against and returns a slice of strings representing the environments diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index 6f1775ce2..e5dc27ff0 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -30,6 +30,13 @@ func TestApply(t *testing.T) { content, err = ioutil.ReadFile(filepath.Join(tmpDir, "project2/project.out")) assert.NoError(t, err) assert.Equal(t, "baz: qux\n", string(content)) + + }) + + t.Run("Moudles runs command overides", func(t *testing.T) { + content, err := ioutil.ReadFile(filepath.Join(tmpDir, "project2/check.out")) + assert.NoError(t, err) + assert.Equal(t, "custom check\n", string(content)) }) t.Run("Zero apply honors the envVarName overwrite from module definition", func(t *testing.T) { @@ -43,7 +50,11 @@ func TestApply(t *testing.T) { err := apply.Apply(tmpDir, applyConfigPath, applyEnvironments) assert.Regexp(t, "^Module checks failed:", err.Error()) + assert.Regexp(t, "Module \\(project1\\)", err.Error()) + assert.Regexp(t, "Module \\(project2\\)", err.Error()) + assert.Regexp(t, "Module \\(project3\\)", err.Error()) }) + } func setupTmpDir(t *testing.T, exampleDirPath string) string { diff --git a/internal/config/moduleconfig/module_config.go b/internal/config/moduleconfig/module_config.go index df1199ca5..f040a0595 100644 --- a/internal/config/moduleconfig/module_config.go +++ b/internal/config/moduleconfig/module_config.go @@ -22,7 +22,8 @@ type ModuleConfig struct { Name string Description string Author string - DependsOn []string `yaml:"dependsOn,omitempty"` + Commands ModuleCommands `yaml:"commands,omitempty"` + DependsOn []string `yaml:"dependsOn,omitempty"` TemplateConfig `yaml:"template"` RequiredCredentials []string `yaml:"requiredCredentials"` ZeroVersion VersionConstraints `yaml:"zeroVersion,omitempty"` @@ -30,6 +31,12 @@ type ModuleConfig struct { Conditions []Condition `yaml:"conditions,omitempty"` } +type ModuleCommands struct { + Apply string `yaml:"apply,omitempty"` + Check string `yaml:"check,omitempty"` + Summary string `yaml:"summary,omitempty"` +} + func checkVersionAgainstConstrains(vc VersionConstraints, versionString string) bool { v, err := goVerson.NewVersion(versionString) if err != nil { diff --git a/internal/module/module_test.go b/internal/module/module_test.go index 21d0df699..34ae1111b 100644 --- a/internal/module/module_test.go +++ b/internal/module/module_test.go @@ -89,6 +89,11 @@ func TestParseModuleConfig(t *testing.T) { assert.Equal(t, []string{"<%", "%>"}, mod.TemplateConfig.Delimiters) }) + t.Run("Parsing commands", func(t *testing.T) { + checkCommand := mod.Commands.Check + assert.Equal(t, "ls", checkCommand) + }) + t.Run("Parsing zero version constraints", func(t *testing.T) { moduleConstraints := mod.ZeroVersion.Constraints.String() assert.Equal(t, ">= 3.0.0, < 4.0.0", moduleConstraints) diff --git a/internal/util/util.go b/internal/util/util.go index 398453189..70c8ebfc4 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -101,7 +101,7 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { } } - return err + return errors.New(errContent.String()) } if errStdout != nil { diff --git a/tests/test_data/apply-failing/project1/Makefile b/tests/test_data/apply-failing/project1/Makefile index 882a1e8e8..b3236e75a 100644 --- a/tests/test_data/apply-failing/project1/Makefile +++ b/tests/test_data/apply-failing/project1/Makefile @@ -5,3 +5,4 @@ current_dir: summary: check: + @$(error "Failure 1 of 2") diff --git a/tests/test_data/apply-failing/project1/project.out b/tests/test_data/apply-failing/project1/project.out new file mode 100644 index 000000000..028efdf7e --- /dev/null +++ b/tests/test_data/apply-failing/project1/project.out @@ -0,0 +1,2 @@ +foo: bar +repo: github.com/commitdev/project1 diff --git a/tests/test_data/apply-failing/project2/Makefile b/tests/test_data/apply-failing/project2/Makefile index 92b233330..808931db0 100644 --- a/tests/test_data/apply-failing/project2/Makefile +++ b/tests/test_data/apply-failing/project2/Makefile @@ -6,5 +6,5 @@ current_dir: summary: check: - $(foreach bin, $(REQUIRED_BINS),\ - $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`))) + $(foreach bin, $(REQUIRED_BINS),\ + $(if $(shell command -v $(bin) 2> /dev/null),$(info Found `$(bin)`),$(error Please install `$(bin)`))) diff --git a/tests/test_data/apply-failing/project2/project.out b/tests/test_data/apply-failing/project2/project.out new file mode 100644 index 000000000..48416721f --- /dev/null +++ b/tests/test_data/apply-failing/project2/project.out @@ -0,0 +1 @@ +baz: qux diff --git a/tests/test_data/apply-failing/project3/Makefile b/tests/test_data/apply-failing/project3/Makefile new file mode 100644 index 000000000..9a725c790 --- /dev/null +++ b/tests/test_data/apply-failing/project3/Makefile @@ -0,0 +1,5 @@ +REQUIRED_BINS := ls nonexisting-binary + +current_dir: + +summary: diff --git a/tests/test_data/apply-failing/project3/check.sh b/tests/test_data/apply-failing/project3/check.sh new file mode 100644 index 000000000..376dcab21 --- /dev/null +++ b/tests/test_data/apply-failing/project3/check.sh @@ -0,0 +1 @@ +>&2 echo "Check script erroring out";exit 1; \ No newline at end of file diff --git a/tests/test_data/apply-failing/project3/project.out b/tests/test_data/apply-failing/project3/project.out new file mode 100644 index 000000000..48416721f --- /dev/null +++ b/tests/test_data/apply-failing/project3/project.out @@ -0,0 +1 @@ +baz: qux diff --git a/tests/test_data/apply-failing/project3/zero-module.yml b/tests/test_data/apply-failing/project3/zero-module.yml new file mode 100644 index 000000000..826ffc129 --- /dev/null +++ b/tests/test_data/apply-failing/project3/zero-module.yml @@ -0,0 +1,21 @@ +name: project3 +description: 'project3' +author: 'Commit' + +commands: + check: sh check.sh +template: + strictMode: true + delimiters: + - "<%" + - "%>" + inputDir: '.' + outputDir: 'test' + +requiredCredentials: + - aws + - github + +parameters: + - field: baz + label: baz diff --git a/tests/test_data/apply-failing/zero-project.yml b/tests/test_data/apply-failing/zero-project.yml index 09c239cfc..dff2c5b60 100644 --- a/tests/test_data/apply-failing/zero-project.yml +++ b/tests/test_data/apply-failing/zero-project.yml @@ -15,3 +15,8 @@ modules: dir: project2 repo: github.com/commitdev/project2 source: project2 + project3: + files: + dir: project3 + repo: github.com/commitdev/project3 + source: project3 diff --git a/tests/test_data/apply/project2/check.sh b/tests/test_data/apply/project2/check.sh new file mode 100644 index 000000000..b73208798 --- /dev/null +++ b/tests/test_data/apply/project2/check.sh @@ -0,0 +1,2 @@ +pwd +echo "custom check" > check.out \ No newline at end of file diff --git a/tests/test_data/apply/project2/zero-module.yml b/tests/test_data/apply/project2/zero-module.yml index c50d5df76..6ce2ddf03 100644 --- a/tests/test_data/apply/project2/zero-module.yml +++ b/tests/test_data/apply/project2/zero-module.yml @@ -1,7 +1,8 @@ name: project2 description: 'project2' author: 'Commit' - +commands: + check: sh check.sh template: strictMode: true delimiters: diff --git a/tests/test_data/modules/ci/zero-module.yml b/tests/test_data/modules/ci/zero-module.yml index 7e5081368..4e78e7503 100644 --- a/tests/test_data/modules/ci/zero-module.yml +++ b/tests/test_data/modules/ci/zero-module.yml @@ -4,6 +4,8 @@ author: "CI author" icon: "" thumbnail: "" zeroVersion: ">= 3.0.0, < 4.0.0" +commands: + check: ls requiredCredentials: - aws From f2bf40dfb130a0d3b701cd29637693ee8d8aac64 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 13:54:41 -0400 Subject: [PATCH 06/12] fixup! allow overriding commands for apply and check --- internal/apply/apply.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 37da95475..165c6851f 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -142,19 +142,19 @@ func getModuleOperationCommand(mod moduleconfig.ModuleConfig, operation string) switch operation { case "check": if mod.Commands.Check != "" { - operationCommand = strings.Split(mod.Commands.Check, " ") + operationCommand = []string{"sh", "-c", mod.Commands.Check} } else { operationCommand = defaultCheck } case "apply": if mod.Commands.Apply != "" { - operationCommand = strings.Split(mod.Commands.Apply, " ") + operationCommand = []string{"sh", "-c", mod.Commands.Apply} } else { operationCommand = defaultApply } case "summary": if mod.Commands.Summary != "" { - operationCommand = strings.Split(mod.Commands.Summary, " ") + operationCommand = []string{"sh", "-c", mod.Commands.Summary} } else { operationCommand = defaultSummary } From c19f94134eff3919fcbfcb45f5a094ded2444bc7 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 14:06:52 -0400 Subject: [PATCH 07/12] add module commands to readme --- docs/module-definition.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/module-definition.md b/docs/module-definition.md index f75378e82..dec427286 100644 --- a/docs/module-definition.md +++ b/docs/module-definition.md @@ -10,9 +10,17 @@ It also declares the module's dependencies to determine the order of execution | `author` | string | Author of the module | | `icon` | string | Path to logo image | | `parameters` | list(Parameter) | Parameters to prompt users | +| `commands` | Commands | Commands to use instead of makefile defaults | | `zeroVersion` | string([go-semver])| Zero versions its compatible with | +### Commands +Commands are the lifecycle of `zero apply`, it will run all module's `check phase`, then once satisfied run in sequence `apply phase` then if successful run `summary phase`. +| Parameters | Type | Default | Description | +|------------|--------|----------------|--------------------------------------------------------------------------| +| `check` | string | `make check` | Command to check module requirements. check is satisfied if exit code is 0 eg: `sh check-token.sh`, `zero apply` will check all modules before executing | +| `apply` | string | `make` | Command to execute the project provisioning. | +| `summary` | string | `make summary` | Command to summarize to users the module's output and next steps. | ### Template | Parameters | Type | Description | |--------------|---------|-----------------------------------------------------------------------| From a5a87e70122429b21d84939f09d505085e638025 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 16:38:27 -0400 Subject: [PATCH 08/12] fixup! add module commands to readme --- internal/apply/apply.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 165c6851f..66458b24a 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -43,9 +43,9 @@ Only a single environment may be suitable for an initial test, but for a real sy if len(errs) > 0 { msg := "" for i := 0; i < len(errs); i++ { - msg += "\t" + errs[i].Error() + msg += "- " + errs[i].Error() } - return errors.New(fmt.Sprintf("Module checks failed: \n%s", msg)) + return errors.New(fmt.Sprintf("The following Module check(s) failed: \n%s", msg)) } flog.Infof(":tada: Bootstrapping project %s. Please use the zero-project.yml file to modify the project as needed.", projectConfig.Name) From 157b422cd394c4ecd513edfc96e52be7b06a8982 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 16:43:21 -0400 Subject: [PATCH 09/12] fixup! fixup! add module commands to readme --- internal/apply/apply_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index e5dc27ff0..9542e04d9 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -49,7 +49,7 @@ func TestApply(t *testing.T) { tmpDir = setupTmpDir(t, "../../tests/test_data/apply-failing/") err := apply.Apply(tmpDir, applyConfigPath, applyEnvironments) - assert.Regexp(t, "^Module checks failed:", err.Error()) + assert.Regexp(t, "^The following Module check\\(s\\) failed:", err.Error()) assert.Regexp(t, "Module \\(project1\\)", err.Error()) assert.Regexp(t, "Module \\(project2\\)", err.Error()) assert.Regexp(t, "Module \\(project3\\)", err.Error()) From cdeb162fdf4c8af023098f211ec92f41ecc2f5e7 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 17:23:31 -0400 Subject: [PATCH 10/12] fixup! fixup! fixup! add module commands to readme --- internal/apply/apply.go | 10 +++++----- internal/apply/apply_test.go | 4 ++-- internal/util/util.go | 8 ++++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 66458b24a..5649ba772 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -38,7 +38,7 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof(":mag: checking project %s's module requirements.", projectConfig.Name) - errs = modulesWalkCmd("check", rootDir, projectConfig, "check", environments, false) + errs = modulesWalkCmd("check", rootDir, projectConfig, "check", environments, false, false) // Check operation walks through all modules and can return multiple errors if len(errs) > 0 { msg := "" @@ -56,7 +56,7 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof("Infrastructure executor: %s", "Terraform") - errs = modulesWalkCmd("apply", rootDir, projectConfig, "apply", environments, true) + errs = modulesWalkCmd("apply", rootDir, projectConfig, "apply", environments, true, true) if len(errs) > 0 { return errors.New(fmt.Sprintf("Module Apply failed: %s", errs[0])) } @@ -64,14 +64,14 @@ Only a single environment may be suitable for an initial test, but for a real sy flog.Infof(":check_mark_button: Done.") flog.Infof("Your projects and infrastructure have been successfully created. Here are some useful links and commands to get you started:") - errs = modulesWalkCmd("summary", rootDir, projectConfig, "summary", environments, true) + errs = modulesWalkCmd("summary", rootDir, projectConfig, "summary", environments, true, true) if len(errs) > 0 { return errors.New(fmt.Sprintf("Module summary failed: %s", errs[0])) } return nil } -func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, operation string, environments []string, bailOnError bool) []error { +func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconfig.ZeroProjectConfig, operation string, environments []string, bailOnError bool, shouldPipeStderr bool) []error { var moduleErrors []error graph := projectConfig.GetDAG() root := []dag.Vertex{projectconfig.GraphRootName} @@ -116,7 +116,7 @@ func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconf flog.Infof("Executing %s command for %s...", lifecycleName, modConfig.Name) } operationCommand := getModuleOperationCommand(modConfig, operation) - execErr := util.ExecuteCommand(exec.Command(operationCommand[0], operationCommand[1:]...), modulePath, envList) + execErr := util.ExecuteCommand(exec.Command(operationCommand[0], operationCommand[1:]...), modulePath, envList, shouldPipeStderr) if execErr != nil { formatedErr := errors.New(fmt.Sprintf("Module (%s) %s", modConfig.Name, execErr.Error())) if bailOnError { diff --git a/internal/apply/apply_test.go b/internal/apply/apply_test.go index 9542e04d9..e5ecc0359 100644 --- a/internal/apply/apply_test.go +++ b/internal/apply/apply_test.go @@ -33,7 +33,7 @@ func TestApply(t *testing.T) { }) - t.Run("Moudles runs command overides", func(t *testing.T) { + t.Run("Modules runs command overides", func(t *testing.T) { content, err := ioutil.ReadFile(filepath.Join(tmpDir, "project2/check.out")) assert.NoError(t, err) assert.Equal(t, "custom check\n", string(content)) @@ -45,7 +45,7 @@ func TestApply(t *testing.T) { assert.Equal(t, "envVarName of viaEnvVarName: baz\n", string(content)) }) - t.Run("Moudles with failing checks should return error", func(t *testing.T) { + t.Run("Modules with failing checks should return error", func(t *testing.T) { tmpDir = setupTmpDir(t, "../../tests/test_data/apply-failing/") err := apply.Apply(tmpDir, applyConfigPath, applyEnvironments) diff --git a/internal/util/util.go b/internal/util/util.go index 70c8ebfc4..e6a1f6efe 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -52,7 +52,7 @@ func GetCwd() string { return dir } -func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { +func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string, shouldPipeStdErr bool) error { cmd.Dir = pathPrefix if !filepath.IsAbs(pathPrefix) { @@ -80,7 +80,11 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string) error { _, errStdout = io.Copy(os.Stdout, stdoutPipe) }() go func() { - stdErr := io.MultiWriter(errContent, os.Stderr) + strErrStreams := []io.Writer{errContent} + if shouldPipeStdErr { + strErrStreams = append(strErrStreams, os.Stderr) + } + stdErr := io.MultiWriter(strErrStreams...) _, errStderr = io.Copy(stdErr, stderrPipe) }() From 0b9bbee4f4160600ca23a0cff51f2e10962d49f2 Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 17:29:40 -0400 Subject: [PATCH 11/12] fixup! fixup! fixup! fixup! add module commands to readme --- internal/util/util.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/util/util.go b/internal/util/util.go index e6a1f6efe..5dc13c1e7 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -80,11 +80,11 @@ func ExecuteCommand(cmd *exec.Cmd, pathPrefix string, envars []string, shouldPip _, errStdout = io.Copy(os.Stdout, stdoutPipe) }() go func() { - strErrStreams := []io.Writer{errContent} + stderrStreams := []io.Writer{errContent} if shouldPipeStdErr { - strErrStreams = append(strErrStreams, os.Stderr) + stderrStreams = append(stderrStreams, os.Stderr) } - stdErr := io.MultiWriter(strErrStreams...) + stdErr := io.MultiWriter(stderrStreams...) _, errStderr = io.Copy(stdErr, stderrPipe) }() From 5d475f1c0b3e3fd65d6b48a8fe6adeae8b39bd6a Mon Sep 17 00:00:00 2001 From: David Cheung Date: Wed, 7 Apr 2021 17:45:46 -0400 Subject: [PATCH 12/12] fix module error printing incorrect message --- internal/apply/apply.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 5649ba772..c60303197 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -104,7 +104,7 @@ func modulesWalkCmd(lifecycleName string, dir string, projectConfig *projectconf // and we should redownload the module for the user modConfig, err := module.ParseModuleConfig(modulePath) if err != nil { - exit.Fatal("Failed to load module config, credentials cannot be injected properly") + exit.Fatal("Failed to load Module: %s", err) } envVarTranslationMap := modConfig.GetParamEnvVarTranslationMap()