From e40dd7a0ddd30ba2f590d4fa51e606b09904417a Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 7 Aug 2024 12:59:26 +0100 Subject: [PATCH 1/6] update devcontainer template --- examples/templates/devcontainer-docker/main.tf | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/examples/templates/devcontainer-docker/main.tf b/examples/templates/devcontainer-docker/main.tf index 6e867ea1c12e7..7d263bd359f05 100644 --- a/examples/templates/devcontainer-docker/main.tf +++ b/examples/templates/devcontainer-docker/main.tf @@ -7,11 +7,15 @@ terraform { docker = { source = "kreuzwerker/docker" } + envbuilder = { + source = "coder/envbuilder" + } } } provider "coder" {} provider "docker" {} +provider "envbuilder" {} data "coder_provisioner" "me" {} data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} @@ -145,9 +149,18 @@ resource "docker_volume" "workspaces" { } } +# Check for the presence of a prebuilt image in the cache repo +# that we can use instead. +resource "envbuilder_cached_image" "cached" { + count = data.coder_workspace.me.start_count + builder_image = local.devcontainer_builder_image + git_url = local.repo_url + cache_repo = var.cache_repo +} + resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count - image = local.devcontainer_builder_image + image = envbuilder_cached_image.cached.0.image # Uses lower() to avoid Docker restriction on container names. name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" # Hostname makes the shell more user friendly: coder@my-workspace:~$ @@ -161,7 +174,10 @@ resource "docker_container" "workspace" { "ENVBUILDER_FALLBACK_IMAGE=${data.coder_parameter.fallback_image.value}", "ENVBUILDER_CACHE_REPO=${var.cache_repo}", "ENVBUILDER_DOCKER_CONFIG_BASE64=${try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, "")}", + "ENVBUILDER_PUSH_IMAGE=${var.cache_repo != "" ? "true" : ""}", + #"ENVBUILDER_INSECURE=true", # Uncomment if testing with a local registry. ] + # network_mode = "host" # Uncomment if testing with a local registry. host { host = "host.docker.internal" ip = "host-gateway" From 3f37703c732e42f01a08b2ffb75d2a51fd99dd29 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 13 Aug 2024 15:21:33 +0100 Subject: [PATCH 2/6] updates after testing with new provider version --- .../templates/devcontainer-docker/README.md | 8 +++-- .../templates/devcontainer-docker/main.tf | 32 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/examples/templates/devcontainer-docker/README.md b/examples/templates/devcontainer-docker/README.md index 4d6ad990d1839..45adec9d60f9f 100644 --- a/examples/templates/devcontainer-docker/README.md +++ b/examples/templates/devcontainer-docker/README.md @@ -34,11 +34,12 @@ Coder supports Devcontainers via [envbuilder](https://github.com/coder/envbuilde This template provisions the following resources: +- Envbuilder cached image (conditional, persistent) - Docker image (persistent) - Docker container (ephemeral) - Docker volume (persistent on `/workspaces`) -with [`envbuilder`](https://github.com/coder/envbuilder). +with [`envbuilder`](https://github.com/coder/envbuilder) and [`terraform-provider-envbuilder`](https://github.com/coder/terraform-provider-envbuilder). The Git repository is cloned inside the `/workspaces` volume if not present. Any local changes to the Devcontainer files inside the volume will be applied when you restart the workspace. Keep in mind that any tools or files outside of `/workspaces` or not added as part of the Devcontainer specification are not persisted. @@ -51,10 +52,11 @@ Edit the `devcontainer.json` instead! See the [Envbuilder documentation](https://github.com/coder/envbuilder/blob/main/docs/docker.md) for information on running Docker containers inside a devcontainer built by Envbuilder. + ## Caching To speed up your builds, you can use a container registry as a cache. -When creating the template, set the parameter `cache_repo`. +When creating the template, set the parameter `cache_repo` to a valid Docker repository. For example, you can run a local registry: @@ -69,6 +71,8 @@ docker run --detach \ Then, when creating the template, enter `localhost:5000/devcontainer-cache` for the parameter `cache_repo`. +See the [Envbuilder Terraform Provider Examples](https://github.com/coder/terraform-provider-envbuilder/blob/main/examples/resources/envbuilder_cached_image/envbuilder_cached_image_resource.tf/) for a more complete example of how the provider works. + > [!NOTE] We recommend using a registry cache with authentication enabled. > To allow Envbuilder to authenticate with the registry cache, specify the variable `cache_repo_docker_config_path` > with the path to a Docker config `.json` on disk containing valid credentials for the registry. diff --git a/examples/templates/devcontainer-docker/main.tf b/examples/templates/devcontainer-docker/main.tf index 7d263bd359f05..c6c885e3a51ee 100644 --- a/examples/templates/devcontainer-docker/main.tf +++ b/examples/templates/devcontainer-docker/main.tf @@ -93,14 +93,13 @@ EOF variable "cache_repo" { default = "" - description = "Use a container registry as a cache to speed up builds." - sensitive = true + description = "(Optional) Use a container registry as a cache to speed up builds." type = string } variable "cache_repo_docker_config_path" { default = "" - description = "Path to a docker config.json containing credentials to the provided cache repo, if required." + description = "(Optional) Path to a docker config.json containing credentials to the provided cache repo, if required." sensitive = true type = string } @@ -152,7 +151,7 @@ resource "docker_volume" "workspaces" { # Check for the presence of a prebuilt image in the cache repo # that we can use instead. resource "envbuilder_cached_image" "cached" { - count = data.coder_workspace.me.start_count + count = var.cache_repo == "" ? 0 : data.coder_workspace.me.start_count builder_image = local.devcontainer_builder_image git_url = local.repo_url cache_repo = var.cache_repo @@ -160,7 +159,7 @@ resource "envbuilder_cached_image" "cached" { resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count - image = envbuilder_cached_image.cached.0.image + image = var.cache_repo == "" ? local.devcontainer_builder_image : envbuilder_cached_image.cached.0.image # Uses lower() to avoid Docker restriction on container names. name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" # Hostname makes the shell more user friendly: coder@my-workspace:~$ @@ -174,10 +173,10 @@ resource "docker_container" "workspace" { "ENVBUILDER_FALLBACK_IMAGE=${data.coder_parameter.fallback_image.value}", "ENVBUILDER_CACHE_REPO=${var.cache_repo}", "ENVBUILDER_DOCKER_CONFIG_BASE64=${try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, "")}", - "ENVBUILDER_PUSH_IMAGE=${var.cache_repo != "" ? "true" : ""}", - #"ENVBUILDER_INSECURE=true", # Uncomment if testing with a local registry. + "ENVBUILDER_PUSH_IMAGE=${var.cache_repo == "" ? "" : "true"}", + #"ENVBUILDER_INSECURE=true", # Uncomment if testing with a registry running on `localhost`. ] - # network_mode = "host" # Uncomment if testing with a local registry. + # network_mode = "host" # Uncomment if testing with a registry running on `localhost`. host { host = "host.docker.internal" ip = "host-gateway" @@ -314,3 +313,20 @@ resource "coder_app" "code-server" { threshold = 6 } } + +resource "coder_metadata" "container_info" { + count = data.coder_workspace.me.start_count + resource_id = docker_container.workspace.0.id + item { + key = "workspace image" + value = var.cache_repo == "" ? local.devcontainer_builder_image : envbuilder_cached_image.cached.0.image + } + item { + key = "git url" + value = local.repo_url + } + item { + key = "cache repo" + value = var.cache_repo == "" ? "not enabled" : var.cache_repo + } +} From b74e9845f46e8dec3f8f69ff35f7c1856a6c2e16 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 13 Aug 2024 15:24:58 +0100 Subject: [PATCH 3/6] make fmt --- examples/templates/devcontainer-docker/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/templates/devcontainer-docker/README.md b/examples/templates/devcontainer-docker/README.md index 45adec9d60f9f..536f693aa8b3a 100644 --- a/examples/templates/devcontainer-docker/README.md +++ b/examples/templates/devcontainer-docker/README.md @@ -52,7 +52,6 @@ Edit the `devcontainer.json` instead! See the [Envbuilder documentation](https://github.com/coder/envbuilder/blob/main/docs/docker.md) for information on running Docker containers inside a devcontainer built by Envbuilder. - ## Caching To speed up your builds, you can use a container registry as a cache. From 1ea88abd1401a6da50eaa715dea226787922b47f Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 13 Aug 2024 16:54:57 +0100 Subject: [PATCH 4/6] convert docker_env to map --- .../templates/devcontainer-docker/main.tf | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/examples/templates/devcontainer-docker/main.tf b/examples/templates/devcontainer-docker/main.tf index c6c885e3a51ee..12dc99533521d 100644 --- a/examples/templates/devcontainer-docker/main.tf +++ b/examples/templates/devcontainer-docker/main.tf @@ -110,6 +110,24 @@ locals { git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) git_author_email = data.coder_workspace_owner.me.email repo_url = data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value + # The envbuilder provider requires a key-value map of environment variables. + envbuilder_env = { + "CODER_AGENT_TOKEN": coder_agent.main.token, + # Use the docker gateway if the access URL is 127.0.0.1 + "CODER_AGENT_URL": replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), + "ENVBUILDER_GIT_URL": local.repo_url, + # Use the docker gateway if the access URL is 127.0.0.1 + "ENVBUILDER_INIT_SCRIPT": replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), + "ENVBUILDER_FALLBACK_IMAGE": data.coder_parameter.fallback_image.value, + "ENVBUILDER_CACHE_REPO": var.cache_repo, + "ENVBUILDER_DOCKER_CONFIG_BASE64": try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, ""), + "ENVBUILDER_PUSH_IMAGE": var.cache_repo == "" ? "" : "true", + #"ENVBUILDER_INSECURE": "true", # Uncomment if testing with an insecure registry. + } + # Convert the above map to the format expected by the docker provider. + docker_env = [ + for k, v in local.envbuilder_env : "${k}=${v}" + ] } data "local_sensitive_file" "cache_repo_dockerconfigjson" { @@ -155,6 +173,8 @@ resource "envbuilder_cached_image" "cached" { builder_image = local.devcontainer_builder_image git_url = local.repo_url cache_repo = var.cache_repo + extra_env = local.envbuilder_env + #insecure = true # Uncomment if testing with an insecure registry. } resource "docker_container" "workspace" { @@ -164,18 +184,10 @@ resource "docker_container" "workspace" { name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}" # Hostname makes the shell more user friendly: coder@my-workspace:~$ hostname = data.coder_workspace.me.name - # Use the docker gateway if the access URL is 127.0.0.1 - env = [ - "CODER_AGENT_TOKEN=${coder_agent.main.token}", - "CODER_AGENT_URL=${replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}", - "ENVBUILDER_GIT_URL=${local.repo_url}", - "ENVBUILDER_INIT_SCRIPT=${replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal")}", - "ENVBUILDER_FALLBACK_IMAGE=${data.coder_parameter.fallback_image.value}", - "ENVBUILDER_CACHE_REPO=${var.cache_repo}", - "ENVBUILDER_DOCKER_CONFIG_BASE64=${try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, "")}", - "ENVBUILDER_PUSH_IMAGE=${var.cache_repo == "" ? "" : "true"}", - #"ENVBUILDER_INSECURE=true", # Uncomment if testing with a registry running on `localhost`. - ] + # Use the environment specified by the envbuilder provider, if available. + # FIXME: https://github.com/coder/terraform-provider-envbuilder/issues/31 + #env = var.cache_repo == "" ? local.docker_env : envbuilder_cached_image.cached.0.env + env = local.docker_env # network_mode = "host" # Uncomment if testing with a registry running on `localhost`. host { host = "host.docker.internal" From 08c04eaf457040bf7cd2b9f6884d14d01a54fd72 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Tue, 13 Aug 2024 16:56:51 +0100 Subject: [PATCH 5/6] fumpt --- .../templates/devcontainer-docker/main.tf | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/templates/devcontainer-docker/main.tf b/examples/templates/devcontainer-docker/main.tf index 12dc99533521d..e4f7f505eeabe 100644 --- a/examples/templates/devcontainer-docker/main.tf +++ b/examples/templates/devcontainer-docker/main.tf @@ -112,16 +112,16 @@ locals { repo_url = data.coder_parameter.repo.value == "custom" ? data.coder_parameter.custom_repo_url.value : data.coder_parameter.repo.value # The envbuilder provider requires a key-value map of environment variables. envbuilder_env = { - "CODER_AGENT_TOKEN": coder_agent.main.token, - # Use the docker gateway if the access URL is 127.0.0.1 - "CODER_AGENT_URL": replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), - "ENVBUILDER_GIT_URL": local.repo_url, - # Use the docker gateway if the access URL is 127.0.0.1 - "ENVBUILDER_INIT_SCRIPT": replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), - "ENVBUILDER_FALLBACK_IMAGE": data.coder_parameter.fallback_image.value, - "ENVBUILDER_CACHE_REPO": var.cache_repo, - "ENVBUILDER_DOCKER_CONFIG_BASE64": try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, ""), - "ENVBUILDER_PUSH_IMAGE": var.cache_repo == "" ? "" : "true", + "CODER_AGENT_TOKEN" : coder_agent.main.token, + # Use the docker gateway if the access URL is 127.0.0.1 + "CODER_AGENT_URL" : replace(data.coder_workspace.me.access_url, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), + "ENVBUILDER_GIT_URL" : local.repo_url, + # Use the docker gateway if the access URL is 127.0.0.1 + "ENVBUILDER_INIT_SCRIPT" : replace(coder_agent.main.init_script, "/localhost|127\\.0\\.0\\.1/", "host.docker.internal"), + "ENVBUILDER_FALLBACK_IMAGE" : data.coder_parameter.fallback_image.value, + "ENVBUILDER_CACHE_REPO" : var.cache_repo, + "ENVBUILDER_DOCKER_CONFIG_BASE64" : try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, ""), + "ENVBUILDER_PUSH_IMAGE" : var.cache_repo == "" ? "" : "true", #"ENVBUILDER_INSECURE": "true", # Uncomment if testing with an insecure registry. } # Convert the above map to the format expected by the docker provider. @@ -173,7 +173,7 @@ resource "envbuilder_cached_image" "cached" { builder_image = local.devcontainer_builder_image git_url = local.repo_url cache_repo = var.cache_repo - extra_env = local.envbuilder_env + extra_env = local.envbuilder_env #insecure = true # Uncomment if testing with an insecure registry. } From 40e4e12d071a82ef866c78f5d259ac68742a7835 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Wed, 14 Aug 2024 10:26:29 +0100 Subject: [PATCH 6/6] make insecure a variable, fix coder metadata resource_id --- examples/templates/devcontainer-docker/main.tf | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/templates/devcontainer-docker/main.tf b/examples/templates/devcontainer-docker/main.tf index e4f7f505eeabe..0393b15004dda 100644 --- a/examples/templates/devcontainer-docker/main.tf +++ b/examples/templates/devcontainer-docker/main.tf @@ -97,6 +97,12 @@ variable "cache_repo" { type = string } +variable "insecure_cache_repo" { + default = false + description = "Enable this option if your cache registry does not serve HTTPS." + type = bool +} + variable "cache_repo_docker_config_path" { default = "" description = "(Optional) Path to a docker config.json containing credentials to the provided cache repo, if required." @@ -122,7 +128,7 @@ locals { "ENVBUILDER_CACHE_REPO" : var.cache_repo, "ENVBUILDER_DOCKER_CONFIG_BASE64" : try(data.local_sensitive_file.cache_repo_dockerconfigjson[0].content_base64, ""), "ENVBUILDER_PUSH_IMAGE" : var.cache_repo == "" ? "" : "true", - #"ENVBUILDER_INSECURE": "true", # Uncomment if testing with an insecure registry. + "ENVBUILDER_INSECURE" : "${var.insecure_cache_repo}", } # Convert the above map to the format expected by the docker provider. docker_env = [ @@ -174,7 +180,7 @@ resource "envbuilder_cached_image" "cached" { git_url = local.repo_url cache_repo = var.cache_repo extra_env = local.envbuilder_env - #insecure = true # Uncomment if testing with an insecure registry. + insecure = var.insecure_cache_repo } resource "docker_container" "workspace" { @@ -328,7 +334,7 @@ resource "coder_app" "code-server" { resource "coder_metadata" "container_info" { count = data.coder_workspace.me.start_count - resource_id = docker_container.workspace.0.id + resource_id = coder_agent.main.id item { key = "workspace image" value = var.cache_repo == "" ? local.devcontainer_builder_image : envbuilder_cached_image.cached.0.image