Skip to content

Commit

Permalink
[DT-3750] Add --config-dump option (#189)
Browse files Browse the repository at this point in the history
Added a new --config-dump option that dumps the TGF configuration and exits. It also filters out AWS credentials from the environment so the dumped configuration can be used in more contexts and avoids leaking secrets.
Example usage: `tgf --ignore-user-config --config-dump`

Co-authored-by: Jo Giroux <[email protected]>
  • Loading branch information
ycanty and jocgir authored Feb 22, 2021
1 parent 0298799 commit cc05c23
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 21 deletions.
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,3 @@ Builds are automatically launched on tagging.

Tags with the format image-0.0.0 automatically launch a Docker images build that are available through Docker Hub.
Tags with the format v0.0.0 automatically launch a new release on Github for the TGF executable.

Tests that involve docker builds are not compatible with docker buildkit. Run them with:

```bash
DOCKER_BUILDKIT=0 go test ./...
```
11 changes: 2 additions & 9 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ type TGFApplication struct {
AwsProfile string
ConfigFiles string
ConfigLocation string
ConfigDump bool
DisableUserConfig bool
DockerBuild bool
DockerInteractive bool
Expand Down Expand Up @@ -164,6 +165,7 @@ func NewTGFApplication(args []string) *TGFApplication {
app.Flag("ssm-path", "Parameter Store path used to find AWS common configuration shared by a team").PlaceHolder("<path>").Default(defaultSSMParameterFolder).StringVar(&app.PsPath)
app.Flag("config-files", "Set the files to look for (default: "+remoteDefaultConfigPath+")").PlaceHolder("<files>").StringVar(&app.ConfigFiles)
app.Flag("config-location", "Set the configuration location").PlaceHolder("<path>").StringVar(&app.ConfigLocation)
app.Flag("config-dump", "Print the TGF configuration and exit").BoolVar(&app.ConfigDump)
app.Flag("update", "Run auto update script").IsSetByUser(&app.AutoUpdateSet).BoolVar(&app.AutoUpdate)

kingpin.CommandLine = app.Application
Expand Down Expand Up @@ -266,14 +268,5 @@ func (app *TGFApplication) ShowHelp(c *kingpin.ParseContext) error {

// Run execute the application
func (app *TGFApplication) Run() int {
if app.GetCurrentVersion {
if version == locallyBuilt {
fmt.Println("tgf (built from source)")
} else {
fmt.Printf("tgf v%s\n", version)
}
return 0
}

return RunWithUpdateCheck(InitConfig(app))
}
6 changes: 5 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,10 @@ func (config *TGFConfig) InitAWS() error {
"AWS_REGION": *session.Config.Region,
} {
os.Setenv(key, value)
config.Environment[key] = value
if !config.tgf.ConfigDump {
// If we are saving the current configuration, we do not want to save the current credentials
config.Environment[key] = value
}
}
return nil
}
Expand All @@ -279,6 +282,7 @@ func (config *TGFConfig) setDefaultValues() {
if err := config.InitAWS(); err != nil {
log.Fatal(err)
}

if app.ConfigLocation == "" {
values := config.readSSMParameterStore(app.PsPath)
app.ConfigLocation = values[remoteConfigLocationParameter]
Expand Down
14 changes: 14 additions & 0 deletions config_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ func (config *TGFConfig) Run() int {
return 1
}

if app.GetCurrentVersion {
if version == locallyBuilt {
fmt.Println("tgf (built from source)")
} else {
fmt.Printf("tgf v%s\n", version)
}
return 0
}

if app.ConfigDump {
fmt.Println(config.String())
return 0
}

if app.GetAllVersions {
if filepath.Base(config.EntryPoint) != "terragrunt" {
log.Error("--all-version works only with terragrunt as the entrypoint")
Expand Down
24 changes: 19 additions & 5 deletions config_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ package main
import (
"bytes"
"fmt"
"github.com/coveooss/gotemplate/v3/yaml"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"

"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)

func setup(t *testing.T, testFunction func()) string {
func setup(t *testing.T, testFunction func()) (string, string) {
// To ensure that the test is not altered by the environment
env := os.Environ()
os.Clearenv()
Expand Down Expand Up @@ -45,12 +47,13 @@ func setup(t *testing.T, testFunction func()) string {
testFunction()
w.Close()
out, _ := ioutil.ReadAll(r)
return string(out) + logBuffer.String()
return string(out), logBuffer.String()
}

func TestCurrentVersion(t *testing.T) {
version = locallyBuilt
output := setup(t, func() {
output, _ := setup(t, func() {
log.SetDefaultConsoleHookLevel(logrus.WarnLevel)
app := NewTGFApplication([]string{"--current-version"})
exitCode := app.Run()
assert.Equal(t, 0, exitCode, "exitCode")
Expand All @@ -59,10 +62,21 @@ func TestCurrentVersion(t *testing.T) {
}

func TestAllVersions(t *testing.T) {
output := setup(t, func() {
_, logOutput := setup(t, func() {
app := NewTGFApplication([]string{"--all-versions", "--no-aws", "--ignore-user-config", "--entrypoint=OTHER_FILE"})
exitCode := InitConfig(app).Run()
assert.Equal(t, 1, exitCode, "exitCode")
})
assert.Contains(t, output, "ERROR: --all-version works only with terragrunt as the entrypoint\n")
assert.Contains(t, logOutput, "ERROR: --all-version works only with terragrunt as the entrypoint\n")
}

func TestConfigDump_isValidYAML(t *testing.T) {
output, _ := setup(t, func() {
app := NewTGFApplication([]string{"-L=5", "--config-dump", "--no-aws", "--ignore-user-config", "--entrypoint=OTHER_FILE"})
exitCode := InitConfig(app).Run()
assert.Equal(t, 0, exitCode, "exitCode")
})

// --config-dump output can be redirected to a file, so it must be valid YAML.
assert.NoError(t, yaml.UnmarshalStrict([]byte(output), &TGFConfig{}))
}
36 changes: 36 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,42 @@ func TestSetConfigDefaultValues(t *testing.T) {
assert.Nil(t, config.ImageVersion)
}

/*
Test that --config-dump filters out AWS secrets.
This allows the dumped configuration to be used in more contexts, without exposing secrets.
See https://coveord.atlassian.net/browse/DT-3750
*/
func TestConfigDumpFiltersOutAWSEnvironment(t *testing.T) {
log.SetOut(os.Stdout)

// We must reset the cached AWS config check since it could have been modified by another test
resetCache()
tempDir, _ := filepath.EvalSymlinks(must(ioutil.TempDir("", "TestGetConfig")).(string))
currentDir, _ := os.Getwd()
assert.NoError(t, os.Chdir(tempDir))
defer func() {
assert.NoError(t, os.Chdir(currentDir))
assert.NoError(t, os.RemoveAll(tempDir))
}()

testTgfConfigFile := fmt.Sprintf("%s/.tgf.config", tempDir)

tgfConfig := []byte(String(`
docker-image: coveo/stuff
docker-image-build: RUN ls test2
docker-image-build-tag: hello
docker-image-build-folder: my-folder
`).UnIndent().TrimSpace())
ioutil.WriteFile(testTgfConfigFile, tgfConfig, 0644)

app := NewTestApplication([]string{"--config-dump"}, true)
config := InitConfig(app)

for key := range config.Environment {
assert.False(t, strings.Contains(key, "AWS"), "Environment must not contain any AWS_* key, but found %s", key)
}
}

func TestTwoLevelsOfTgfConfig(t *testing.T) {
tempDir, _ := filepath.EvalSymlinks(must(ioutil.TempDir("", "TestGetConfig")).(string))
currentDir, _ := os.Getwd()
Expand Down
1 change: 1 addition & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (

func TestGetImage(t *testing.T) {

os.Setenv("DOCKER_BUILDKIT", "0") // For docker tests to pass on Mac, docker buildkit must be disabled
testImageName := "test-image" + strconv.Itoa(randInt())
testTag := "test" + strconv.Itoa(randInt())
testImageNameTagged := testImageName + ":" + testTag
Expand Down

0 comments on commit cc05c23

Please sign in to comment.