diff --git a/docs/resources/config.md b/docs/resources/config.md index ab619530..473fef83 100644 --- a/docs/resources/config.md +++ b/docs/resources/config.md @@ -10,7 +10,14 @@ description: |- +## Example Usage +```terraform +resource "docker_config" "foo_config" { + name = "foo_config" + data = "ewogICJzZXJIfQo=" +} +``` ## Schema @@ -24,4 +31,11 @@ description: |- - **id** (String) The ID of this resource. +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash +$ terraform import docker_config.foo "$(docker config inspect -f {{.ID}} p73)" +``` diff --git a/docs/resources/container.md b/docs/resources/container.md index 9f69ac2b..d04ff433 100644 --- a/docs/resources/container.md +++ b/docs/resources/container.md @@ -10,7 +10,20 @@ description: |- +## Example Usage +```terraform +# Start a container +resource "docker_container" "ubuntu" { + name = "foo" + image = docker_image.ubuntu.latest +} + +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + name = "ubuntu:precise" +} +``` ## Schema @@ -276,4 +289,11 @@ Read-Only: - **ipv6_gateway** (String) - **network_name** (String) +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash +$ terraform import docker_container.foo "$(docker inspect -f {.ID}} foo)" +``` diff --git a/docs/resources/image.md b/docs/resources/image.md index 2793ea9a..59a5608b 100644 --- a/docs/resources/image.md +++ b/docs/resources/image.md @@ -10,7 +10,41 @@ description: |- +## Example Usage +```terraform +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + name = "ubuntu:precise" +} + +# Access it somewhere else with ${docker_image.ubuntu.latest} + +# image "zoo" and "zoo:develop" are built +resource "docker_image" "zoo" { + name = "zoo" + build { + path = "." + tag = ["zoo:develop"] + build_arg = { + foo : "zoo" + } + label = { + author : "zoo" + } + } +} + +# Dynamic image +data "docker_registry_image" "ubuntu" { + name = "ubuntu:precise" +} + +resource "docker_image" "ubuntu" { + name = data.docker_registry_image.ubuntu.name + pull_triggers = [data.docker_registry_image.ubuntu.sha256_digest] +} +``` ## Schema @@ -51,4 +85,11 @@ Optional: - **tag** (List of String) Name and optionally a tag in the 'name:tag' format - **target** (String) Set the target build stage to build +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash +# TODO +``` diff --git a/docs/resources/registry_image.md b/docs/resources/registry_image.md index 7cdb8618..615da4c7 100644 --- a/docs/resources/registry_image.md +++ b/docs/resources/registry_image.md @@ -10,7 +10,17 @@ description: |- +## Example Usage +```terraform +resource "docker_registry_image" "helloworld" { + name = "helloworld:1.0" + + build { + context = "pathToContextFolder" + } +} +``` ## Schema @@ -97,4 +107,11 @@ Required: - **name** (String) - **soft** (Number) +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash +# TODO +``` diff --git a/docs/resources/secret.md b/docs/resources/secret.md index 35159f3c..4195eb55 100644 --- a/docs/resources/secret.md +++ b/docs/resources/secret.md @@ -10,7 +10,43 @@ description: |- +## Example Usage +```terraform +# Creates a secret +resource "docker_secret" "foo_secret" { + name = "foo_secret" + data = "ewogICJzZXJsaasIfQo=" +} + +# Update secret with no downtime +## To update a `secret`, Terraform will destroy the existing resource and create a replacement. +## To effectively use a `docker_secret` resource with a `docker_service` resource, +## it's recommended to specify `create_before_destroy` in a `lifecycle` block. Provide a unique `name` attribute, for example +## with one of the interpolation functions `uuid` or `timestamp` as shown +## in the example below. The reason is [moby-35803](https://github.com/moby/moby/issues/35803). + +resource "docker_secret" "service_secret" { + name = "${var.service_name}-secret-${replace(timestamp(), ":", ".")}" + data = base64encode(data.template_file.service_secret_tpl.rendered) + + lifecycle { + ignore_changes = ["name"] + create_before_destroy = true + } +} + +resource "docker_service" "service" { + # ... + secrets = [ + { + secret_id = docker_secret.service_secret.id + secret_name = docker_secret.service_secret.name + file_name = "/root/configs/configs.json" + }, + ] +} +``` ## Schema @@ -33,4 +69,12 @@ Required: - **label** (String) Name of the label - **value** (String) Value of the label +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash + +# Docker secret cannot be imported as the secret data, once set, is never exposed again. +``` diff --git a/docs/resources/service.md b/docs/resources/service.md index 13c052e9..36b90fa8 100644 --- a/docs/resources/service.md +++ b/docs/resources/service.md @@ -10,7 +10,250 @@ description: |- +## Example Usage +```terraform +# Basic +## The following configuration starts a Docker Service with +## - the given image, +## - 1 replica +## - exposes the port `8080` in `vip` mode to the host machine +## - moreover, uses the `container` runtime + +resource "docker_service" "foo" { + name = "foo-service" + + task_spec { + container_spec { + image = "repo.mycompany.com:8080/foo-service:v1" + } + } + + endpoint_spec { + ports { + target_port = "8080" + } + } +} + +# The following command is the equivalent: +# docker service create -d -p 8080 --name foo-service repo.mycompany.com:8080/foo-service:v1 + +# Advanced +## The following configuration shows the full capabilities of a Docker Service. +# Currently, the [Docker API 1.32](https://docs.docker.com/engine/api/v1.32) is implemented. + +resource "docker_volume" "test_volume" { + name = "tftest-volume" +} + +resource "docker_config" "service_config" { + name = "tftest-full-myconfig" + data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" +} + +resource "docker_secret" "service_secret" { + name = "tftest-mysecret" + data = "ewogICJrZXkiOiAiUVdFUlRZIgp9" +} + +resource "docker_network" "test_network" { + name = "tftest-network" + driver = "overlay" +} + +resource "docker_service" "foo" { + name = "tftest-service-basic" + + task_spec { + container_spec { + image = "repo.mycompany.com:8080/foo-service:v1" + + labels { + label = "foo.bar" + value = "baz" + } + + command = ["ls"] + args = ["-las"] + hostname = "my-fancy-service" + + env = { + MYFOO = "BAR" + } + + dir = "/root" + user = "root" + groups = ["docker", "foogroup"] + + privileges { + se_linux_context { + disable = true + user = "user-label" + role = "role-label" + type = "type-label" + level = "level-label" + } + } + + read_only = true + + mounts { + target = "/mount/test" + source = docker_volume.test_volume.name + type = "volume" + read_only = true + + bind_options { + propagation = "private" + } + } + + mounts { + # another mount + } + + stop_signal = "SIGTERM" + stop_grace_period = "10s" + + healthcheck { + test = ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval = "5s" + timeout = "2s" + retries = 4 + } + + hosts { + host = "testhost" + ip = "10.0.1.0" + } + + dns_config { + nameservers = ["8.8.8.8"] + search = ["example.org"] + options = ["timeout:3"] + } + + secrets { + secret_id = docker_secret.service_secret.id + secret_name = docker_secret.service_secret.name + file_name = "/secrets.json" + file_uid = "0" + file_gid = "0" + file_mode = 0777 + } + + secrets { + # another secret + } + + configs { + config_id = docker_config.service_config.id + config_name = docker_config.service_config.name + file_name = "/configs.json" + } + + configs { + # another config + } + } + + resources { + limits { + nano_cpus = 1000000 + memory_bytes = 536870912 + } + + reservation { + nano_cpus = 1000000 + memory_bytes = 536870912 + + generic_resources { + named_resources_spec = [ + "GPU=UUID1", + ] + + discrete_resources_spec = [ + "SSD=3", + ] + } + } + } + + restart_policy = { + condition = "on-failure" + delay = "3s" + max_attempts = 4 + window = "10s" + } + + placement { + constraints = [ + "node.role==manager", + ] + + prefs = [ + "spread=node.role.manager", + ] + + max_replicas = 1 + } + + force_update = 0 + runtime = "container" + networks = [docker_network.test_network.id] + + log_driver { + name = "json-file" + + options { + max-size = "10m" + max-file = "3" + } + } + } + + mode { + replicated { + replicas = 2 + } + } + + update_config { + parallelism = 2 + delay = "10s" + failure_action = "pause" + monitor = "5s" + max_failure_ratio = "0.1" + order = "start-first" + } + + rollback_config { + parallelism = 2 + delay = "5ms" + failure_action = "pause" + monitor = "10h" + max_failure_ratio = "0.9" + order = "stop-first" + } + + endpoint_spec { + mode = "vip" + + ports { + name = "random" + protocol = "tcp" + target_port = "8080" + published_port = "8080" + publish_mode = "ingress" + } + + ports { + # another port + } + } +} +``` ## Schema @@ -418,4 +661,15 @@ Optional: - **order** (String) Update order: either 'stop-first' or 'start-first' - **parallelism** (Number) Maximum number of tasks to be updated in one iteration +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash + +## A Docker service can be imported using the long id, +## e.g. for a service with the short id `55ba873dd`: + +$ terraform import docker_service.foo "$(docker service inspect -f {{.ID}} 55b)" +``` diff --git a/docs/resources/volume.md b/docs/resources/volume.md index 4309556e..ccf661f7 100644 --- a/docs/resources/volume.md +++ b/docs/resources/volume.md @@ -10,7 +10,16 @@ description: |- +## Example Usage +```terraform +# Creates a docker volume "shared_volume". +resource "docker_volume" "shared_volume" { + name = "shared_volume" +} + +# Reference the volume with ${docker_volume.shared_volume.name} +``` ## Schema @@ -35,4 +44,15 @@ Required: - **label** (String) Name of the label - **value** (String) Value of the label +## Import +Import is supported using the following syntax: + +```shell +#!/bin/bash + +# Docker volume can be imported using the long id, +# e.g. for a volume with the short id `ecae276c5`: + +terraform import docker_volume.foo "$(docker volume inspect -f {{.ID}} eca)" +``` diff --git a/examples/data-sources/registry_image/data-source.tf b/examples/data-sources/registry_image/data-source.tf new file mode 100644 index 00000000..f226d2ea --- /dev/null +++ b/examples/data-sources/registry_image/data-source.tf @@ -0,0 +1,8 @@ +data "docker_registry_image" "ubuntu" { + name = "ubuntu:precise" +} + +resource "docker_image" "ubuntu" { + name = data.docker_registry_image.ubuntu.name + pull_triggers = [data.docker_registry_image.ubuntu.sha256_digest] +} diff --git a/examples/resources/docker_config/import.sh b/examples/resources/docker_config/import.sh new file mode 100644 index 00000000..e9820ce3 --- /dev/null +++ b/examples/resources/docker_config/import.sh @@ -0,0 +1,2 @@ +#!/bin/bash +$ terraform import docker_config.foo "$(docker config inspect -f {{.ID}} p73)" \ No newline at end of file diff --git a/examples/resources/docker_config/resource.tf b/examples/resources/docker_config/resource.tf new file mode 100644 index 00000000..333477c6 --- /dev/null +++ b/examples/resources/docker_config/resource.tf @@ -0,0 +1,4 @@ +resource "docker_config" "foo_config" { + name = "foo_config" + data = "ewogICJzZXJIfQo=" +} \ No newline at end of file diff --git a/examples/resources/docker_container/import.sh b/examples/resources/docker_container/import.sh new file mode 100644 index 00000000..17f39d0e --- /dev/null +++ b/examples/resources/docker_container/import.sh @@ -0,0 +1,2 @@ +#!/bin/bash +$ terraform import docker_container.foo "$(docker inspect -f {.ID}} foo)" \ No newline at end of file diff --git a/examples/resources/docker_container/resource.tf b/examples/resources/docker_container/resource.tf new file mode 100644 index 00000000..d7f2bd21 --- /dev/null +++ b/examples/resources/docker_container/resource.tf @@ -0,0 +1,10 @@ +# Start a container +resource "docker_container" "ubuntu" { + name = "foo" + image = docker_image.ubuntu.latest +} + +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + name = "ubuntu:precise" +} diff --git a/examples/resources/docker_image/import.sh b/examples/resources/docker_image/import.sh new file mode 100644 index 00000000..74c27dbf --- /dev/null +++ b/examples/resources/docker_image/import.sh @@ -0,0 +1,2 @@ +#!/bin/bash +# TODO \ No newline at end of file diff --git a/examples/resources/docker_image/resource.tf b/examples/resources/docker_image/resource.tf new file mode 100644 index 00000000..a690f691 --- /dev/null +++ b/examples/resources/docker_image/resource.tf @@ -0,0 +1,31 @@ +# Find the latest Ubuntu precise image. +resource "docker_image" "ubuntu" { + name = "ubuntu:precise" +} + +# Access it somewhere else with ${docker_image.ubuntu.latest} + +# image "zoo" and "zoo:develop" are built +resource "docker_image" "zoo" { + name = "zoo" + build { + path = "." + tag = ["zoo:develop"] + build_arg = { + foo : "zoo" + } + label = { + author : "zoo" + } + } +} + +# Dynamic image +data "docker_registry_image" "ubuntu" { + name = "ubuntu:precise" +} + +resource "docker_image" "ubuntu" { + name = data.docker_registry_image.ubuntu.name + pull_triggers = [data.docker_registry_image.ubuntu.sha256_digest] +} \ No newline at end of file diff --git a/examples/resources/docker_registry_image/import.sh b/examples/resources/docker_registry_image/import.sh new file mode 100644 index 00000000..74c27dbf --- /dev/null +++ b/examples/resources/docker_registry_image/import.sh @@ -0,0 +1,2 @@ +#!/bin/bash +# TODO \ No newline at end of file diff --git a/examples/resources/docker_registry_image/resource.tf b/examples/resources/docker_registry_image/resource.tf new file mode 100644 index 00000000..665cc25d --- /dev/null +++ b/examples/resources/docker_registry_image/resource.tf @@ -0,0 +1,7 @@ +resource "docker_registry_image" "helloworld" { + name = "helloworld:1.0" + + build { + context = "pathToContextFolder" + } +} \ No newline at end of file diff --git a/examples/resources/docker_secret/import.sh b/examples/resources/docker_secret/import.sh new file mode 100644 index 00000000..00ca3010 --- /dev/null +++ b/examples/resources/docker_secret/import.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +# Docker secret cannot be imported as the secret data, once set, is never exposed again. \ No newline at end of file diff --git a/examples/resources/docker_secret/resource.tf b/examples/resources/docker_secret/resource.tf new file mode 100644 index 00000000..7914205f --- /dev/null +++ b/examples/resources/docker_secret/resource.tf @@ -0,0 +1,33 @@ +# Creates a secret +resource "docker_secret" "foo_secret" { + name = "foo_secret" + data = "ewogICJzZXJsaasIfQo=" +} + +# Update secret with no downtime +## To update a `secret`, Terraform will destroy the existing resource and create a replacement. +## To effectively use a `docker_secret` resource with a `docker_service` resource, +## it's recommended to specify `create_before_destroy` in a `lifecycle` block. Provide a unique `name` attribute, for example +## with one of the interpolation functions `uuid` or `timestamp` as shown +## in the example below. The reason is [moby-35803](https://github.com/moby/moby/issues/35803). + +resource "docker_secret" "service_secret" { + name = "${var.service_name}-secret-${replace(timestamp(), ":", ".")}" + data = base64encode(data.template_file.service_secret_tpl.rendered) + + lifecycle { + ignore_changes = ["name"] + create_before_destroy = true + } +} + +resource "docker_service" "service" { + # ... + secrets = [ + { + secret_id = docker_secret.service_secret.id + secret_name = docker_secret.service_secret.name + file_name = "/root/configs/configs.json" + }, + ] +} diff --git a/examples/resources/docker_service/import.sh b/examples/resources/docker_service/import.sh new file mode 100644 index 00000000..b0da9685 --- /dev/null +++ b/examples/resources/docker_service/import.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +## A Docker service can be imported using the long id, +## e.g. for a service with the short id `55ba873dd`: + +$ terraform import docker_service.foo "$(docker service inspect -f {{.ID}} 55b)" \ No newline at end of file diff --git a/examples/resources/docker_service/resource.tf b/examples/resources/docker_service/resource.tf new file mode 100644 index 00000000..f37f8427 --- /dev/null +++ b/examples/resources/docker_service/resource.tf @@ -0,0 +1,240 @@ +# Basic +## The following configuration starts a Docker Service with +## - the given image, +## - 1 replica +## - exposes the port `8080` in `vip` mode to the host machine +## - moreover, uses the `container` runtime + +resource "docker_service" "foo" { + name = "foo-service" + + task_spec { + container_spec { + image = "repo.mycompany.com:8080/foo-service:v1" + } + } + + endpoint_spec { + ports { + target_port = "8080" + } + } +} + +# The following command is the equivalent: +# docker service create -d -p 8080 --name foo-service repo.mycompany.com:8080/foo-service:v1 + +# Advanced +## The following configuration shows the full capabilities of a Docker Service. +# Currently, the [Docker API 1.32](https://docs.docker.com/engine/api/v1.32) is implemented. + +resource "docker_volume" "test_volume" { + name = "tftest-volume" +} + +resource "docker_config" "service_config" { + name = "tftest-full-myconfig" + data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" +} + +resource "docker_secret" "service_secret" { + name = "tftest-mysecret" + data = "ewogICJrZXkiOiAiUVdFUlRZIgp9" +} + +resource "docker_network" "test_network" { + name = "tftest-network" + driver = "overlay" +} + +resource "docker_service" "foo" { + name = "tftest-service-basic" + + task_spec { + container_spec { + image = "repo.mycompany.com:8080/foo-service:v1" + + labels { + label = "foo.bar" + value = "baz" + } + + command = ["ls"] + args = ["-las"] + hostname = "my-fancy-service" + + env = { + MYFOO = "BAR" + } + + dir = "/root" + user = "root" + groups = ["docker", "foogroup"] + + privileges { + se_linux_context { + disable = true + user = "user-label" + role = "role-label" + type = "type-label" + level = "level-label" + } + } + + read_only = true + + mounts { + target = "/mount/test" + source = docker_volume.test_volume.name + type = "volume" + read_only = true + + bind_options { + propagation = "private" + } + } + + mounts { + # another mount + } + + stop_signal = "SIGTERM" + stop_grace_period = "10s" + + healthcheck { + test = ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval = "5s" + timeout = "2s" + retries = 4 + } + + hosts { + host = "testhost" + ip = "10.0.1.0" + } + + dns_config { + nameservers = ["8.8.8.8"] + search = ["example.org"] + options = ["timeout:3"] + } + + secrets { + secret_id = docker_secret.service_secret.id + secret_name = docker_secret.service_secret.name + file_name = "/secrets.json" + file_uid = "0" + file_gid = "0" + file_mode = 0777 + } + + secrets { + # another secret + } + + configs { + config_id = docker_config.service_config.id + config_name = docker_config.service_config.name + file_name = "/configs.json" + } + + configs { + # another config + } + } + + resources { + limits { + nano_cpus = 1000000 + memory_bytes = 536870912 + } + + reservation { + nano_cpus = 1000000 + memory_bytes = 536870912 + + generic_resources { + named_resources_spec = [ + "GPU=UUID1", + ] + + discrete_resources_spec = [ + "SSD=3", + ] + } + } + } + + restart_policy = { + condition = "on-failure" + delay = "3s" + max_attempts = 4 + window = "10s" + } + + placement { + constraints = [ + "node.role==manager", + ] + + prefs = [ + "spread=node.role.manager", + ] + + max_replicas = 1 + } + + force_update = 0 + runtime = "container" + networks = [docker_network.test_network.id] + + log_driver { + name = "json-file" + + options { + max-size = "10m" + max-file = "3" + } + } + } + + mode { + replicated { + replicas = 2 + } + } + + update_config { + parallelism = 2 + delay = "10s" + failure_action = "pause" + monitor = "5s" + max_failure_ratio = "0.1" + order = "start-first" + } + + rollback_config { + parallelism = 2 + delay = "5ms" + failure_action = "pause" + monitor = "10h" + max_failure_ratio = "0.9" + order = "stop-first" + } + + endpoint_spec { + mode = "vip" + + ports { + name = "random" + protocol = "tcp" + target_port = "8080" + published_port = "8080" + publish_mode = "ingress" + } + + ports { + # another port + } + } +} diff --git a/examples/resources/docker_volume/import.sh b/examples/resources/docker_volume/import.sh new file mode 100644 index 00000000..a78b9c7a --- /dev/null +++ b/examples/resources/docker_volume/import.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +# Docker volume can be imported using the long id, +# e.g. for a volume with the short id `ecae276c5`: + +terraform import docker_volume.foo "$(docker volume inspect -f {{.ID}} eca)" \ No newline at end of file diff --git a/examples/resources/docker_volume/resource.tf b/examples/resources/docker_volume/resource.tf new file mode 100644 index 00000000..f128bd67 --- /dev/null +++ b/examples/resources/docker_volume/resource.tf @@ -0,0 +1,6 @@ +# Creates a docker volume "shared_volume". +resource "docker_volume" "shared_volume" { + name = "shared_volume" +} + +# Reference the volume with ${docker_volume.shared_volume.name} \ No newline at end of file