diff --git a/docs/resources/cached_image.md b/docs/resources/cached_image.md index 93923fe..842d806 100644 --- a/docs/resources/cached_image.md +++ b/docs/resources/cached_image.md @@ -25,6 +25,7 @@ The cached image resource can be used to retrieve a cached image produced by env - `base_image_cache_dir` (String) (Envbuilder option) The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image. - `build_context_path` (String) (Envbuilder option) Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned. +- `build_secrets` (Map of String) The secrets to use for the build. This is a map of key-value pairs. - `cache_ttl_days` (Number) (Envbuilder option) The number of days to use cached layers before expiring them. Defaults to 7 days. - `devcontainer_dir` (String) (Envbuilder option) The path to the folder containing the devcontainer.json file that will be used to build the workspace and can either be an absolute path or a path relative to the workspace folder. If not provided, defaults to `.devcontainer`. - `devcontainer_json_path` (String) (Envbuilder option) The path to a devcontainer.json file that is either an absolute path or a path relative to DevcontainerDir. This can be used in cases where one wants to substitute an edited devcontainer.json file for the one that exists in the repo. diff --git a/internal/provider/cached_image_resource.go b/internal/provider/cached_image_resource.go index 3085e0c..986d628 100644 --- a/internal/provider/cached_image_resource.go +++ b/internal/provider/cached_image_resource.go @@ -51,6 +51,7 @@ type CachedImageResourceModel struct { // Optional "inputs". BaseImageCacheDir types.String `tfsdk:"base_image_cache_dir"` BuildContextPath types.String `tfsdk:"build_context_path"` + BuildSecrets types.Map `tfsdk:"build_secrets"` CacheTTLDays types.Int64 `tfsdk:"cache_ttl_days"` DevcontainerDir types.String `tfsdk:"devcontainer_dir"` DevcontainerJSONPath types.String `tfsdk:"devcontainer_json_path"` @@ -121,6 +122,11 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq MarkdownDescription: "(Envbuilder option) Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned.", Optional: true, }, + "build_secrets": schema.MapAttribute{ + MarkdownDescription: "The secrets to use for the build. This is a map of key-value pairs.", + ElementType: types.StringType, + Optional: true, + }, "cache_ttl_days": schema.Int64Attribute{ MarkdownDescription: "(Envbuilder option) The number of days to use cached layers before expiring them. Defaults to 7 days.", Optional: true, diff --git a/internal/provider/helpers.go b/internal/provider/helpers.go index af4d237..acc69f2 100644 --- a/internal/provider/helpers.go +++ b/internal/provider/helpers.go @@ -2,6 +2,7 @@ package provider import ( "fmt" + "slices" "strings" eboptions "github.com/coder/envbuilder/options" @@ -47,6 +48,22 @@ func optionsFromDataModel(data CachedImageResourceModel) (eboptions.Options, dia opts.BuildContextPath = data.BuildContextPath.ValueString() } + if !data.BuildSecrets.IsNull() { + providerOpts["ENVBUILDER_BUILD_SECRETS"] = true + + // Depending on use case, users might want to provide build secrets as a map or a list of strings. + // The string list option is supported by extra_env, so we support the map option here. Envbuilder + // expects a list of strings, so we convert the map to a list of strings here. + buildSecretMap := tfutil.TFMapToStringMap(data.BuildSecrets) + buildSecretSlice := make([]string, 0, len(buildSecretMap)) + for k, v := range buildSecretMap { + buildSecretSlice = append(buildSecretSlice, fmt.Sprintf("%s=%s", k, v)) + } + slices.Sort(buildSecretSlice) + + opts.BuildSecrets = buildSecretSlice + } + if !data.CacheTTLDays.IsNull() { providerOpts["ENVBUILDER_CACHE_TTL_DAYS"] = true opts.CacheTTLDays = data.CacheTTLDays.ValueInt64() @@ -199,7 +216,7 @@ func overrideOptionsFromExtraEnv(opts *eboptions.Options, extraEnv map[string]st // XXX: workaround for serpent behaviour where calling Set() on a // string slice will append instead of replace: set to empty first. - if key == "ENVBUILDER_IGNORE_PATHS" { + if _, ok := optsMap[key].(*serpent.StringArray); ok { _ = optsMap[key].Set("") } diff --git a/internal/provider/provider_internal_test.go b/internal/provider/provider_internal_test.go index c273a8c..c1ae983 100644 --- a/internal/provider/provider_internal_test.go +++ b/internal/provider/provider_internal_test.go @@ -35,11 +35,15 @@ func Test_optionsFromDataModel(t *testing.T) { { name: "all options without extra_env", data: CachedImageResourceModel{ - BuilderImage: basetypes.NewStringValue("envbuilder:latest"), - CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), - GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), - BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"), - BuildContextPath: basetypes.NewStringValue("."), + BuilderImage: basetypes.NewStringValue("envbuilder:latest"), + CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), + GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), + BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"), + BuildContextPath: basetypes.NewStringValue("."), + BuildSecrets: basetypes.NewMapValueMust(basetypes.StringType{}, map[string]attr.Value{ + "FOO": basetypes.NewStringValue("bar"), + "BAZ": basetypes.NewStringValue("qux"), + }), CacheTTLDays: basetypes.NewInt64Value(7), DevcontainerDir: basetypes.NewStringValue(".devcontainer"), DevcontainerJSONPath: basetypes.NewStringValue(".devcontainer/devcontainer.json"), @@ -66,6 +70,7 @@ func Test_optionsFromDataModel(t *testing.T) { GitURL: "git@git.local/devcontainer.git", BaseImageCacheDir: "/tmp/cache", BuildContextPath: ".", + BuildSecrets: []string{"BAZ=qux", "FOO=bar"}, // Sorted CacheTTLDays: 7, DevcontainerDir: ".devcontainer", DevcontainerJSONPath: ".devcontainer/devcontainer.json", @@ -97,9 +102,11 @@ func Test_optionsFromDataModel(t *testing.T) { "CODER_AGENT_TOKEN", "token", "CODER_AGENT_URL", "http://coder", "FOO", "bar", + "ENVBUILDER_BUILD_SECRETS", "FOO=bar,BAZ=qux", ), }, expectOpts: eboptions.Options{ + BuildSecrets: []string{"FOO=bar", "BAZ=qux"}, CacheRepo: "localhost:5000/cache", GitURL: "git@git.local/devcontainer.git", RemoteRepoBuildMode: true, @@ -110,11 +117,14 @@ func Test_optionsFromDataModel(t *testing.T) { { name: "extra_env override warnings", data: CachedImageResourceModel{ - BuilderImage: basetypes.NewStringValue("envbuilder:latest"), - CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), - GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), - BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"), - BuildContextPath: basetypes.NewStringValue("."), + BuilderImage: basetypes.NewStringValue("envbuilder:latest"), + CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), + GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), + BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"), + BuildContextPath: basetypes.NewStringValue("."), + BuildSecrets: basetypes.NewMapValueMust(basetypes.StringType{}, map[string]attr.Value{ + "FOO": basetypes.NewStringValue("bar"), + }), CacheTTLDays: basetypes.NewInt64Value(7), DevcontainerDir: basetypes.NewStringValue(".devcontainer"), DevcontainerJSONPath: basetypes.NewStringValue(".devcontainer/devcontainer.json"), @@ -136,6 +146,7 @@ func Test_optionsFromDataModel(t *testing.T) { Verbose: basetypes.NewBoolValue(true), WorkspaceFolder: basetypes.NewStringValue("workspace"), ExtraEnv: extraEnvMap(t, + "ENVBUILDER_BUILD_SECRETS", "FOO=bar,BAZ=qux", "ENVBUILDER_CACHE_REPO", "override", "ENVBUILDER_GIT_URL", "override", "ENVBUILDER_BASE_IMAGE_CACHE_DIR", "override", @@ -169,6 +180,7 @@ func Test_optionsFromDataModel(t *testing.T) { // overridden BaseImageCacheDir: "override", BuildContextPath: "override", + BuildSecrets: []string{"FOO=bar", "BAZ=qux"}, CacheTTLDays: 8, DevcontainerDir: "override", DevcontainerJSONPath: "override", @@ -189,7 +201,7 @@ func Test_optionsFromDataModel(t *testing.T) { Verbose: false, WorkspaceFolder: "override", }, - expectNumWarningDiags: 23, + expectNumWarningDiags: 24, }, { name: "extra_env override errors", @@ -295,6 +307,7 @@ func Test_computeEnvFromOptions(t *testing.T) { BaseImageCacheDir: "string", BinaryPath: "string", BuildContextPath: "string", + BuildSecrets: []string{"FOO=bar", "BAZ=qux"}, CacheRepo: "string", CacheTTLDays: 1, CoderAgentSubsystem: []string{"one", "two"}, @@ -339,6 +352,7 @@ func Test_computeEnvFromOptions(t *testing.T) { "ENVBUILDER_BASE_IMAGE_CACHE_DIR": "string", "ENVBUILDER_BINARY_PATH": "string", "ENVBUILDER_BUILD_CONTEXT_PATH": "string", + "ENVBUILDER_BUILD_SECRETS": "FOO=bar,BAZ=qux", "ENVBUILDER_CACHE_REPO": "string", "ENVBUILDER_CACHE_TTL_DAYS": "1", "ENVBUILDER_DEVCONTAINER_DIR": "string",