diff --git a/examples/templates/devcontainer-docker/README.md b/examples/templates/devcontainer-docker/README.md index 4d6ad990d1839..536f693aa8b3a 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. @@ -54,7 +55,7 @@ See the [Envbuilder documentation](https://github.com/coder/envbuilder/blob/main ## 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 +70,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 6e867ea1c12e7..0393b15004dda 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" {} @@ -89,14 +93,19 @@ 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 "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 = "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 } @@ -107,6 +116,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" : "${var.insecure_cache_repo}", + } + # 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" { @@ -145,23 +172,29 @@ 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 = 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 + extra_env = local.envbuilder_env + insecure = var.insecure_cache_repo +} + resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count - image = local.devcontainer_builder_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:~$ 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, "")}", - ] + # 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" ip = "host-gateway" @@ -298,3 +331,20 @@ resource "coder_app" "code-server" { threshold = 6 } } + +resource "coder_metadata" "container_info" { + count = data.coder_workspace.me.start_count + resource_id = coder_agent.main.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 + } +}