Skip to content

Commit cd1599f

Browse files
authored
fix(internal/provider): set all supported envbuilder options (#38)
* fix(internal/provider): set all supported envbuilder options * do not allow overriding git_url and cache_repo
1 parent 6cf3d93 commit cd1599f

File tree

2 files changed

+146
-64
lines changed

2 files changed

+146
-64
lines changed

internal/provider/cached_image_resource.go

+134-59
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525

2626
"github.com/hashicorp/terraform-plugin-framework/attr"
2727
"github.com/hashicorp/terraform-plugin-framework/diag"
28+
"github.com/hashicorp/terraform-plugin-framework/path"
2829
"github.com/hashicorp/terraform-plugin-framework/resource"
2930
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
3031
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
@@ -160,7 +161,6 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq
160161
MarkdownDescription: "(Envbuilder option) Terminates upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error.",
161162
Optional: true,
162163
},
163-
// TODO(mafredri): Map vs List? Support both?
164164
"extra_env": schema.MapAttribute{
165165
MarkdownDescription: "Extra environment variables to set for the container. This may include envbuilder options.",
166166
ElementType: types.StringType,
@@ -293,6 +293,135 @@ func (r *CachedImageResource) Configure(ctx context.Context, req resource.Config
293293
r.client = client
294294
}
295295

296+
// setComputedEnv sets data.Env and data.EnvMap based on the values of the
297+
// other fields in the model.
298+
func (data *CachedImageResourceModel) setComputedEnv(ctx context.Context) diag.Diagnostics {
299+
env := make(map[string]string)
300+
301+
env["ENVBUILDER_CACHE_REPO"] = tfValueToString(data.CacheRepo)
302+
env["ENVBUILDER_GIT_URL"] = tfValueToString(data.GitURL)
303+
304+
if !data.BaseImageCacheDir.IsNull() {
305+
env["ENVBUILDER_BASE_IMAGE_CACHE_DIR"] = tfValueToString(data.BaseImageCacheDir)
306+
}
307+
308+
if !data.BuildContextPath.IsNull() {
309+
env["ENVBUILDER_BUILD_CONTEXT_PATH"] = tfValueToString(data.BuildContextPath)
310+
}
311+
312+
if !data.CacheTTLDays.IsNull() {
313+
env["ENVBUILDER_CACHE_TTL_DAYS"] = tfValueToString(data.CacheTTLDays)
314+
}
315+
316+
if !data.DevcontainerDir.IsNull() {
317+
env["ENVBUILDER_DEVCONTAINER_DIR"] = tfValueToString(data.DevcontainerDir)
318+
}
319+
320+
if !data.DevcontainerJSONPath.IsNull() {
321+
env["ENVBUILDER_DEVCONTAINER_JSON_PATH"] = tfValueToString(data.DevcontainerJSONPath)
322+
}
323+
324+
if !data.DockerfilePath.IsNull() {
325+
env["ENVBUILDER_DOCKERFILE_PATH"] = tfValueToString(data.DockerfilePath)
326+
}
327+
328+
if !data.DockerConfigBase64.IsNull() {
329+
env["ENVBUILDER_DOCKER_CONFIG_BASE64"] = tfValueToString(data.DockerConfigBase64)
330+
}
331+
332+
if !data.ExitOnBuildFailure.IsNull() {
333+
env["ENVBUILDER_EXIT_ON_BUILD_FAILURE"] = tfValueToString(data.ExitOnBuildFailure)
334+
}
335+
336+
if !data.FallbackImage.IsNull() {
337+
env["ENVBUILDER_FALLBACK_IMAGE"] = tfValueToString(data.FallbackImage)
338+
}
339+
340+
if !data.GitCloneDepth.IsNull() {
341+
env["ENVBUILDER_GIT_CLONE_DEPTH"] = tfValueToString(data.GitCloneDepth)
342+
}
343+
344+
if !data.GitCloneSingleBranch.IsNull() {
345+
env["ENVBUILDER_GIT_CLONE_SINGLE_BRANCH"] = tfValueToString(data.GitCloneSingleBranch)
346+
}
347+
348+
if !data.GitHTTPProxyURL.IsNull() {
349+
env["ENVBUILDER_GIT_HTTP_PROXY_URL"] = tfValueToString(data.GitHTTPProxyURL)
350+
}
351+
352+
if !data.GitSSHPrivateKeyPath.IsNull() {
353+
env["ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH"] = tfValueToString(data.GitSSHPrivateKeyPath)
354+
}
355+
356+
if !data.GitUsername.IsNull() {
357+
env["ENVBUILDER_GIT_USERNAME"] = tfValueToString(data.GitUsername)
358+
}
359+
360+
if !data.GitPassword.IsNull() {
361+
env["ENVBUILDER_GIT_PASSWORD"] = tfValueToString(data.GitPassword)
362+
}
363+
364+
if !data.IgnorePaths.IsNull() {
365+
env["ENVBUILDER_IGNORE_PATHS"] = strings.Join(tfListToStringSlice(data.IgnorePaths), ",")
366+
}
367+
368+
if !data.Insecure.IsNull() {
369+
env["ENVBUILDER_INSECURE"] = tfValueToString(data.Insecure)
370+
}
371+
372+
// Default to remote build mode.
373+
if data.RemoteRepoBuildMode.IsNull() {
374+
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = "true"
375+
} else {
376+
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = tfValueToString(data.RemoteRepoBuildMode)
377+
}
378+
379+
if !data.SSLCertBase64.IsNull() {
380+
env["ENVBUILDER_SSL_CERT_BASE64"] = tfValueToString(data.SSLCertBase64)
381+
}
382+
383+
if !data.Verbose.IsNull() {
384+
env["ENVBUILDER_VERBOSE"] = tfValueToString(data.Verbose)
385+
}
386+
387+
if !data.WorkspaceFolder.IsNull() {
388+
env["ENVBUILDER_WORKSPACE_FOLDER"] = tfValueToString(data.WorkspaceFolder)
389+
}
390+
391+
// Do ExtraEnv last so that it can override any other values.
392+
// With one exception: ENVBUILDER_CACHE_REPO and ENVBUILDER_GIT_URL are required and should not be overridden.
393+
// Other values set by the provider may be overridden, but will generate a warning.
394+
var diag, ds diag.Diagnostics
395+
if !data.ExtraEnv.IsNull() {
396+
for key, elem := range data.ExtraEnv.Elements() {
397+
switch key {
398+
// These are required and should not be overridden.
399+
case "ENVBUILDER_CACHE_REPO", "ENVBUILDER_GIT_URL":
400+
diag.AddAttributeWarning(path.Root("extra_env"),
401+
"Cannot override required environment variable",
402+
fmt.Sprintf("The key %q in extra_env cannot be overridden.", key),
403+
)
404+
default:
405+
if _, ok := env[key]; ok {
406+
// This is a warning because it's possible that the user wants to override
407+
// a value set by the provider.
408+
diag.AddAttributeWarning(path.Root("extra_env"),
409+
"Overriding provider environment variable",
410+
fmt.Sprintf("The key %q in extra_env overrides an environment variable set by the provider.", key),
411+
)
412+
}
413+
env[key] = tfValueToString(elem)
414+
}
415+
}
416+
}
417+
418+
data.EnvMap, ds = basetypes.NewMapValueFrom(ctx, types.StringType, env)
419+
diag = append(diag, ds...)
420+
data.Env, ds = basetypes.NewListValueFrom(ctx, types.StringType, sortedKeyValues(env))
421+
diag = append(diag, ds...)
422+
return diag
423+
}
424+
296425
func (r *CachedImageResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
297426
var data CachedImageResourceModel
298427

@@ -350,35 +479,7 @@ func (r *CachedImageResource) Read(ctx context.Context, req resource.ReadRequest
350479
data.Exists = types.BoolValue(true)
351480

352481
// Set the expected environment variables.
353-
env := make(map[string]string)
354-
for key, elem := range data.ExtraEnv.Elements() {
355-
env[key] = tfValueToString(elem)
356-
}
357-
358-
env["ENVBUILDER_CACHE_REPO"] = tfValueToString(data.CacheRepo)
359-
env["ENVBUILDER_GIT_URL"] = tfValueToString(data.GitURL)
360-
361-
if !data.CacheTTLDays.IsNull() {
362-
env["ENVBUILDER_CACHE_TTL_DAYS"] = tfValueToString(data.CacheTTLDays)
363-
}
364-
if !data.GitUsername.IsNull() {
365-
env["ENVBUILDER_GIT_USERNAME"] = tfValueToString(data.GitUsername)
366-
}
367-
if !data.GitPassword.IsNull() {
368-
env["ENVBUILDER_GIT_PASSWORD"] = tfValueToString(data.GitPassword)
369-
}
370-
// Default to remote build mode.
371-
if data.RemoteRepoBuildMode.IsNull() {
372-
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = "true"
373-
} else {
374-
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = tfValueToString(data.RemoteRepoBuildMode)
375-
}
376-
377-
var diag diag.Diagnostics
378-
data.EnvMap, diag = basetypes.NewMapValueFrom(ctx, types.StringType, env)
379-
resp.Diagnostics.Append(diag...)
380-
data.Env, diag = basetypes.NewListValueFrom(ctx, types.StringType, sortedKeyValues(env))
381-
resp.Diagnostics.Append(diag...)
482+
resp.Diagnostics.Append(data.setComputedEnv(ctx)...)
382483

383484
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
384485
}
@@ -415,36 +516,9 @@ func (r *CachedImageResource) Create(ctx context.Context, req resource.CreateReq
415516
data.Image = types.StringValue(fmt.Sprintf("%s@%s", data.CacheRepo.ValueString(), digest))
416517
data.ID = types.StringValue(digest.String())
417518
}
418-
// Compute the env attribute from the config map.
419-
env := make(map[string]string)
420-
for key, elem := range data.ExtraEnv.Elements() {
421-
env[key] = tfValueToString(elem)
422-
}
423519

424-
env["ENVBUILDER_CACHE_REPO"] = tfValueToString(data.CacheRepo)
425-
env["ENVBUILDER_GIT_URL"] = tfValueToString(data.GitURL)
426-
427-
if !data.CacheTTLDays.IsNull() {
428-
env["ENVBUILDER_CACHE_TTL_DAYS"] = tfValueToString(data.CacheTTLDays)
429-
}
430-
if !data.GitUsername.IsNull() {
431-
env["ENVBUILDER_GIT_USERNAME"] = tfValueToString(data.GitUsername)
432-
}
433-
if !data.GitPassword.IsNull() {
434-
env["ENVBUILDER_GIT_PASSWORD"] = tfValueToString(data.GitPassword)
435-
}
436-
// Default to remote build mode.
437-
if data.RemoteRepoBuildMode.IsNull() {
438-
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = "true"
439-
} else {
440-
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = tfValueToString(data.RemoteRepoBuildMode)
441-
}
442-
443-
var diag diag.Diagnostics
444-
data.EnvMap, diag = basetypes.NewMapValueFrom(ctx, types.StringType, env)
445-
resp.Diagnostics.Append(diag...)
446-
data.Env, diag = basetypes.NewListValueFrom(ctx, types.StringType, sortedKeyValues(env))
447-
resp.Diagnostics.Append(diag...)
520+
// Set the expected environment variables.
521+
resp.Diagnostics.Append(data.setComputedEnv(ctx)...)
448522

449523
// Save data into Terraform state
450524
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
@@ -556,6 +630,7 @@ func (r *CachedImageResource) runCacheProbe(ctx context.Context, data CachedImag
556630
Insecure: data.Insecure.ValueBool(), // might have internal CAs?
557631
IgnorePaths: tfListToStringSlice(data.IgnorePaths), // may need to be specified?
558632
// The below options are not relevant and are set to their zero value explicitly.
633+
// They must be set by extra_env.
559634
CoderAgentSubsystem: nil,
560635
CoderAgentToken: "",
561636
CoderAgentURL: "",

internal/provider/cached_image_resource_test.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ RUN date > /date.txt`,
4848
//nolint: paralleltest
4949
deps := setup(ctx, t, tc.files)
5050
deps.ExtraEnv["FOO"] = testEnvValue
51+
deps.ExtraEnv["ENVBUILDER_GIT_URL"] = "https://not.the.real.git/url"
52+
deps.ExtraEnv["ENVBUILDER_CACHE_REPO"] = "not-the-real-cache-repo"
5153

5254
resource.Test(t, resource.TestCase{
5355
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
@@ -149,16 +151,21 @@ func assertEnv(t *testing.T, deps testDependencies) resource.TestCheckFunc {
149151
return resource.ComposeAggregateTestCheckFunc(
150152
// Check that the environment variables are set correctly.
151153
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.0", fmt.Sprintf("ENVBUILDER_CACHE_REPO=%s", deps.CacheRepo)),
152-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.1", fmt.Sprintf("ENVBUILDER_GIT_URL=%s", deps.Repo.URL)),
153-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.2", "ENVBUILDER_REMOTE_REPO_BUILD_MODE=true"),
154+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.1", fmt.Sprintf("ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH=%s", deps.Repo.Key)),
155+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.2", fmt.Sprintf("ENVBUILDER_GIT_URL=%s", deps.Repo.URL)),
156+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.3", "ENVBUILDER_REMOTE_REPO_BUILD_MODE=true"),
157+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.4", "ENVBUILDER_VERBOSE=true"),
154158
// Check that the extra environment variables are set correctly.
155-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.3", "FOO=bar\nbaz"),
159+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.5", "FOO=bar\nbaz"),
156160
// We should not have any other environment variables set.
157-
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "env.4"),
161+
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "env.6"),
162+
158163
// Check that the same values are set in env_map.
159-
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.FOO", "bar\nbaz"),
160164
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_CACHE_REPO", deps.CacheRepo),
165+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key),
161166
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_GIT_URL", deps.Repo.URL),
162167
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true"),
168+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_VERBOSE", "true"),
169+
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.FOO", "bar\nbaz"),
163170
)
164171
}

0 commit comments

Comments
 (0)