Skip to content

Commit 2fe557a

Browse files
authored
Merge branch 'main' into helm-install-generate-name
2 parents d840712 + 5d3ece5 commit 2fe557a

29 files changed

Lines changed: 567 additions & 78 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ only happen in major releases (e.g. v2.0.0).
3737
Symbols renamed or replaced in v1 are kept with `// Deprecated:` annotations pointing at the new name; removals happen
3838
in v2. Migrating from v0.x: see the [v1 migration guide](https://terratest.gruntwork.io/docs/migrating-to-v1/overview/).
3939

40+
**v1 maintenance.** With v2 in development, the v1 line has entered maintenance: it receives security fixes only,
41+
delivered on the `v1` branch, until 12 months after v2.0.0 reaches general availability. v2 ships under new `/v2`
42+
module paths, so pinned v1 consumers are unaffected. Upgrade on your own schedule.
43+
4044
## More info
4145

4246
- [Terratest Website](https://terratest.gruntwork.io)

docs/Gemfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ GEM
3737
ffi (>= 1.3.0)
3838
eventmachine (1.2.7)
3939
execjs (2.7.0)
40-
faraday (2.14.2)
40+
faraday (2.14.3)
4141
faraday-net_http (>= 2.0, < 3.5)
4242
json
4343
logger
44-
faraday-net_http (3.4.2)
44+
faraday-net_http (3.4.4)
4545
net-http (~> 0.5)
4646
ffi (1.13.1)
4747
forwardable-extended (2.6.0)
@@ -213,7 +213,7 @@ GEM
213213
gemoji (~> 3.0)
214214
html-pipeline (~> 2.2)
215215
jekyll (>= 3.0, < 5.0)
216-
json (2.19.5)
216+
json (2.20.0)
217217
kramdown (2.3.2)
218218
rexml
219219
kramdown-parser-gfm (1.1.0)

docs/v2-import-map.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Terratest v2 Import Map
2+
3+
Status: FROZEN. The one open decision (renames) is resolved: the three hyphenated packages are renamed to idiomatic Go names at the `/v2` boundary.
4+
5+
Built from the actual v1 layout at tag `v1.0.1-test` (27 `modules/` packages, 2 `cmd/` binaries, 1 `internal/lib` tree).
6+
7+
Module base path: `github.com/gruntwork-io/terratest`
8+
9+
## Transformation rule
10+
11+
For any surviving import path, the rewrite is a prefix replacement that also applies to every subpackage:
12+
13+
- `modules/<name>/...` -> `modules/<name>/v2/...` (the `/v2` SIV goes after the module root; directory layout unchanged except for the three renames below)
14+
- The six tier-0 utilities collapse under one module: `modules/<util>/...` -> `modules/core/v2/<util>/...`
15+
- Three packages are also renamed to drop the hyphen: `http-helper` -> `httphelper`, `dns-helper` -> `dnshelper`, `test-structure` -> `teststructure`. The package identifier loses its underscore too (`http_helper` -> `httphelper`), so call sites change, not just the import path. The codemod handles both.
16+
17+
So e.g. `modules/logger/parser` -> `modules/core/v2/logger/parser`, `modules/aws/foo` -> `modules/aws/v2/foo`, and `modules/http-helper` -> `modules/httphelper/v2`.
18+
19+
## core collapse (6 v1 packages -> one `modules/core/v2`)
20+
21+
| v1 import path | v2 import path |
22+
|---|---|
23+
| `modules/logger` | `modules/core/v2/logger` |
24+
| `modules/testing` | `modules/core/v2/testing` |
25+
| `modules/retry` | `modules/core/v2/retry` |
26+
| `modules/random` | `modules/core/v2/random` |
27+
| `modules/files` | `modules/core/v2/files` |
28+
| `modules/shell` | `modules/core/v2/shell` |
29+
30+
## Standalone `/v2` submodules
31+
32+
| v1 import path | v2 import path |
33+
|---|---|
34+
| `modules/aws` | `modules/aws/v2` |
35+
| `modules/azure` | `modules/azure/v2` |
36+
| `modules/gcp` | `modules/gcp/v2` |
37+
| `modules/k8s` | `modules/k8s/v2` |
38+
| `modules/helm` | `modules/helm/v2` |
39+
| `modules/ssh` | `modules/ssh/v2` |
40+
| `modules/docker` | `modules/docker/v2` |
41+
| `modules/packer` | `modules/packer/v2` |
42+
| `modules/database` | `modules/database/v2` |
43+
| `modules/opa` | `modules/opa/v2` |
44+
| `modules/terraform` | `modules/terraform/v2` |
45+
| `modules/terragrunt` | `modules/terragrunt/v2` |
46+
| `modules/http-helper` | `modules/httphelper/v2` |
47+
| `modules/dns-helper` | `modules/dnshelper/v2` |
48+
| `modules/test-structure` | `modules/teststructure/v2` |
49+
50+
## Removed in v2.0.0 (deprecated in v1 first, deleted at cutover)
51+
52+
| v1 import path | replacement |
53+
|---|---|
54+
| `modules/collections` | stdlib `slices` |
55+
| `modules/environment` | stdlib `os.Getenv` |
56+
| `modules/git` | stdlib `os/exec` |
57+
| `modules/slack` | none, vendor from frozen v1 if needed |
58+
| `modules/version-checker` | none, shell out |
59+
| `modules/oci` | none, Oracle Cloud support not carried forward; remains in frozen v1, vendor if needed |
60+
| `cmd/pick-instance-type` | none, standalone binary, out of scope |
61+
| `cmd/terratest_log_parser` | none, standalone binary (its `logger/parser` lib survives under `modules/core/v2/logger/parser`) |
62+
63+
## Internal flatten (non-importable, not consumer-facing)
64+
65+
| v1 | v2 |
66+
|---|---|
67+
| `internal/lib/formatting` | `internal/formatting` |
68+
69+
## Accounting
70+
71+
27 `modules/` packages = 6 collapsed into core + 15 standalone submodules + 6 removed. Plus 2 removed `cmd/` binaries and 1 internal flatten. Submodule count: 16.
72+
73+
## Open decisions
74+
75+
None. The map is frozen.
76+
77+
## Resolved
78+
79+
- **`oci`** (Oracle Cloud Infrastructure): not carried forward to v2. Niche provider; removed alongside the other dropped packages. Oracle Cloud users stay on frozen v1.
80+
- **Renames.** Decided to rename the three hyphenated packages at the v2 boundary: `http-helper` -> `httphelper`, `dns-helper` -> `dnshelper`, `test-structure` -> `teststructure`. Consumers already rewrite every import for the `/v2` bump, so folding the rename into that same edit adds no separate migration, and it drops the non-idiomatic underscore package names (`http_helper`, currently suppressed with `//nolint:staticcheck`). The rename rides along with the modularization import rewrite, and the codemod covers both the path and the package identifier.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Package collections holds small generic slice helpers shared across
2+
// Terratest's own packages. It is internal and not part of the public API; the
3+
// public modules/collections package is deprecated and scheduled for removal in
4+
// v2.
5+
package collections
6+
7+
// Intersection returns the items present in both lists, de-duplicated, in the
8+
// order they appear in list1.
9+
func Intersection[T comparable](list1 []T, list2 []T) []T {
10+
lookups := make(map[T]struct{}, len(list2))
11+
for _, item := range list2 {
12+
lookups[item] = struct{}{}
13+
}
14+
15+
out := make([]T, 0, min(len(list1), len(list2)))
16+
17+
for _, item := range list1 {
18+
if _, found := lookups[item]; found {
19+
out = append(out, item)
20+
delete(lookups, item) // delete so a repeated list1 item isn't emitted twice
21+
}
22+
}
23+
24+
return out
25+
}
26+
27+
// Subtract returns the items in list1 that are not in list2.
28+
func Subtract[T comparable](list1 []T, list2 []T) []T {
29+
lookups := make(map[T]struct{}, len(list2))
30+
for _, item := range list2 {
31+
lookups[item] = struct{}{}
32+
}
33+
34+
out := make([]T, 0, len(list1))
35+
36+
for _, item := range list1 {
37+
if _, found := lookups[item]; !found {
38+
out = append(out, item)
39+
}
40+
}
41+
42+
return out
43+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package collections_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/gruntwork-io/terratest/internal/collections"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestIntersection(t *testing.T) {
12+
t.Parallel()
13+
14+
tests := []struct {
15+
name string
16+
list1 []string
17+
list2 []string
18+
want []string
19+
}{
20+
{"common items, ordered by list1", []string{"a", "b", "c"}, []string{"b", "c", "d"}, []string{"b", "c"}},
21+
{"dedups output", []string{"a", "a"}, []string{"a"}, []string{"a"}},
22+
{"dedups duplicates in list2", []string{"a"}, []string{"a", "a"}, []string{"a"}},
23+
{"no overlap returns empty, not nil", []string{"a"}, []string{"b"}, []string{}},
24+
{"nil inputs return empty, not nil", nil, nil, []string{}},
25+
}
26+
27+
for _, tc := range tests {
28+
t.Run(tc.name, func(t *testing.T) {
29+
t.Parallel()
30+
assert.Equal(t, tc.want, collections.Intersection(tc.list1, tc.list2))
31+
})
32+
}
33+
}
34+
35+
func TestSubtract(t *testing.T) {
36+
t.Parallel()
37+
38+
tests := []struct {
39+
name string
40+
list1 []string
41+
list2 []string
42+
want []string
43+
}{
44+
{"removes list2 items", []string{"a", "b", "c"}, []string{"b", "c"}, []string{"a"}},
45+
{"everything removed returns empty, not nil", []string{"a", "b"}, []string{"a", "b"}, []string{}},
46+
{"nil list1 returns empty, not nil", nil, []string{"a"}, []string{}},
47+
{"nil list2 keeps list1", []string{"a", "b"}, nil, []string{"a", "b"}},
48+
}
49+
50+
for _, tc := range tests {
51+
t.Run(tc.name, func(t *testing.T) {
52+
t.Parallel()
53+
assert.Equal(t, tc.want, collections.Subtract(tc.list1, tc.list2))
54+
})
55+
}
56+
}
57+
58+
func TestSubtractDoesNotMutateInput(t *testing.T) {
59+
t.Parallel()
60+
61+
in := []string{"a", "b"}
62+
collections.Subtract(in, []string{"a"})
63+
assert.Equal(t, []string{"a", "b"}, in, "does not mutate the input slice")
64+
}
65+
66+
func ExampleIntersection() {
67+
fmt.Println(collections.Intersection([]int{1, 2, 3}, []int{2, 3, 4}))
68+
// Output: [2 3]
69+
}
70+
71+
func ExampleSubtract() {
72+
fmt.Println(collections.Subtract([]int{1, 2, 3}, []int{2, 3}))
73+
// Output: [1]
74+
}

modules/aws/region.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/aws/aws-sdk-go-v2/aws"
99
"github.com/aws/aws-sdk-go-v2/service/ec2"
1010
"github.com/aws/aws-sdk-go-v2/service/ssm"
11-
"github.com/gruntwork-io/terratest/modules/collections"
11+
"github.com/gruntwork-io/terratest/internal/collections"
1212
"github.com/gruntwork-io/terratest/modules/logger"
1313
"github.com/gruntwork-io/terratest/modules/random"
1414
"github.com/gruntwork-io/terratest/modules/testing"
@@ -53,11 +53,11 @@ func GetRandomStableRegionContextE(t testing.TestingT, ctx context.Context, appr
5353
regionsToPickFrom := stableRegions
5454

5555
if len(approvedRegions) > 0 {
56-
regionsToPickFrom = collections.ListIntersection(regionsToPickFrom, approvedRegions)
56+
regionsToPickFrom = collections.Intersection(regionsToPickFrom, approvedRegions)
5757
}
5858

5959
if len(forbiddenRegions) > 0 {
60-
regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions)
60+
regionsToPickFrom = collections.Subtract(regionsToPickFrom, forbiddenRegions)
6161
}
6262

6363
return GetRandomRegionContextE(t, ctx, regionsToPickFrom, nil)
@@ -122,7 +122,7 @@ func GetRandomRegionContextE(t testing.TestingT, ctx context.Context, approvedRe
122122
regionsToPickFrom = allRegions
123123
}
124124

125-
regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions)
125+
regionsToPickFrom = collections.Subtract(regionsToPickFrom, forbiddenRegions)
126126
region := random.RandomString(regionsToPickFrom)
127127

128128
logger.Default.Logf(t, "Using region %s", region)

modules/azure/errors.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,21 @@ func (e *UnknownEnvironmentError) Error() string {
7272
e.EnvironmentName)
7373
}
7474

75+
// ResourceIDNameNotFoundError is returned when a name cannot be resolved from an Azure resource ID.
76+
// The offending ID is exported so callers can inspect it via errors.As.
77+
type ResourceIDNameNotFoundError struct {
78+
ResourceID string
79+
}
80+
81+
func (err ResourceIDNameNotFoundError) Error() string {
82+
return fmt.Sprintf("could not resolve name from resource ID %q", err.ResourceID)
83+
}
84+
85+
// NewResourceIDNameNotFoundError creates a ResourceIDNameNotFoundError for the given resource ID.
86+
func NewResourceIDNameNotFoundError(resourceID string) ResourceIDNameNotFoundError {
87+
return ResourceIDNameNotFoundError{ResourceID: resourceID}
88+
}
89+
7590
// ResourceNotFoundErrorExists checks the Service Error Code for the 'Resource Not Found' error.
7691
func ResourceNotFoundErrorExists(err error) bool {
7792
if err == nil {

modules/azure/region.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package azure
33
import (
44
"context"
55

6-
"github.com/gruntwork-io/terratest/modules/collections"
6+
"github.com/gruntwork-io/terratest/internal/collections"
77
"github.com/gruntwork-io/terratest/modules/random"
88
"github.com/gruntwork-io/terratest/modules/testing"
99
"github.com/stretchr/testify/require"
@@ -72,11 +72,11 @@ func GetRandomStableRegionContext(t testing.TestingT, ctx context.Context, appro
7272
regionsToPickFrom := stableRegions
7373

7474
if len(approvedRegions) > 0 {
75-
regionsToPickFrom = collections.ListIntersection(regionsToPickFrom, approvedRegions)
75+
regionsToPickFrom = collections.Intersection(regionsToPickFrom, approvedRegions)
7676
}
7777

7878
if len(forbiddenRegions) > 0 {
79-
regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions)
79+
regionsToPickFrom = collections.Subtract(regionsToPickFrom, forbiddenRegions)
8080
}
8181

8282
return GetRandomRegionContext(t, ctx, regionsToPickFrom, nil, subscriptionID) //nolint:staticcheck
@@ -145,7 +145,7 @@ func GetRandomRegionContextE(t testing.TestingT, ctx context.Context, approvedRe
145145
regionsToPickFrom = allRegions
146146
}
147147

148-
regionsToPickFrom = collections.ListSubtract(regionsToPickFrom, forbiddenRegions)
148+
regionsToPickFrom = collections.Subtract(regionsToPickFrom, forbiddenRegions)
149149
region := random.RandomString(regionsToPickFrom)
150150

151151
return region, nil

modules/azure/resourceid.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package azure
22

3-
import "github.com/gruntwork-io/terratest/modules/collections"
3+
import (
4+
"strings"
5+
)
46

57
// GetNameFromResourceID gets the Name from an Azure Resource ID.
68
func GetNameFromResourceID(resourceID string) string {
@@ -15,10 +17,10 @@ func GetNameFromResourceID(resourceID string) string {
1517
// GetNameFromResourceIDE gets the Name from an Azure Resource ID.
1618
// This function would fail the test if there is an error.
1719
func GetNameFromResourceIDE(resourceID string) (string, error) {
18-
id, err := collections.GetSliceLastValueE(resourceID, "/")
19-
if err != nil {
20-
return "", err
20+
i := strings.LastIndex(resourceID, "/")
21+
if i == -1 || i == len(resourceID)-1 {
22+
return "", NewResourceIDNameNotFoundError(resourceID)
2123
}
2224

23-
return id, nil
25+
return resourceID[i+1:], nil
2426
}

0 commit comments

Comments
 (0)