Skip to content

Commit 9f45583

Browse files
authored
fix(provider): correctly handle devcontainer-only in cache probe mode (#33)
* chore(internal/provider): add test case for dockerfile-only operation * chore(deps): update envbuilder to include #315
1 parent 68cc59d commit 9f45583

File tree

3 files changed

+125
-102
lines changed

3 files changed

+125
-102
lines changed

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20240702054557-aa55
1010

1111
require (
1212
github.com/GoogleContainerTools/kaniko v1.9.2
13-
github.com/coder/envbuilder v1.0.0-rc.0.0.20240807151028-6e5bfa5faa29
13+
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815111948-e6283db826e8
1414
github.com/docker/docker v26.1.4+incompatible
1515
github.com/gliderlabs/ssh v0.3.7
1616
github.com/go-git/go-billy/v5 v5.5.0
@@ -248,7 +248,7 @@ require (
248248
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
249249
github.com/shopspring/decimal v1.3.1 // indirect
250250
github.com/sirupsen/logrus v1.9.3 // indirect
251-
github.com/skeema/knownhosts v1.2.2 // indirect
251+
github.com/skeema/knownhosts v1.3.0 // indirect
252252
github.com/spaolacci/murmur3 v1.1.0 // indirect
253253
github.com/spf13/afero v1.11.0 // indirect
254254
github.com/spf13/cast v1.6.0 // indirect

go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
186186
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
187187
github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352 h1:L/EjCuZxs5tOcqqCaASj/nu65TRYEFcTt8qRQfHZXX0=
188188
github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352/go.mod h1:P1KoQSgnKEAG6Mnd3YlGzAophty+yKA9VV48LpfNRvo=
189-
github.com/coder/envbuilder v1.0.0-rc.0.0.20240807151028-6e5bfa5faa29 h1:PhJBofIrh6NGuTQ93nW7/KWcYX6Ju0PGoE/BbNvf87o=
190-
github.com/coder/envbuilder v1.0.0-rc.0.0.20240807151028-6e5bfa5faa29/go.mod h1:lm33s3+chqnl7lB4avNFfDH5gXDYSunYD7/y4bJ/LMA=
189+
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815111948-e6283db826e8 h1:tfmaVV7qFpODoBliTcDQyrB09FIjCQRUtjefr7zFEXY=
190+
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815111948-e6283db826e8/go.mod h1:HFqLE6BNJhR/fLknKWon5Eqhsr5FmuEJO1OJ/RKF2BA=
191191
github.com/coder/kaniko v0.0.0-20240807142221-ffc5e60fca41 h1:1Ye7AcLnuT5IDv6il7Fxo+aqpzlWfedkpraCCwx8Lyo=
192192
github.com/coder/kaniko v0.0.0-20240807142221-ffc5e60fca41/go.mod h1:YMK7BlxerzLlMwihGxNWUaFoN9LXCij4P+w/8/fNlcM=
193193
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
@@ -718,8 +718,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
718718
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
719719
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
720720
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
721-
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
722-
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
721+
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
722+
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
723723
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
724724
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
725725
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=

internal/provider/cached_image_resource_test.go

+119-96
Original file line numberDiff line numberDiff line change
@@ -10,106 +10,129 @@ import (
1010
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
1111
)
1212

13+
// testEnvValue is a multi-line environment variable value that we use in
14+
// tests to ensure that we can handle multi-line values correctly.
15+
var testEnvValue = `bar
16+
baz`
17+
1318
func TestAccCachedImageResource(t *testing.T) {
14-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
19+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
1520
defer cancel()
16-
files := map[string]string{
17-
".devcontainer/devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
18-
".devcontainer/Dockerfile": `FROM localhost:5000/test-ubuntu:latest
19-
RUN date > /date.txt`,
20-
}
2121

22-
deps := setup(ctx, t, files)
23-
deps.ExtraEnv["FOO"] = `bar
24-
baz` // THIS IS A LOAD-BEARING NEWLINE. DO NOT REMOVE.
25-
resource.Test(t, resource.TestCase{
26-
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
27-
Steps: []resource.TestStep{
28-
// Initial state: cache has not been seeded.
29-
{
30-
Config: deps.Config(t),
31-
PlanOnly: true,
32-
ExpectNonEmptyPlan: true,
33-
},
34-
// Should detect that no cached image is present and plan to create the resource.
35-
{
36-
Config: deps.Config(t),
37-
Check: resource.ComposeAggregateTestCheckFunc(
38-
// Computed values MUST be present.
39-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "id", uuid.Nil.String()),
40-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "false"),
41-
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "env.0"),
42-
// Cached image should be set to the builder image.
43-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
44-
// Inputs should still be present.
45-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
46-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
47-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
48-
// Should be empty
49-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
50-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
51-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
52-
),
53-
ExpectNonEmptyPlan: true, // TODO: check the plan.
54-
},
55-
// Re-running plan should have the same effect.
56-
{
57-
Config: deps.Config(t),
58-
Check: resource.ComposeAggregateTestCheckFunc(
59-
// Computed values MUST be present.
60-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "id", uuid.Nil.String()),
61-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "false"),
62-
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "env.0"),
63-
// Cached image should be set to the builder image.
64-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
65-
// Inputs should still be present.
66-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
67-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
68-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
69-
// Should be empty
70-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
71-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
72-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
73-
),
74-
ExpectNonEmptyPlan: true, // TODO: check the plan.
75-
},
76-
// Now, seed the cache and re-run. We should now successfully create the cached image resource.
77-
{
78-
PreConfig: func() {
79-
seedCache(ctx, t, deps)
80-
},
81-
Config: deps.Config(t),
82-
Check: resource.ComposeAggregateTestCheckFunc(
83-
// Inputs should still be present.
84-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
85-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
86-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
87-
// Should be empty
88-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
89-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
90-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
91-
// Computed
92-
resource.TestCheckResourceAttrWith("envbuilder_cached_image.test", "id", quotedPrefix("sha256:")),
93-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "true"),
94-
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "image"),
95-
resource.TestCheckResourceAttrWith("envbuilder_cached_image.test", "image", quotedPrefix(deps.CacheRepo)),
96-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.0", "FOO=bar\nbaz"),
97-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.1", fmt.Sprintf("ENVBUILDER_CACHE_REPO=%s", deps.CacheRepo)),
98-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.2", fmt.Sprintf("ENVBUILDER_GIT_URL=%s", deps.Repo.URL)),
99-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.3", "ENVBUILDER_REMOTE_REPO_BUILD_MODE=true"),
100-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "env.4"),
101-
),
22+
for _, tc := range []struct {
23+
name string
24+
files map[string]string
25+
}{
26+
{
27+
name: "devcontainer only",
28+
files: map[string]string{
29+
".devcontainer/devcontainer.json": `{"image": "localhost:5000/test-ubuntu:latest"}`,
10230
},
103-
// Should produce an empty plan after apply
104-
{
105-
Config: deps.Config(t),
106-
PlanOnly: true,
107-
},
108-
// Ensure idempotence in this state!
109-
{
110-
Config: deps.Config(t),
111-
PlanOnly: true,
31+
},
32+
{
33+
name: "devcontainer and Dockerfile",
34+
files: map[string]string{
35+
".devcontainer/devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
36+
".devcontainer/Dockerfile": `FROM localhost:5000/test-ubuntu:latest
37+
RUN date > /date.txt`,
11238
},
11339
},
114-
})
40+
} {
41+
t.Run(tc.name, func(t *testing.T) {
42+
//nolint: paralleltest
43+
deps := setup(ctx, t, tc.files)
44+
deps.ExtraEnv["FOO"] = testEnvValue
45+
46+
resource.Test(t, resource.TestCase{
47+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
48+
Steps: []resource.TestStep{
49+
// Initial state: cache has not been seeded.
50+
{
51+
Config: deps.Config(t),
52+
PlanOnly: true,
53+
ExpectNonEmptyPlan: true,
54+
},
55+
// Should detect that no cached image is present and plan to create the resource.
56+
{
57+
Config: deps.Config(t),
58+
Check: resource.ComposeAggregateTestCheckFunc(
59+
// Computed values MUST be present.
60+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "id", uuid.Nil.String()),
61+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "false"),
62+
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "env.0"),
63+
// Cached image should be set to the builder image.
64+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
65+
// Inputs should still be present.
66+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
67+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
68+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
69+
// Should be empty
70+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
71+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
72+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
73+
),
74+
ExpectNonEmptyPlan: true, // TODO: check the plan.
75+
},
76+
// Re-running plan should have the same effect.
77+
{
78+
Config: deps.Config(t),
79+
Check: resource.ComposeAggregateTestCheckFunc(
80+
// Computed values MUST be present.
81+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "id", uuid.Nil.String()),
82+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "false"),
83+
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "env.0"),
84+
// Cached image should be set to the builder image.
85+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
86+
// Inputs should still be present.
87+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
88+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
89+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
90+
// Should be empty
91+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
92+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
93+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
94+
),
95+
ExpectNonEmptyPlan: true, // TODO: check the plan.
96+
},
97+
// Now, seed the cache and re-run. We should now successfully create the cached image resource.
98+
{
99+
PreConfig: func() {
100+
seedCache(ctx, t, deps)
101+
},
102+
Config: deps.Config(t),
103+
Check: resource.ComposeAggregateTestCheckFunc(
104+
// Inputs should still be present.
105+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
106+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
107+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
108+
// Should be empty
109+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
110+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
111+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
112+
// Computed
113+
resource.TestCheckResourceAttrWith("envbuilder_cached_image.test", "id", quotedPrefix("sha256:")),
114+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "exists", "true"),
115+
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "image"),
116+
resource.TestCheckResourceAttrWith("envbuilder_cached_image.test", "image", quotedPrefix(deps.CacheRepo)),
117+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.0", "FOO=bar\nbaz"),
118+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.1", fmt.Sprintf("ENVBUILDER_CACHE_REPO=%s", deps.CacheRepo)),
119+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.2", fmt.Sprintf("ENVBUILDER_GIT_URL=%s", deps.Repo.URL)),
120+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.3", "ENVBUILDER_REMOTE_REPO_BUILD_MODE=true"),
121+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "env.4"),
122+
),
123+
},
124+
// Should produce an empty plan after apply
125+
{
126+
Config: deps.Config(t),
127+
PlanOnly: true,
128+
},
129+
// Ensure idempotence in this state!
130+
{
131+
Config: deps.Config(t),
132+
PlanOnly: true,
133+
},
134+
},
135+
})
136+
})
137+
}
115138
}

0 commit comments

Comments
 (0)