Skip to content

chore(examples): update devcontainer-docker template #14199

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 6 commits into from
Aug 14, 2024
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
7 changes: 5 additions & 2 deletions examples/templates/devcontainer-docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:

Expand All @@ -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.
78 changes: 64 additions & 14 deletions examples/templates/devcontainer-docker/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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" {}
Expand Down Expand Up @@ -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."
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review: this shouldn't contain any credentials and it's kind of annoying to have this sensitive

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually sensitive if it's just the path to a file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷 maybe? The nice thing about making the file sensitive is that the content automatically becomes sensitive. (That's my recollection anyhow)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so there’s some magic there wrt content? If so please keep it as is 👍

type = string
}
Expand All @@ -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}",
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are fed in via extra env, shouldn't they be included in the computed env the resource outputs? (In which case the below would be unnecessary?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I'm going to keep it like this until coder/terraform-provider-envbuilder#31 is fixed.

# 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" {
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make these optional and let them be provided via extra env too? Would simplify a bit and a user knows they can just cram everything in extra.

Right now for url is given here and in extra, what if the values differ? What's the behavior? (IMO maybe it's an error).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we could validate at runtime that extra_env does not contain any duplicated variables from the inputs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that’s what I had in mind, but in a more roundabout way 😅. I think it would help with avoiding mistakes, and remove ambiguity.

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"
Expand Down Expand Up @@ -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
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition ❤️

}
Loading