From bfc2299cffa2ab37861aad7fd144762e082863ae Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Oct 2024 11:55:42 +0000 Subject: [PATCH 1/4] feat: support ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64 --- docs/resources/cached_image.md | 1 + go.mod | 2 +- go.sum | 4 +- internal/provider/cached_image_resource.go | 49 ++++++++++++---------- internal/provider/helpers.go | 5 +++ 5 files changed, 36 insertions(+), 25 deletions(-) diff --git a/docs/resources/cached_image.md b/docs/resources/cached_image.md index a7f4a38..9d53a3e 100644 --- a/docs/resources/cached_image.md +++ b/docs/resources/cached_image.md @@ -37,6 +37,7 @@ The cached image resource can be used to retrieve a cached image produced by env - `git_clone_single_branch` (Boolean) (Envbuilder option) Clone only a single branch of the Git repository. - `git_http_proxy_url` (String) (Envbuilder option) The URL for the HTTP proxy. This is optional. - `git_password` (String, Sensitive) (Envbuilder option) The password to use for Git authentication. This is optional. +- `git_ssh_private_key_base64` (String) (Envbuilder option) Base64 encoded SSH private key to be used for Git authentication. - `git_ssh_private_key_path` (String) (Envbuilder option) Path to an SSH private key to be used for Git authentication. - `git_username` (String) (Envbuilder option) The username to use for Git authentication. This is optional. - `ignore_paths` (List of String) (Envbuilder option) The comma separated list of paths to ignore when building the workspace. diff --git a/go.mod b/go.mod index 2435f51..b961c18 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20240702054557-aa55 require ( github.com/GoogleContainerTools/kaniko v1.9.2 - github.com/coder/envbuilder v1.0.3 + github.com/coder/envbuilder v1.0.4 github.com/coder/serpent v0.8.0 github.com/docker/docker v26.1.5+incompatible github.com/gliderlabs/ssh v0.3.7 diff --git a/go.sum b/go.sum index 1e5903c..cf57857 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352 h1:L/EjCuZxs5tOcqqCaASj/nu65TRYEFcTt8qRQfHZXX0= github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352/go.mod h1:P1KoQSgnKEAG6Mnd3YlGzAophty+yKA9VV48LpfNRvo= -github.com/coder/envbuilder v1.0.3 h1:Ne2s+JBjGXwdO8PoNBEtgIos1a+VFB2iSuOBQHj+cFA= -github.com/coder/envbuilder v1.0.3/go.mod h1:CkP/qKzWmK14I/aSPwEXoEHlss7OqYDmaM8Q7rDhwt8= +github.com/coder/envbuilder v1.0.4 h1:27DFtKhgsode5I0kBPTCHuzG88Zpny27OVvXOuA/zso= +github.com/coder/envbuilder v1.0.4/go.mod h1:CkP/qKzWmK14I/aSPwEXoEHlss7OqYDmaM8Q7rDhwt8= github.com/coder/kaniko v0.0.0-20240925122543-caa18967f374 h1:/cyXf0vTSwFh7evQqeWHXXl14aRfC4CsNIYxOenJytQ= github.com/coder/kaniko v0.0.0-20240925122543-caa18967f374/go.mod h1:XoTDIhNF0Ll4tLmRYdOn31udU9w5zFrY2PME/crSRCA= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= diff --git a/internal/provider/cached_image_resource.go b/internal/provider/cached_image_resource.go index 9378094..5972053 100644 --- a/internal/provider/cached_image_resource.go +++ b/internal/provider/cached_image_resource.go @@ -49,28 +49,29 @@ type CachedImageResourceModel struct { CacheRepo types.String `tfsdk:"cache_repo"` GitURL types.String `tfsdk:"git_url"` // Optional "inputs". - BaseImageCacheDir types.String `tfsdk:"base_image_cache_dir"` - BuildContextPath types.String `tfsdk:"build_context_path"` - CacheTTLDays types.Int64 `tfsdk:"cache_ttl_days"` - DevcontainerDir types.String `tfsdk:"devcontainer_dir"` - DevcontainerJSONPath types.String `tfsdk:"devcontainer_json_path"` - DockerfilePath types.String `tfsdk:"dockerfile_path"` - DockerConfigBase64 types.String `tfsdk:"docker_config_base64"` - ExitOnBuildFailure types.Bool `tfsdk:"exit_on_build_failure"` - ExtraEnv types.Map `tfsdk:"extra_env"` - FallbackImage types.String `tfsdk:"fallback_image"` - GitCloneDepth types.Int64 `tfsdk:"git_clone_depth"` - GitCloneSingleBranch types.Bool `tfsdk:"git_clone_single_branch"` - GitHTTPProxyURL types.String `tfsdk:"git_http_proxy_url"` - GitPassword types.String `tfsdk:"git_password"` - GitSSHPrivateKeyPath types.String `tfsdk:"git_ssh_private_key_path"` - GitUsername types.String `tfsdk:"git_username"` - IgnorePaths types.List `tfsdk:"ignore_paths"` - Insecure types.Bool `tfsdk:"insecure"` - RemoteRepoBuildMode types.Bool `tfsdk:"remote_repo_build_mode"` - SSLCertBase64 types.String `tfsdk:"ssl_cert_base64"` - Verbose types.Bool `tfsdk:"verbose"` - WorkspaceFolder types.String `tfsdk:"workspace_folder"` + BaseImageCacheDir types.String `tfsdk:"base_image_cache_dir"` + BuildContextPath types.String `tfsdk:"build_context_path"` + CacheTTLDays types.Int64 `tfsdk:"cache_ttl_days"` + DevcontainerDir types.String `tfsdk:"devcontainer_dir"` + DevcontainerJSONPath types.String `tfsdk:"devcontainer_json_path"` + DockerfilePath types.String `tfsdk:"dockerfile_path"` + DockerConfigBase64 types.String `tfsdk:"docker_config_base64"` + ExitOnBuildFailure types.Bool `tfsdk:"exit_on_build_failure"` + ExtraEnv types.Map `tfsdk:"extra_env"` + FallbackImage types.String `tfsdk:"fallback_image"` + GitCloneDepth types.Int64 `tfsdk:"git_clone_depth"` + GitCloneSingleBranch types.Bool `tfsdk:"git_clone_single_branch"` + GitHTTPProxyURL types.String `tfsdk:"git_http_proxy_url"` + GitPassword types.String `tfsdk:"git_password"` + GitSSHPrivateKeyPath types.String `tfsdk:"git_ssh_private_key_path"` + GitSSHPrivateKeyBase64 types.String `tfsdk:"git_ssh_private_key_base64"` + GitUsername types.String `tfsdk:"git_username"` + IgnorePaths types.List `tfsdk:"ignore_paths"` + Insecure types.Bool `tfsdk:"insecure"` + RemoteRepoBuildMode types.Bool `tfsdk:"remote_repo_build_mode"` + SSLCertBase64 types.String `tfsdk:"ssl_cert_base64"` + Verbose types.Bool `tfsdk:"verbose"` + WorkspaceFolder types.String `tfsdk:"workspace_folder"` // Computed "outputs". Env types.List `tfsdk:"env"` EnvMap types.Map `tfsdk:"env_map"` @@ -186,6 +187,10 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq MarkdownDescription: "(Envbuilder option) Path to an SSH private key to be used for Git authentication.", Optional: true, }, + "git_ssh_private_key_base64": schema.StringAttribute{ + MarkdownDescription: "(Envbuilder option) Base64 encoded SSH private key to be used for Git authentication.", + Optional: true, + }, "git_username": schema.StringAttribute{ MarkdownDescription: "(Envbuilder option) The username to use for Git authentication. This is optional.", Optional: true, diff --git a/internal/provider/helpers.go b/internal/provider/helpers.go index 21137f1..051a6b0 100644 --- a/internal/provider/helpers.go +++ b/internal/provider/helpers.go @@ -102,6 +102,11 @@ func optionsFromDataModel(data CachedImageResourceModel) (eboptions.Options, dia opts.GitSSHPrivateKeyPath = data.GitSSHPrivateKeyPath.ValueString() } + if !data.GitSSHPrivateKeyBase64.IsNull() { + providerOpts["ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64"] = true + opts.GitSSHPrivateKeyBase64 = data.GitSSHPrivateKeyBase64.ValueString() + } + if !data.GitUsername.IsNull() { providerOpts["ENVBUILDER_GIT_USERNAME"] = true opts.GitUsername = data.GitUsername.ValueString() From 3fd7526e37bd9bb51cefb36d8c686a7baa27d57f Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Oct 2024 12:34:49 +0000 Subject: [PATCH 2/4] chore: mark sensitive, add tests, add diag check --- internal/provider/cached_image_resource.go | 1 + internal/provider/helpers.go | 5 ++ internal/provider/provider_internal_test.go | 53 +++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/internal/provider/cached_image_resource.go b/internal/provider/cached_image_resource.go index 5972053..097b5fd 100644 --- a/internal/provider/cached_image_resource.go +++ b/internal/provider/cached_image_resource.go @@ -190,6 +190,7 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq "git_ssh_private_key_base64": schema.StringAttribute{ MarkdownDescription: "(Envbuilder option) Base64 encoded SSH private key to be used for Git authentication.", Optional: true, + Sensitive: true, }, "git_username": schema.StringAttribute{ MarkdownDescription: "(Envbuilder option) The username to use for Git authentication. This is optional.", diff --git a/internal/provider/helpers.go b/internal/provider/helpers.go index 051a6b0..5d1216d 100644 --- a/internal/provider/helpers.go +++ b/internal/provider/helpers.go @@ -156,6 +156,11 @@ func optionsFromDataModel(data CachedImageResourceModel) (eboptions.Options, dia } diags = append(diags, overrideOptionsFromExtraEnv(&opts, extraEnv, providerOpts)...) + if opts.GitSSHPrivateKeyPath != "" && opts.GitSSHPrivateKeyBase64 != "" { + diags.AddError("Cannot set more than one git ssh private key options", + "Both ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH and ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64 have been set.") + } + return opts, diags } diff --git a/internal/provider/provider_internal_test.go b/internal/provider/provider_internal_test.go index aedfca2..c273a8c 100644 --- a/internal/provider/provider_internal_test.go +++ b/internal/provider/provider_internal_test.go @@ -211,6 +211,59 @@ func Test_optionsFromDataModel(t *testing.T) { }, expectNumErrorDiags: 2, }, + { + name: "errors when git ssh private key path and base64 are set", + data: CachedImageResourceModel{ + BuilderImage: basetypes.NewStringValue("envbuilder:latest"), + CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), + GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), + GitSSHPrivateKeyPath: basetypes.NewStringValue("/tmp/id_rsa"), + GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="), + }, + expectOpts: eboptions.Options{ + CacheRepo: "localhost:5000/cache", + GitURL: "git@git.local/devcontainer.git", + RemoteRepoBuildMode: true, + GitSSHPrivateKeyPath: "/tmp/id_rsa", + GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=", + }, + expectNumErrorDiags: 1, + }, + { + name: "extra_env override errors when git ssh private key path and base64 are set", + data: CachedImageResourceModel{ + BuilderImage: basetypes.NewStringValue("envbuilder:latest"), + CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), + GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), + GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="), + ExtraEnv: extraEnvMap(t, + "ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", "/tmp/id_rsa", + ), + }, + expectOpts: eboptions.Options{ + CacheRepo: "localhost:5000/cache", + GitURL: "git@git.local/devcontainer.git", + RemoteRepoBuildMode: true, + GitSSHPrivateKeyPath: "/tmp/id_rsa", + GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=", + }, + expectNumErrorDiags: 1, + }, + { + name: "required only with base64 ssh key", + data: CachedImageResourceModel{ + BuilderImage: basetypes.NewStringValue("envbuilder:latest"), + CacheRepo: basetypes.NewStringValue("localhost:5000/cache"), + GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"), + GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="), + }, + expectOpts: eboptions.Options{ + CacheRepo: "localhost:5000/cache", + GitURL: "git@git.local/devcontainer.git", + RemoteRepoBuildMode: true, + GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=", + }, + }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() From 6157701f0638e051753990ba6f4441ff73821244 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Oct 2024 12:35:18 +0000 Subject: [PATCH 3/4] fix: update docs --- docs/resources/cached_image.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/resources/cached_image.md b/docs/resources/cached_image.md index 9d53a3e..93923fe 100644 --- a/docs/resources/cached_image.md +++ b/docs/resources/cached_image.md @@ -37,7 +37,7 @@ The cached image resource can be used to retrieve a cached image produced by env - `git_clone_single_branch` (Boolean) (Envbuilder option) Clone only a single branch of the Git repository. - `git_http_proxy_url` (String) (Envbuilder option) The URL for the HTTP proxy. This is optional. - `git_password` (String, Sensitive) (Envbuilder option) The password to use for Git authentication. This is optional. -- `git_ssh_private_key_base64` (String) (Envbuilder option) Base64 encoded SSH private key to be used for Git authentication. +- `git_ssh_private_key_base64` (String, Sensitive) (Envbuilder option) Base64 encoded SSH private key to be used for Git authentication. - `git_ssh_private_key_path` (String) (Envbuilder option) Path to an SSH private key to be used for Git authentication. - `git_username` (String) (Envbuilder option) The username to use for Git authentication. This is optional. - `ignore_paths` (List of String) (Envbuilder option) The comma separated list of paths to ignore when building the workspace. From 524cb272564688f07d6e0282b1b655c6215504bc Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Wed, 30 Oct 2024 14:02:13 +0000 Subject: [PATCH 4/4] chore: apply suggestion from feedback Co-authored-by: Mathias Fredriksson --- internal/provider/helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/helpers.go b/internal/provider/helpers.go index 5d1216d..af4d237 100644 --- a/internal/provider/helpers.go +++ b/internal/provider/helpers.go @@ -157,7 +157,7 @@ func optionsFromDataModel(data CachedImageResourceModel) (eboptions.Options, dia diags = append(diags, overrideOptionsFromExtraEnv(&opts, extraEnv, providerOpts)...) if opts.GitSSHPrivateKeyPath != "" && opts.GitSSHPrivateKeyBase64 != "" { - diags.AddError("Cannot set more than one git ssh private key options", + diags.AddError("Cannot set more than one git ssh private key option", "Both ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH and ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64 have been set.") }