From 129a82d46126e10a235eff99c73b5dec861fe891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kota=C4=8Dka?= Date: Wed, 3 Oct 2018 16:17:30 +0200 Subject: [PATCH 1/3] Implement a function for workspace selection, or creation A new function `WorkspaceSelectOrNew` has been implemented and covered via three unit tests. --- modules/terraform/workspace.go | 36 +++++++++++++ modules/terraform/workspace_test.go | 62 +++++++++++++++++++++++ test/fixtures/terraform-workspace/main.tf | 3 ++ 3 files changed, 101 insertions(+) create mode 100644 modules/terraform/workspace.go create mode 100644 modules/terraform/workspace_test.go create mode 100644 test/fixtures/terraform-workspace/main.tf diff --git a/modules/terraform/workspace.go b/modules/terraform/workspace.go new file mode 100644 index 000000000..e60d3aa5f --- /dev/null +++ b/modules/terraform/workspace.go @@ -0,0 +1,36 @@ +package terraform + +import ( + "strings" + "testing" +) + +// WorkspaceSelectOrNew runs terraform workspace with the given options and returns workspace name. +// It tries to select a workspace with the given name, or it creates a new one if it doesn't exist. +func WorkspaceSelectOrNew(t *testing.T, options *Options, name string) string { + out, err := WorkspaceSelectOrNewE(t, options, name) + if err != nil { + t.Fatal(err) + } + return out +} + +// WorkspaceSelectOrNewE runs terraform workspace with the given options and returns workspace name. +// It tries to select a workspace with the given name, or it creates a new one if it doesn't exist. +func WorkspaceSelectOrNewE(t *testing.T, options *Options, name string) (string, error) { + out, err := RunTerraformCommandE(t, options, "workspace", "list") + if err != nil { + return out, nil + } + + if strings.Contains(out, name) { + _, err = RunTerraformCommandE(t, options, "workspace", "select", name) + } else { + _, err = RunTerraformCommandE(t, options, "workspace", "new", name) + } + if err != nil { + return out, nil + } + + return RunTerraformCommandE(t, options, "workspace", "show") +} diff --git a/modules/terraform/workspace_test.go b/modules/terraform/workspace_test.go new file mode 100644 index 000000000..74e019885 --- /dev/null +++ b/modules/terraform/workspace_test.go @@ -0,0 +1,62 @@ +package terraform + +import ( + "testing" + + "github.com/gruntwork-io/terratest/modules/files" + "github.com/stretchr/testify/assert" +) + +func TestWorkspaceNew(t *testing.T) { + t.Parallel() + + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-workspace", t.Name()) + if err != nil { + t.Fatal(err) + } + + options := &Options{ + TerraformDir: testFolder, + } + + out := WorkspaceSelectOrNew(t, options, "terratest") + + assert.Contains(t, out, "terratest") +} + +func TestWorkspaceSelect(t *testing.T) { + t.Parallel() + + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-workspace", t.Name()) + if err != nil { + t.Fatal(err) + } + + options := &Options{ + TerraformDir: testFolder, + } + + out := WorkspaceSelectOrNew(t, options, "terratest") + assert.Contains(t, out, "terratest") + + out = WorkspaceSelectOrNew(t, options, "default") + assert.Contains(t, out, "default") +} + +func TestWorkspaceApply(t *testing.T) { + t.Parallel() + + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-workspace", t.Name()) + if err != nil { + t.Fatal(err) + } + + options := &Options{ + TerraformDir: testFolder, + } + + WorkspaceSelectOrNew(t, options, "Terratest") + out := InitAndApply(t, options) + + assert.Contains(t, out, "Hello, Terratest") +} diff --git a/test/fixtures/terraform-workspace/main.tf b/test/fixtures/terraform-workspace/main.tf new file mode 100644 index 000000000..a1be4f9dc --- /dev/null +++ b/test/fixtures/terraform-workspace/main.tf @@ -0,0 +1,3 @@ +output "test" { + value = "Hello, ${terraform.workspace}" +} \ No newline at end of file From 0aaf98b32ddaac5cfd116f6c5b5a1bfcabd7d46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kota=C4=8Dka?= Date: Thu, 4 Oct 2018 17:15:20 +0200 Subject: [PATCH 2/3] Improve checking for existing workspace name + additional unit tests --- modules/terraform/workspace.go | 33 ++++++++++--- modules/terraform/workspace_test.go | 76 +++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/modules/terraform/workspace.go b/modules/terraform/workspace.go index e60d3aa5f..7e9957cc1 100644 --- a/modules/terraform/workspace.go +++ b/modules/terraform/workspace.go @@ -1,12 +1,15 @@ package terraform import ( + "fmt" + "regexp" "strings" "testing" ) -// WorkspaceSelectOrNew runs terraform workspace with the given options and returns workspace name. -// It tries to select a workspace with the given name, or it creates a new one if it doesn't exist. +// WorkspaceSelectOrNew runs terraform workspace with the given options and the workspace name +// and returns a name of the current workspace. It tries to select a workspace with the given +// name, or it creates a new one if it doesn't exist. func WorkspaceSelectOrNew(t *testing.T, options *Options, name string) string { out, err := WorkspaceSelectOrNewE(t, options, name) if err != nil { @@ -15,22 +18,38 @@ func WorkspaceSelectOrNew(t *testing.T, options *Options, name string) string { return out } -// WorkspaceSelectOrNewE runs terraform workspace with the given options and returns workspace name. -// It tries to select a workspace with the given name, or it creates a new one if it doesn't exist. +// WorkspaceSelectOrNewE runs terraform workspace with the given options and the workspace name +// and returns a name of the current workspace. It tries to select a workspace with the given +// name, or it creates a new one if it doesn't exist. func WorkspaceSelectOrNewE(t *testing.T, options *Options, name string) (string, error) { out, err := RunTerraformCommandE(t, options, "workspace", "list") if err != nil { - return out, nil + return "", err } - if strings.Contains(out, name) { + if isExistingWorkspace(out, name) { _, err = RunTerraformCommandE(t, options, "workspace", "select", name) } else { _, err = RunTerraformCommandE(t, options, "workspace", "new", name) } if err != nil { - return out, nil + return "", err } return RunTerraformCommandE(t, options, "workspace", "show") } + +func isExistingWorkspace(out string, name string) bool { + workspaces := strings.Split(out, "\n") + for _, ws := range workspaces { + if nameMatchesWorkspace(name, ws) { + return true + } + } + return false +} + +func nameMatchesWorkspace(name string, workspace string) bool { + match, _ := regexp.MatchString(fmt.Sprintf("^\\*?\\s*%s$", name), workspace) + return match +} diff --git a/modules/terraform/workspace_test.go b/modules/terraform/workspace_test.go index 74e019885..4d3d05916 100644 --- a/modules/terraform/workspace_test.go +++ b/modules/terraform/workspace_test.go @@ -21,7 +21,25 @@ func TestWorkspaceNew(t *testing.T) { out := WorkspaceSelectOrNew(t, options, "terratest") - assert.Contains(t, out, "terratest") + assert.Equal(t, "terratest", out) +} + +func TestWorkspaceIllegalName(t *testing.T) { + t.Parallel() + + testFolder, err := files.CopyTerraformFolderToTemp("../../test/fixtures/terraform-workspace", t.Name()) + if err != nil { + t.Fatal(err) + } + + options := &Options{ + TerraformDir: testFolder, + } + + out, err := WorkspaceSelectOrNewE(t, options, "###@@@&&&") + + assert.Error(t, err) + assert.Equal(t, "", out, "%q should be an empty string", out) } func TestWorkspaceSelect(t *testing.T) { @@ -37,10 +55,10 @@ func TestWorkspaceSelect(t *testing.T) { } out := WorkspaceSelectOrNew(t, options, "terratest") - assert.Contains(t, out, "terratest") + assert.Equal(t, "terratest", out) out = WorkspaceSelectOrNew(t, options, "default") - assert.Contains(t, out, "default") + assert.Equal(t, "default", out) } func TestWorkspaceApply(t *testing.T) { @@ -60,3 +78,55 @@ func TestWorkspaceApply(t *testing.T) { assert.Contains(t, out, "Hello, Terratest") } + +func TestIsExistingWorkspace(t *testing.T) { + t.Parallel() + + testCases := []struct { + out string + name string + expected bool + }{ + {" default\n* foo\n", "default", true}, + {"* default\n foo\n", "default", true}, + {" foo\n* default\n", "default", true}, + {"* foo\n default\n", "default", true}, + {" foo\n* bar\n", "default", false}, + {"* foo\n bar\n", "default", false}, + {" default\n* foobar\n", "foo", false}, + {"* default\n foobar\n", "foo", false}, + {" default\n* foo\n", "foobar", false}, + {"* default\n foo\n", "foobar", false}, + {"* default\n foo\n", "foo", true}, + } + + for _, testCase := range testCases { + actual := isExistingWorkspace(testCase.out, testCase.name) + assert.Equal(t, testCase.expected, actual, "Out: %q, Name: %q", testCase.out, testCase.name) + } +} + +func TestNameMatchesWorkspace(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + workspace string + expected bool + }{ + {"default", " default", true}, + {"default", "* default", true}, + {"default", "", false}, + {"foo", " foobar", false}, + {"foo", "* foobar", false}, + {"foobar", " foo", false}, + {"foobar", "* foo", false}, + {"foo", " foo", true}, + {"foo", "* foo", true}, + } + + for _, testCase := range testCases { + actual := nameMatchesWorkspace(testCase.name, testCase.workspace) + assert.Equal(t, testCase.expected, actual, "Name: %q, Workspace: %q", testCase.name, testCase.workspace) + } +} From 8ca27732b4582e6d309bad8169953d45b9361080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Kota=C4=8Dka?= Date: Fri, 5 Oct 2018 19:05:17 +0200 Subject: [PATCH 3/3] Document regex for matching workspace --- modules/terraform/workspace.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/terraform/workspace.go b/modules/terraform/workspace.go index 7e9957cc1..98c57a924 100644 --- a/modules/terraform/workspace.go +++ b/modules/terraform/workspace.go @@ -50,6 +50,12 @@ func isExistingWorkspace(out string, name string) bool { } func nameMatchesWorkspace(name string, workspace string) bool { + // Regex for matching workspace should match for strings with optional leading asterisk "*" + // following optional white spaces following the workspace name. + // E.g. for the given name "terratest", following strings will match: + // + // "* terratest" + // " terratest" match, _ := regexp.MatchString(fmt.Sprintf("^\\*?\\s*%s$", name), workspace) return match }