Skip to content

feat: add build secrets option #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/resources/cached_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions internal/provider/cached_image_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 18 additions & 1 deletion internal/provider/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package provider

import (
"fmt"
"slices"
"strings"

eboptions "github.com/coder/envbuilder/options"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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("")
}

Expand Down
36 changes: 25 additions & 11 deletions internal/provider/provider_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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("[email protected]/devcontainer.git"),
BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"),
BuildContextPath: basetypes.NewStringValue("."),
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
GitURL: basetypes.NewStringValue("[email protected]/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"),
Expand All @@ -66,6 +70,7 @@ func Test_optionsFromDataModel(t *testing.T) {
GitURL: "[email protected]/devcontainer.git",
BaseImageCacheDir: "/tmp/cache",
BuildContextPath: ".",
BuildSecrets: []string{"BAZ=qux", "FOO=bar"}, // Sorted
CacheTTLDays: 7,
DevcontainerDir: ".devcontainer",
DevcontainerJSONPath: ".devcontainer/devcontainer.json",
Expand Down Expand Up @@ -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: "[email protected]/devcontainer.git",
RemoteRepoBuildMode: true,
Expand All @@ -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("[email protected]/devcontainer.git"),
BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"),
BuildContextPath: basetypes.NewStringValue("."),
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
GitURL: basetypes.NewStringValue("[email protected]/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"),
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -189,7 +201,7 @@ func Test_optionsFromDataModel(t *testing.T) {
Verbose: false,
WorkspaceFolder: "override",
},
expectNumWarningDiags: 23,
expectNumWarningDiags: 24,
},
{
name: "extra_env override errors",
Expand Down Expand Up @@ -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"},
Expand Down Expand Up @@ -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",
Expand Down
Loading