terraform-provider-docker/internal/provider/resource_docker_service_test.go

1672 lines
60 KiB
Go
Raw Normal View History

package provider
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"testing"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
// ----------------------------------------
// ----------- UNIT TESTS -----------
// ----------------------------------------
func TestMigrateServiceV1ToV2_empty_restart_policy_and_auth(t *testing.T) {
v1State := map[string]interface{}{
"name": "volume-name",
"task_spec": []interface{}{
map[string]interface{}{
"container_spec": []interface{}{
map[string]interface{}{
"image": "repo:tag",
"stop_grace_period": "10s",
},
},
},
},
}
// first validate that we build that correctly
v1Config := terraform.NewResourceConfigRaw(v1State)
diags := resourceDockerServiceV1().Validate(v1Config)
if diags.HasError() {
t.Error("test precondition failed - attempt to migrate an invalid v1 config")
return
}
ctx := context.Background()
v2State, _ := resourceDockerServiceStateUpgradeV2(ctx, v1State, nil)
v2Config := terraform.NewResourceConfigRaw(v2State)
diags = resourceDockerService().Validate(v2Config)
if diags.HasError() {
fmt.Println(diags)
t.Error("migrated service config is invalid")
return
}
}
func TestMigrateServiceV1ToV2_with_restart_policy(t *testing.T) {
v1State := map[string]interface{}{
"name": "volume-name",
"task_spec": []interface{}{
map[string]interface{}{
"container_spec": []interface{}{
map[string]interface{}{
"image": "repo:tag",
"stop_grace_period": "10s",
},
},
"restart_policy": map[string]interface{}{
"condition": "on-failure",
"delay": "3s",
"max_attempts": 4,
"window": "10s",
},
},
},
}
// first validate that we build that correctly
v1Config := terraform.NewResourceConfigRaw(v1State)
diags := resourceDockerServiceV1().Validate(v1Config)
if diags.HasError() {
t.Error("test precondition failed - attempt to migrate an invalid v1 config")
return
}
ctx := context.Background()
v2State, _ := resourceDockerServiceStateUpgradeV2(ctx, v1State, nil)
v2Config := terraform.NewResourceConfigRaw(v2State)
diags = resourceDockerService().Validate(v2Config)
if diags.HasError() {
fmt.Println(diags)
t.Error("migrated service config is invalid")
return
}
}
func TestMigrateServiceV1ToV2_with_auth(t *testing.T) {
v1State := map[string]interface{}{
"auth": map[string]interface{}{
"server_address": "docker-reg.acme.com",
"username": "user",
"password": "pass",
},
"name": "volume-name",
"task_spec": []interface{}{
map[string]interface{}{
"container_spec": []interface{}{
map[string]interface{}{
"image": "repo:tag",
"stop_grace_period": "10s",
},
},
},
},
}
// first validate that we build that correctly
v1Config := terraform.NewResourceConfigRaw(v1State)
diags := resourceDockerServiceV1().Validate(v1Config)
if diags.HasError() {
t.Error("test precondition failed - attempt to migrate an invalid v1 config")
return
}
ctx := context.Background()
v2State, _ := resourceDockerServiceStateUpgradeV2(ctx, v1State, nil)
v2Config := terraform.NewResourceConfigRaw(v2State)
diags = resourceDockerService().Validate(v2Config)
if diags.HasError() {
fmt.Println(diags)
t.Error("migrated service config is invalid")
return
}
}
func TestMigrateServiceLabelState_empty_labels(t *testing.T) {
v0State := map[string]interface{}{
"name": "volume-name",
"task_spec": []interface{}{
map[string]interface{}{
"container_spec": []interface{}{
map[string]interface{}{
"image": "repo:tag",
"mounts": []interface{}{
map[string]interface{}{
"target": "path/to/target",
"type": "bind",
"volume_options": []interface{}{
map[string]interface{}{},
},
},
},
},
},
},
},
}
// first validate that we build that correctly
v0Config := terraform.NewResourceConfigRaw(v0State)
diags := resourceDockerServiceV0().Validate(v0Config)
if diags.HasError() {
t.Error("test precondition failed - attempt to migrate an invalid v0 config")
return
}
v1State := migrateServiceLabels(v0State)
v1Config := terraform.NewResourceConfigRaw(v1State)
diags = resourceDockerService().Validate(v1Config)
if diags.HasError() {
fmt.Println(diags)
t.Error("migrated service config is invalid")
return
}
}
func TestMigrateServiceLabelState_with_labels(t *testing.T) {
v0State := map[string]interface{}{
"name": "volume-name",
"task_spec": []interface{}{
map[string]interface{}{
"container_spec": []interface{}{
map[string]interface{}{
"image": "repo:tag",
"labels": map[string]interface{}{
"type": "container",
"env": "dev",
},
"mounts": []interface{}{
map[string]interface{}{
"target": "path/to/target",
"type": "bind",
"volume_options": []interface{}{
map[string]interface{}{
"labels": map[string]interface{}{
"type": "mount",
},
},
},
},
},
},
},
},
},
"labels": map[string]interface{}{
"foo": "bar",
"env": "dev",
},
}
// first validate that we build that correctly
v0Config := terraform.NewResourceConfigRaw(v0State)
diags := resourceDockerServiceV0().Validate(v0Config)
if diags.HasError() {
t.Error("test precondition failed - attempt to migrate an invalid v0 config")
return
}
v1State := migrateServiceLabels(v0State)
v1Config := terraform.NewResourceConfigRaw(v1State)
diags = resourceDockerService().Validate(v1Config)
if diags.HasError() {
fmt.Println(diags)
t.Error("migrated service config is invalid")
return
}
}
func TestDockerSecretFromRegistryAuth_basic(t *testing.T) {
authConfigs := make(map[string]types.AuthConfig)
authConfigs["https://repo.my-company.com:8787"] = types.AuthConfig{
Username: "myuser",
Password: "mypass",
Email: "",
ServerAddress: "repo.my-company.com:8787",
}
foundAuthConfig := fromRegistryAuth("repo.my-company.com:8787/my_image", authConfigs)
checkAttribute(t, "Username", foundAuthConfig.Username, "myuser")
checkAttribute(t, "Password", foundAuthConfig.Password, "mypass")
checkAttribute(t, "Email", foundAuthConfig.Email, "")
checkAttribute(t, "ServerAddress", foundAuthConfig.ServerAddress, "repo.my-company.com:8787")
}
func TestDockerSecretFromRegistryAuth_multiple(t *testing.T) {
authConfigs := make(map[string]types.AuthConfig)
authConfigs["https://repo.my-company.com:8787"] = types.AuthConfig{
Username: "myuser",
Password: "mypass",
Email: "",
ServerAddress: "repo.my-company.com:8787",
}
authConfigs["https://nexus.my-fancy-company.com"] = types.AuthConfig{
Username: "myuser33",
Password: "mypass123",
Email: "test@example.com",
ServerAddress: "nexus.my-fancy-company.com",
}
foundAuthConfig := fromRegistryAuth("nexus.my-fancy-company.com/the_image", authConfigs)
checkAttribute(t, "Username", foundAuthConfig.Username, "myuser33")
checkAttribute(t, "Password", foundAuthConfig.Password, "mypass123")
checkAttribute(t, "Email", foundAuthConfig.Email, "test@example.com")
checkAttribute(t, "ServerAddress", foundAuthConfig.ServerAddress, "nexus.my-fancy-company.com")
foundAuthConfig = fromRegistryAuth("alpine:3.1", authConfigs)
checkAttribute(t, "Username", foundAuthConfig.Username, "")
checkAttribute(t, "Password", foundAuthConfig.Password, "")
checkAttribute(t, "Email", foundAuthConfig.Email, "")
checkAttribute(t, "ServerAddress", foundAuthConfig.ServerAddress, "")
}
func checkAttribute(t *testing.T, name, actual, expected string) {
if actual != expected {
t.Fatalf("bad authconfig attribute for '%q'\nExpected: %s\n Got: %s", name, expected, actual)
}
}
func TestDockerImageNameSuppress(t *testing.T) {
suppressFunc := suppressIfSHAwasAdded()
old := ""
new := "alpine3.1"
suppress := suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:v1"
new = "127.0.0.1:15000/tftest-service:v1@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
suppress = suppressFunc("k", old, new, nil)
if !suppress {
t.Fatalf("Expected suppress for \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:latest@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
new = "127.0.0.1:15000/tftest-service"
suppress = suppressFunc("k", old, new, nil)
if !suppress {
t.Fatalf("Expected suppress for \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:latest"
new = "127.0.0.1:15000/tftest-service:latest@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service"
new = "127.0.0.1:15000/tftest-service:latest@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:v1"
new = "127.0.0.1:15000/tftest-service:v2@sha256:ed8e15d68bb13e3a04abddc295f87d2a8b7d849d5ff91f00dbdd66dc10fd8aac"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for image tag update from \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:v1@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
new = "127.0.0.1:15000/tftest-service:v2@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for image tag update from \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:latest@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
new = "127.0.0.1:15000/tftest-service:latest@sha256:c9d1055182f0607632b7d859d2f220126fb1c0d10aedc4451817840b30c1af86"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for image digest update from \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service:v3@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
new = "127.0.0.1:15000/tftest-service:latest@sha256:c9d1055182f0607632b7d859d2f220126fb1c0d10aedc4451817840b30c1af86"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for image tag but no digest update from \n\told '%s' \n\tnew '%s'", old, new)
}
old = "127.0.0.1:15000/tftest-service@sha256:74d04f400723d9770187ee284255d1eb556f3d51700792fb2bfd6ab13da50981"
new = "127.0.0.1:15000/tftest-service@sha256:c9d1055182f0607632b7d859d2f220126fb1c0d10aedc4451817840b30c1af86"
suppress = suppressFunc("k", old, new, nil)
if suppress {
t.Fatalf("Expected no suppress for image tag but no digest update from \n\told '%s' \n\tnew '%s'", old, new)
}
}
// ----------------------------------------
// ----------- ACCEPTANCE TESTS -----------
// ----------------------------------------
// Fire and Forget
var serviceIDRegex = regexp.MustCompile(`[A-Za-z0-9_\+\.-]+`)
func TestAccDockerService_minimalSpec(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
}
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_fullSpec(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
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"
2019-11-14 08:06:50 -05:00
labels {
label = "servicelabel"
value = "true"
}
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
2019-11-14 08:06:50 -05:00
labels {
label = "foo"
value = "bar"
}
command = ["ls"]
args = ["-las"]
hostname = "my-fancy-service"
env = {
MYFOO = "BAR"
URI = "/api-call?param1=value1"
}
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
volume_options {
no_copy = true
2019-11-14 08:06:50 -05:00
labels {
label = "foo"
value = "bar"
}
driver_name = "random-driver"
driver_options = {
op1 = "val1"
}
}
}
stop_signal = "SIGTERM"
stop_grace_period = "10s"
healthcheck {
test = ["CMD", "curl", "-f", "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
}
configs {
config_id = docker_config.service_config.id
config_name = docker_config.service_config.name
file_name = "/configs.json"
}
}
resources {
limits {
nano_cpus = 1000000
memory_bytes = 536870912
}
}
restart_policy {
condition = "on-failure"
delay = "3s"
max_attempts = 4
window = "10s"
}
placement {
constraints = [
"node.role==manager",
]
prefs = [
"spread=node.role.manager",
]
platforms {
architecture = "amd64"
os = "linux"
}
max_replicas = 2
}
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"
}
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
2019-11-14 08:06:50 -05:00
testCheckLabelMap("docker_service.foo", "labels", map[string]string{"servicelabel": "true"}),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1.*`)),
2019-11-14 08:06:50 -05:00
testCheckLabelMap("docker_service.foo", "task_spec.0.container_spec.0.labels", map[string]string{"foo": "bar"}),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.command.0", "ls"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.args.0", "-las"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hostname", "my-fancy-service"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.env.MYFOO", "BAR"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.env.URI", "/api-call?param1=value1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dir", "/root"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.user", "root"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.groups.0", "docker"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.groups.1", "foogroup"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.0.se_linux_context.0.disable", "true"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.0.se_linux_context.0.user", "user-label"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.0.se_linux_context.0.role", "role-label"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.0.se_linux_context.0.type", "type-label"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.0.se_linux_context.0.level", "level-label"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.read_only", "true"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.target", "/mount/test"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.source", "tftest-volume"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.type", "volume"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.read_only", "true"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.volume_options.0.no_copy", "true"),
testCheckLabelMap("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.volume_options.0.labels", map[string]string{"foo": "bar"}),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.volume_options.0.driver_name", "random-driver"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.0.volume_options.0.driver_options.op1", "val1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.stop_signal", "SIGTERM"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.stop_grace_period", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.0", "CMD"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.1", "curl"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.2", "-f"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.3", "localhost:8080/health"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "5s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "2s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.0.host", "testhost"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.0.ip", "10.0.1.0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.0.nameservers.0", "8.8.8.8"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.0.search.0", "example.org"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.0.options.0", "timeout:3"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.configs.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.secrets.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.resources.0.limits.0.nano_cpus", "1000000"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.resources.0.limits.0.memory_bytes", "536870912"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.0.condition", "on-failure"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.0.delay", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.0.max_attempts", "4"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.restart_policy.0.window", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.constraints.0", "node.role==manager"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.prefs.0", "spread=node.role.manager"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.max_replicas", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.force_update", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "3"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "10m"),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "5s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.parallelism", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.delay", "5ms"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.failure_action", "pause"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.monitor", "10h"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.max_failure_ratio", "0.9"),
resource.TestCheckResourceAttr("docker_service.foo", "rollback_config.0.order", "stop-first"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.mode", "vip"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.name", "random"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.protocol", "tcp"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.published_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.publish_mode", "ingress"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_partialReplicationConfig(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
}
}
mode {}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "1"),
),
},
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
}
}
mode {
replicated {}
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "1"),
),
},
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
}
}
mode {
replicated {
replicas = 2
}
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_globalReplicationMode(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
}
}
mode {
global = true
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.global", "true"),
),
},
{
ResourceName: "docker_service.foo",
ImportState: true,
ImportStateVerify: true,
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_ConflictingGlobalAndReplicated(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
}
}
mode {
replicated {
replicas = 2
}
global = true
}
}
`,
ExpectError: regexp.MustCompile(`.*conflicts with.*`),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_ConflictingGlobalModeAndConverge(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
}
}
mode {
global = true
}
converge_config {
delay = "7s"
timeout = "10s"
}
}
`,
ExpectError: regexp.MustCompile(`.*conflicts with.*`),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
// Converging tests
func TestAccDockerService_privateImageConverge(t *testing.T) {
registry := "127.0.0.1:15000"
image := "127.0.0.1:15000/tftest-service:v1"
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
provider "docker" {
registry_auth {
address = "%s"
}
}
resource "docker_service" "foo" {
name = "tftest-service-foo"
task_spec {
container_spec {
image = "%s"
stop_grace_period = "10s"
}
}
mode {
replicated {
replicas = 2
}
}
converge_config {
delay = "7s"
timeout = "3m"
}
}
`, registry, image),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-foo"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_updateMultiplePropertiesConverge(t *testing.T) {
t.Skip("Skipping this test because it is flaky only on travis")
// Step 1
configData := "ewogICJwcmVmaXgiOiAiMTIzIgp9"
secretData := "ewogICJrZXkiOiAiUVdFUlRZIgp9"
image := "127.0.0.1:15000/tftest-service:v1"
ctx := context.Background()
mounts := `
mounts {
source = docker_volume.foo.name
target = "/mount/test"
type = "volume"
read_only = true
volume_options {
2019-11-14 08:06:50 -05:00
labels {
label = "env"
value = "dev"
}
labels {
label = "terraform"
value = "true"
}
}
}
`
hosts := `
hosts {
host = "testhost"
ip = "10.0.1.0"
}
`
logging := `
name = "json-file"
options = {
max-size = "10m"
max-file = "3"
}
`
healthcheckInterval := "1s"
healthcheckTimeout := "500ms"
replicas := 2
portsSpec := `
ports {
target_port = "8080"
published_port = "8081"
}
`
// Step 2
configData2 := "ewogICJwcmVmaXgiOiAiNTY3Igp9" // UPDATED to prefix: 567
secretData2 := "ewogICJrZXkiOiAiUVdFUlRZIgp9" // UPDATED to YXCVB
image2 := "127.0.0.1:15000/tftest-service:v2"
healthcheckInterval2 := "2s"
mounts2 := `
mounts {
source = docker_volume.foo.name
target = "/mount/test"
type = "volume"
read_only = true
volume_options {
2019-11-14 08:06:50 -05:00
labels {
label = "env"
value = "dev"
}
labels {
label = "terraform"
value = "true"
}
}
}
mounts {
source = docker_volume.foo2.name
target = "/mount/test2"
type = "volume"
read_only = true
volume_options {
2019-11-14 08:06:50 -05:00
labels {
label = "env"
value = "dev"
}
labels {
label = "terraform"
value = "true"
}
}
}
`
hosts2 := `
hosts {
host = "testhost2"
ip = "10.0.2.2"
}
`
logging2 := `
name = "json-file"
options = {
max-size = "15m"
max-file = "5"
}
`
healthcheckTimeout2 := "800ms"
replicas2 := 6
portsSpec2 := `
ports {
target_port = "8080"
published_port = "8081"
}
ports {
target_port = "8080"
published_port = "8082"
}
`
// Step 3
configData3 := configData2
secretData3 := secretData2
image3 := image2
mounts3 := mounts2
hosts3 := hosts2
logging3 := logging2
healthcheckInterval3 := healthcheckInterval2
healthcheckTimeout3 := healthcheckTimeout2
replicas3 := 3 // only decrease
portsSpec3 := portsSpec2
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(updateMultiplePropertiesConfigConverge, configData, secretData, image, mounts, hosts, healthcheckInterval, healthcheckTimeout, logging, replicas, portsSpec),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", strconv.Itoa(replicas)),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "continue"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.5"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.published_port", "8081"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.configs.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.secrets.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dir", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.env.%", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.groups.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.0", "CMD"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.1", "curl"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.2", "-f"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.3", "localhost:8080/health"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", healthcheckInterval),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", healthcheckTimeout),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hostname", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.1878413705.host", "testhost"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.1878413705.ip", "10.0.1.0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.isolation", "default"),
2019-11-14 08:06:50 -05:00
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.labels.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.stop_grace_period", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.user", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.%", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "3"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "10m"),
),
},
{
Config: fmt.Sprintf(updateMultiplePropertiesConfigConverge, configData2, secretData2, image2, mounts2, hosts2, healthcheckInterval2, healthcheckTimeout2, logging2, replicas2, portsSpec2),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v2.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", strconv.Itoa(replicas2)),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "continue"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.5"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.#", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.published_port", "8081"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1.published_port", "8082"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.configs.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.secrets.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dir", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.env.%", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.groups.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.0", "CMD"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.1", "curl"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.2", "-f"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.3", "localhost:8080/health"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", healthcheckInterval2),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", healthcheckTimeout2),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hostname", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.575059346.host", "testhost2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.575059346.ip", "10.0.2.2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.isolation", "default"),
2019-11-14 08:06:50 -05:00
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.labels.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.#", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.stop_grace_period", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.user", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.%", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "5"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "15m"),
),
},
{
Config: fmt.Sprintf(updateMultiplePropertiesConfigConverge, configData3, secretData3, image3, mounts3, hosts3, healthcheckInterval3, healthcheckTimeout3, logging3, replicas3, portsSpec3),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v2.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", strconv.Itoa(replicas3)),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "continue"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "3s"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.5"),
resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.#", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.0.published_port", "8081"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1.target_port", "8080"),
resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1.published_port", "8082"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.configs.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.secrets.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dir", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.dns_config.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.env.%", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.groups.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.0", "CMD"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.1", "curl"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.2", "-f"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.test.3", "localhost:8080/health"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", healthcheckInterval3),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", healthcheckTimeout3),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hostname", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.575059346.host", "testhost2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.575059346.ip", "10.0.2.2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.isolation", "default"),
2019-11-14 08:06:50 -05:00
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.labels.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.#", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.privileges.#", "0"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.stop_grace_period", "10s"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.user", ""),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.#", "1"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.%", "2"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "5"),
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "15m"),
),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_nonExistingPrivateImageConverge(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
resource "docker_service" "foo" {
name = "tftest-service-privateimagedoesnotexist"
task_spec {
container_spec {
image = "127.0.0.1:15000/idonoexist:latest"
}
}
mode {
replicated {
replicas = 2
}
}
converge_config {
delay = "7s"
timeout = "20s"
}
}
`,
ExpectError: regexp.MustCompile(`.*did not converge after.*`),
Check: resource.ComposeTestCheckFunc(
isServiceRemoved("tftest-service-privateimagedoesnotexist"),
),
},
},
})
}
func TestAccDockerService_nonExistingPublicImageConverge(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
resource "docker_service" "foo" {
name = "tftest-service-publicimagedoesnotexist"
task_spec {
container_spec {
image = "stovogel/blablabla:part5"
}
}
mode {
replicated {
replicas = 2
}
}
converge_config {
delay = "7s"
timeout = "10s"
}
}
`,
ExpectError: regexp.MustCompile(`.*did not converge after.*`),
Check: resource.ComposeTestCheckFunc(
isServiceRemoved("tftest-service-publicimagedoesnotexist"),
),
},
},
})
}
func TestAccDockerService_convergeAndStopGracefully(t *testing.T) {
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: `
provider "docker" {
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
name = "tftest-service-basic-converge"
task_spec {
container_spec {
image = "127.0.0.1:15000/tftest-service:v1"
stop_grace_period = "10s"
healthcheck {
test = ["CMD", "curl", "-f", "localhost:8080/health"]
interval = "5s"
timeout = "2s"
start_period = "0s"
retries = 4
}
}
}
mode {
replicated {
replicas = 2
}
}
endpoint_spec {
ports {
target_port = "8080"
}
}
converge_config {
delay = "7s"
timeout = "3m"
}
}
`,
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic-converge"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
testValueHigherEqualThan("docker_service.foo", "endpoint_spec.0.ports.0.target_port", 8080),
testValueHigherEqualThan("docker_service.foo", "endpoint_spec.0.ports.0.published_port", 30000),
),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
func TestAccDockerService_updateFailsAndRollbackConverge(t *testing.T) {
image := "127.0.0.1:15000/tftest-service:v1"
imageFail := "127.0.0.1:15000/tftest-service:v3"
ctx := context.Background()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(updateFailsAndRollbackConvergeConfig, image),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-updateFailsAndRollbackConverge"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
),
},
{
Config: fmt.Sprintf(updateFailsAndRollbackConvergeConfig, imageFail),
ExpectError: regexp.MustCompile(`.*rollback completed.*`),
Check: resource.ComposeTestCheckFunc(
resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex),
resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-updateFailsAndRollbackConverge"),
resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1.*`)),
resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"),
),
},
},
CheckDestroy: func(state *terraform.State) error {
return checkAndRemoveImages(ctx, state)
},
})
}
const updateMultiplePropertiesConfigConverge = `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_volume" "foo" {
name = "tftest-volume"
}
resource "docker_volume" "foo2" {
name = "tftest-volume2"
}
resource "docker_config" "service_config" {
name = "tftest-myconfig-${uuid()}"
data = "%s"
lifecycle {
ignore_changes = ["name"]
create_before_destroy = true
}
}
resource "docker_secret" "service_secret" {
name = "tftest-tftest-mysecret-${replace(timestamp(),":", ".")}"
data = "%s"
lifecycle {
ignore_changes = ["name"]
create_before_destroy = true
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-fnf-service-up-crihiadr"
task_spec {
container_spec {
image = "%s"
%s
%s
configs {
config_id = docker_config.service_config.id
config_name = docker_config.service_config.name
file_name = "/configs.json"
}
secrets {
secret_id = docker_secret.service_secret.id
secret_name = docker_secret.service_secret.name
file_name = "/secrets.json"
}
healthcheck {
test = ["CMD", "curl", "-f", "localhost:8080/health"]
interval = "%s"
timeout = "%s"
start_period = "1s"
retries = 2
}
stop_grace_period = "10s"
}
log_driver {
%s
}
}
mode {
replicated {
replicas = %d
}
}
update_config {
parallelism = 2
delay = "3s"
failure_action = "continue"
monitor = "3s"
max_failure_ratio = "0.5"
order = "start-first"
}
endpoint_spec {
%s
}
converge_config {
delay = "7s"
timeout = "2m"
}
}
`
const updateFailsAndRollbackConvergeConfig = `
provider "docker" {
alias = "private"
registry_auth {
address = "127.0.0.1:15000"
}
}
resource "docker_service" "foo" {
provider = "docker.private"
name = "tftest-service-updateFailsAndRollbackConverge"
task_spec {
container_spec {
image = "%s"
stop_grace_period = "10s"
healthcheck {
test = ["CMD", "curl", "-f", "localhost:8080/health"]
interval = "5s"
timeout = "2s"
start_period = "0s"
retries = 4
}
}
}
mode {
replicated {
replicas = 2
}
}
update_config {
parallelism = 1
delay = "5s"
failure_action = "rollback"
monitor = "10s"
max_failure_ratio = "0.0"
order = "stop-first"
}
rollback_config {
parallelism = 1
delay = "1s"
failure_action = "pause"
monitor = "4s"
max_failure_ratio = "0.0"
order = "stop-first"
}
endpoint_spec {
mode = "vip"
ports {
name = "random"
protocol = "tcp"
target_port = "8080"
published_port = "8080"
publish_mode = "ingress"
}
}
converge_config {
delay = "7s"
timeout = "3m"
}
}
`
// Helpers
// isServiceRemoved checks if a service was removed successfully
func isServiceRemoved(serviceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ctx := context.Background()
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
filters := filters.NewArgs()
filters.Add("name", serviceName)
services, err := client.ServiceList(ctx, types.ServiceListOptions{
Filters: filters,
})
if err != nil {
return fmt.Errorf("Error listing service for name %s: %v", serviceName, err)
}
length := len(services)
if length != 0 {
return fmt.Errorf("Service should be removed but is running: %s", serviceName)
}
return nil
}
}
// checkAndRemoveImages checks and removes all private images with
// the given pattern. This ensures that the image are not kept on the swarm nodes
// and the tests are independent of each other
func checkAndRemoveImages(ctx context.Context, s *terraform.State) error {
retrySleepSeconds := 3
maxRetryDeleteCount := 6
imagePattern := "127.0.0.1:15000/tftest-service*"
client := testAccProvider.Meta().(*ProviderConfig).DockerClient
filters := filters.NewArgs()
filters.Add("reference", imagePattern)
images, err := client.ImageList(ctx, types.ImageListOptions{
Filters: filters,
})
if err != nil {
return err
}
retryDeleteCount := 0
for i := 0; i < len(images); {
image := images[i]
_, err := client.ImageRemove(ctx, image.ID, types.ImageRemoveOptions{
Force: true,
})
if err != nil {
if strings.Contains(err.Error(), "image is being used by running container") {
if retryDeleteCount == maxRetryDeleteCount {
return fmt.Errorf("could not delete image '%s' after %d retries", image.ID, maxRetryDeleteCount)
}
<-time.After(time.Duration(retrySleepSeconds) * time.Second)
retryDeleteCount++
continue
}
return err
}
i++
}
imagesAfterDelete, err := client.ImageList(ctx, types.ImageListOptions{
Filters: filters,
})
if err != nil {
return err
}
if len(imagesAfterDelete) != 0 {
return fmt.Errorf("Expected images of pattern '%s' to be deleted, but there is/are still %d", imagePattern, len(imagesAfterDelete))
}
return nil
}