@@ -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+
296425func (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 : "" ,
0 commit comments