From a6fdf4c2a65ba3afd2d39d2b3362ff999ffe8914 Mon Sep 17 00:00:00 2001 From: Manuel Vogel Date: Sun, 26 May 2019 11:42:53 +0200 Subject: [PATCH] Fixes for image pulling and local registry (#143) Multiple fixes for handling private registries and login credential retrieval on OSX. - Fixes check for image locally before pulling it: #24 - Fixes image prefix workaround: #120 - Fixes passing of the `--with-registry-auth` flag: #77 - Fixes docker registry credentials in osxkeychain: #125 --- .../data_source_docker_registry_image_test.go | 6 +- docker/provider.go | 45 +- docker/provider_test.go | 14 - docker/resource_docker_container.go | 1 + docker/resource_docker_container_funcs.go | 15 +- docker/resource_docker_container_test.go | 39 + docker/resource_docker_image_funcs.go | 36 +- docker/resource_docker_image_test.go | 41 +- docker/resource_docker_service.go | 104 +- docker/resource_docker_service_funcs.go | 20 +- docker/resource_docker_service_test.go | 3575 ++++------------- go.mod | 1 + go.sum | 2 + scripts/runAccTests.sh | 53 +- scripts/testing/dockerconfig.json | 7 + scripts/testing/setup_private_registry.sh | 37 - .../docker/docker-credential-helpers/LICENSE | 20 + .../client/client.go | 121 + .../client/command.go | 56 + .../credentials/credentials.go | 186 + .../credentials/error.go | 102 + .../credentials/helper.go | 14 + .../credentials/version.go | 4 + vendor/modules.txt | 3 + 24 files changed, 1510 insertions(+), 2992 deletions(-) create mode 100644 scripts/testing/dockerconfig.json delete mode 100755 scripts/testing/setup_private_registry.sh create mode 100644 vendor/github.com/docker/docker-credential-helpers/LICENSE create mode 100644 vendor/github.com/docker/docker-credential-helpers/client/client.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/client/command.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/error.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/helper.go create mode 100644 vendor/github.com/docker/docker-credential-helpers/credentials/version.go diff --git a/docker/data_source_docker_registry_image_test.go b/docker/data_source_docker_registry_image_test.go index b0bc04d6..138ab06d 100644 --- a/docker/data_source_docker_registry_image_test.go +++ b/docker/data_source_docker_registry_image_test.go @@ -2,7 +2,6 @@ package docker import ( "fmt" - "os" "regexp" "testing" @@ -42,8 +41,8 @@ func TestAccDockerRegistryImage_private(t *testing.T) { } func TestAccDockerRegistryImage_auth(t *testing.T) { - registry := os.Getenv("DOCKER_REGISTRY_ADDRESS") - image := os.Getenv("DOCKER_PRIVATE_IMAGE") + registry := "127.0.0.1:15000" + image := "127.0.0.1:15000/tftest-service:v1" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -55,6 +54,7 @@ func TestAccDockerRegistryImage_auth(t *testing.T) { ), }, }, + CheckDestroy: checkAndRemoveImages, }) } diff --git a/docker/provider.go b/docker/provider.go index e66573da..817d03c0 100644 --- a/docker/provider.go +++ b/docker/provider.go @@ -8,10 +8,13 @@ import ( "errors" "fmt" "io" + "log" "os" "os/user" + "runtime" "strings" + osx "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker/api/types" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" @@ -134,7 +137,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { authConfigs := &AuthConfigs{} - if v, ok := d.GetOk("registry_auth"); ok { + if v, ok := d.GetOk("registry_auth"); ok { // TODO load them anyway authConfigs, err = providerSetToRegistryAuth(v.(*schema.Set)) if err != nil { @@ -224,14 +227,17 @@ func providerSetToRegistryAuth(authSet *schema.Set) (*AuthConfigs, error) { } // newAuthConfigurations returns AuthConfigs from a JSON encoded string in the -// same format as the .dockercfg file. +// same format as the .dockercfg/ ~/.docker/config.json file. func newAuthConfigurations(r io.Reader) (*AuthConfigs, error) { var auth *AuthConfigs + log.Println("[DEBUG] Parsing Docker config file") confs, err := parseDockerConfig(r) if err != nil { return nil, err } - auth, err = authConfigs(confs) + + log.Printf("[DEBUG] Found Docker configs '%v'", confs) + auth, err = convertDockerConfigToAuthConfigs(confs) if err != nil { return nil, err } @@ -260,13 +266,18 @@ func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) { return confs, nil } -// authConfigs converts a dockerConfigs map to a AuthConfigs object. -func authConfigs(confs map[string]dockerConfig) (*AuthConfigs, error) { +// convertDockerConfigToAuthConfigs converts a dockerConfigs map to a AuthConfigs object. +func convertDockerConfigToAuthConfigs(confs map[string]dockerConfig) (*AuthConfigs, error) { c := &AuthConfigs{ Configs: make(map[string]types.AuthConfig), } - for reg, conf := range confs { + for registryAddress, conf := range confs { if conf.Auth == "" { + authFromKeyChain, err := getCredentialsFromOSKeychain(registryAddress) + if err != nil { + return nil, err + } + c.Configs[registryAddress] = authFromKeyChain continue } data, err := base64.StdEncoding.DecodeString(conf.Auth) @@ -277,13 +288,31 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigs, error) { if len(userpass) != 2 { return nil, ErrCannotParseDockercfg } - c.Configs[reg] = types.AuthConfig{ + c.Configs[registryAddress] = types.AuthConfig{ Email: conf.Email, Username: userpass[0], Password: userpass[1], - ServerAddress: reg, + ServerAddress: registryAddress, Auth: conf.Auth, } } return c, nil } + +// getCredentialsFromOSKeychain get config from system specific keychains +func getCredentialsFromOSKeychain(registryAddress string) (types.AuthConfig, error) { + authConfig := types.AuthConfig{} + log.Printf("[DEBUG] Getting auth for registry '%s' on OS: '%s'", registryAddress, runtime.GOOS) + if runtime.GOOS == "darwin" { + p := osx.NewShellProgramFunc("docker-credential-osxkeychain") + credentials, err := osx.Get(p, registryAddress) + if err != nil { + return authConfig, err + } + authConfig.Username = credentials.Username + authConfig.Password = credentials.Secret + authConfig.ServerAddress = registryAddress + authConfig.Auth = base64.StdEncoding.EncodeToString([]byte(credentials.Username + ":" + credentials.Secret)) + } + return authConfig, nil +} diff --git a/docker/provider_test.go b/docker/provider_test.go index 3c7a773f..cfee40ba 100644 --- a/docker/provider_test.go +++ b/docker/provider_test.go @@ -1,7 +1,6 @@ package docker import ( - "os" "os/exec" "testing" @@ -43,19 +42,6 @@ func testAccPreCheck(t *testing.T) { } } - if v := os.Getenv("DOCKER_REGISTRY_ADDRESS"); v == "" { - t.Fatalf("DOCKER_REGISTRY_ADDRESS must be set for acceptance tests") - } - if v := os.Getenv("DOCKER_REGISTRY_USER"); v == "" { - t.Fatalf("DOCKER_REGISTRY_USER must be set for acceptance tests") - } - if v := os.Getenv("DOCKER_REGISTRY_PASS"); v == "" { - t.Fatalf("DOCKER_REGISTRY_PASS must be set for acceptance tests") - } - if v := os.Getenv("DOCKER_PRIVATE_IMAGE"); v == "" { - t.Fatalf("DOCKER_PRIVATE_IMAGE must be set for acceptance tests") - } - err := testAccProvider.Configure(terraform.NewResourceConfig(nil)) if err != nil { t.Fatal(err) diff --git a/docker/resource_docker_container.go b/docker/resource_docker_container.go index 986d1965..68e86960 100644 --- a/docker/resource_docker_container.go +++ b/docker/resource_docker_container.go @@ -84,6 +84,7 @@ func resourceDockerContainer() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + // DiffSuppressFunc: suppressIfSHAwasAdded(), // TODO mvogel }, "hostname": { diff --git a/docker/resource_docker_container_funcs.go b/docker/resource_docker_container_funcs.go index f4532837..1971a8f9 100644 --- a/docker/resource_docker_container_funcs.go +++ b/docker/resource_docker_container_funcs.go @@ -32,18 +32,11 @@ var ( func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error { var err error client := meta.(*ProviderConfig).DockerClient - - var data Data - if err := fetchLocalImages(&data, client); err != nil { - return err - } - + authConfigs := meta.(*ProviderConfig).AuthConfigs image := d.Get("image").(string) - if _, ok := data.DockerImages[image]; !ok { - if _, ok := data.DockerImages[image+":latest"]; !ok { - return fmt.Errorf("Unable to find image %s", image) - } - image = image + ":latest" + _, err = findImage(image, client, authConfigs) + if err != nil { + return fmt.Errorf("Unable to create container with image %s: %s", image, err) } config := &container.Config{ diff --git a/docker/resource_docker_container_test.go b/docker/resource_docker_container_test.go index ff9b51e0..e46b6c1b 100644 --- a/docker/resource_docker_container_test.go +++ b/docker/resource_docker_container_test.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "fmt" + "os" "reflect" "strconv" "strings" @@ -29,6 +30,28 @@ func TestMapTypeMapValsToStringSlice(t *testing.T) { } } +func TestAccDockerContainer_private_image(t *testing.T) { + registry := "127.0.0.1:15000" + image := "127.0.0.1:15000/tftest-service:v1" + wd, _ := os.Getwd() + dockerConfig := wd + "/../scripts/testing/dockerconfig.json" + + var c types.ContainerJSON + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccDockerContainerPrivateImage, registry, dockerConfig, image), + Check: resource.ComposeTestCheckFunc( + testAccContainerRunning("docker_container.foo", &c), + ), + }, + }, + CheckDestroy: checkAndRemoveImages, + }) +} + func TestAccDockerContainer_basic(t *testing.T) { var c types.ContainerJSON resource.Test(t, resource.TestCase{ @@ -1125,6 +1148,22 @@ func testValueHigherEqualThan(name, key string, value int) resource.TestCheckFun } } +const testAccDockerContainerPrivateImage = ` +provider "docker" { + alias = "private" + registry_auth { + address = "%s" + config_file = "%s" + } +} + +resource "docker_container" "foo" { + provider = "docker.private" + name = "tf-test" + image = "%s" +} +` + const testAccDockerContainerConfig = ` resource "docker_image" "foo" { name = "nginx:latest" diff --git a/docker/resource_docker_image_funcs.go b/docker/resource_docker_image_funcs.go index 8509a787..8d164bfd 100644 --- a/docker/resource_docker_image_funcs.go +++ b/docker/resource_docker_image_funcs.go @@ -17,7 +17,8 @@ import ( func resourceDockerImageCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ProviderConfig).DockerClient - apiImage, err := findImage(d, client, meta.(*ProviderConfig).AuthConfigs) + imageName := d.Get("name").(string) + apiImage, err := findImage(imageName, client, meta.(*ProviderConfig).AuthConfigs) if err != nil { return fmt.Errorf("Unable to read Docker image into resource: %s", err) } @@ -49,7 +50,8 @@ func resourceDockerImageUpdate(d *schema.ResourceData, meta interface{}) error { // We need to re-read in case switching parameters affects // the value of "latest" or others client := meta.(*ProviderConfig).DockerClient - apiImage, err := findImage(d, client, meta.(*ProviderConfig).AuthConfigs) + imageName := d.Get("name").(string) + apiImage, err := findImage(imageName, client, meta.(*ProviderConfig).AuthConfigs) if err != nil { return fmt.Errorf("Unable to read Docker image into resource: %s", err) } @@ -170,7 +172,7 @@ func pullImage(data *Data, client *client.Client, authConfig *AuthConfigs, image s := buf.String() log.Printf("[DEBUG] pulled image %v: %v", image, s) - return fetchLocalImages(data, client) + return nil } type internalPullImageOptions struct { @@ -210,19 +212,15 @@ func parseImageOptions(image string) internalPullImageOptions { return pullOpts } -func findImage(d *schema.ResourceData, client *client.Client, authConfig *AuthConfigs) (*types.ImageSummary, error) { - var data Data - //if err := fetchLocalImages(&data, client); err != nil { - // return nil, err - //} Is done in pullImage - - imageName := d.Get("name").(string) +func findImage(imageName string, client *client.Client, authConfig *AuthConfigs) (*types.ImageSummary, error) { if imageName == "" { return nil, fmt.Errorf("Empty image name is not allowed") } - if err := pullImage(&data, client, authConfig, imageName); err != nil { - return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) + var data Data + // load local images into the data structure + if err := fetchLocalImages(&data, client); err != nil { + return nil, err } foundImage := searchLocalImages(data, imageName) @@ -230,5 +228,19 @@ func findImage(d *schema.ResourceData, client *client.Client, authConfig *AuthCo return foundImage, nil } + if err := pullImage(&data, client, authConfig, imageName); err != nil { + return nil, fmt.Errorf("Unable to pull image %s: %s", imageName, err) + } + + // update the data structure of the images + if err := fetchLocalImages(&data, client); err != nil { + return nil, err + } + + foundImage = searchLocalImages(data, imageName) + if foundImage != nil { + return foundImage, nil + } + return nil, fmt.Errorf("Unable to find or pull image %s", imageName) } diff --git a/docker/resource_docker_image_test.go b/docker/resource_docker_image_test.go index a32867c3..76b020c3 100644 --- a/docker/resource_docker_image_test.go +++ b/docker/resource_docker_image_test.go @@ -107,8 +107,8 @@ func TestAccDockerImage_data_pull_trigger(t *testing.T) { } func TestAccDockerImage_data_private(t *testing.T) { - registry := os.Getenv("DOCKER_REGISTRY_ADDRESS") - image := os.Getenv("DOCKER_PRIVATE_IMAGE") + registry := "127.0.0.1:15000" + image := "127.0.0.1:15000/tftest-service:v1" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -122,6 +122,29 @@ func TestAccDockerImage_data_private(t *testing.T) { ), }, }, + CheckDestroy: checkAndRemoveImages, + }) +} + +func TestAccDockerImage_data_private_config_file(t *testing.T) { + registry := "127.0.0.1:15000" + image := "127.0.0.1:15000/tftest-service:v1" + wd, _ := os.Getwd() + dockerConfig := wd + "/../scripts/testing/dockerconfig.json" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + PreventPostDestroyRefresh: true, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccDockerImageFromDataPrivateConfigFile, registry, dockerConfig, image), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr("docker_image.foo_private", "latest", contentDigestRegexp), + ), + }, + }, + CheckDestroy: checkAndRemoveImages, }) } @@ -214,6 +237,20 @@ resource "docker_image" "foo_private" { } ` +const testAccDockerImageFromDataPrivateConfigFile = ` +provider "docker" { + alias = "private" + registry_auth { + address = "%s" + config_file = "%s" + } +} +resource "docker_image" "foo_private" { + provider = "docker.private" + name = "%s" +} +` + const testAddDockerImageWithSHA256RepoDigest = ` resource "docker_image" "foobar" { name = "stocard/gotthard@sha256:ed752380c07940c651b46c97ca2101034b3be112f4d86198900aa6141f37fe7b" diff --git a/docker/resource_docker_service.go b/docker/resource_docker_service.go index a600b44f..916d1c6b 100644 --- a/docker/resource_docker_service.go +++ b/docker/resource_docker_service.go @@ -1,6 +1,10 @@ package docker import ( + "fmt" + "log" + "strings" + "github.com/hashicorp/terraform/helper/schema" ) @@ -70,9 +74,10 @@ func resourceDockerService() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "image": { - Type: schema.TypeString, - Description: "The image name to use for the containers of the service", - Required: true, + Type: schema.TypeString, + Description: "The image name to use for the containers of the service", + Required: true, + DiffSuppressFunc: suppressIfSHAwasAdded(), }, "labels": { Type: schema.TypeMap, @@ -899,3 +904,96 @@ func resourceDockerService() *schema.Resource { }, } } + +func suppressIfSHAwasAdded() schema.SchemaDiffSuppressFunc { + return func(k, old, new string, d *schema.ResourceData) bool { + // the initial case when the service is created + if old == "" && new != "" { + return false + } + + oldURL, oldImage, oldTag, oldDigest, oldErr := splitImageName(old) + if oldErr != nil { + log.Printf("[DEBUG] invalid old image name: %s\n", oldErr.Error()) + return false + } + log.Printf("[DEBUG] old image parse: %s, %s, %s, %s\n", oldURL, oldImage, oldTag, oldDigest) + + newURL, newImage, newTag, newDigest, newErr := splitImageName(new) + if newErr != nil { + log.Printf("[DEBUG] invalid new image name: %s\n", newErr.Error()) + return false + } + log.Printf("[DEBUG] new image parse: %s, %s, %s, %s\n", newURL, newImage, newTag, newDigest) + + if oldURL != newURL || oldImage != newImage { + return false + } + + // special case with latest + if oldTag == "latest" && (newTag == "" || newTag == "latest") { + if oldDigest != "" && newDigest == "" { + return true + } + + return false + } + + // https://success.docker.com/article/images-tagging-vs-digests + // we always pull if the tag changes, also in the empty and 'latest' case + if (oldTag == "latest" || newTag == "") || (oldTag == "" && newTag == "latest") { + return false + } + + if oldTag != newTag { + return false + } + + // tags are the same and so should be its digests + if oldDigest == newDigest || (oldDigest == "" && newDigest != "") || (oldDigest != "" && newDigest == "") { + return true + } + + // we only update if the digests are given and different + if oldDigest != newDigest { + return false + } + + return true + } +} + +// spitImageName splits an image with name 127.0.0.1:15000/tftest-service:v1@sha256:24.. +// into its parts. Handles edge cases like no tag and no digest +func splitImageName(imageNameToSplit string) (url, image, tag, digest string, err error) { + urlToRestSplit := strings.Split(imageNameToSplit, "/") + if len(urlToRestSplit) != 2 { + return "", "", "", "", fmt.Errorf("image name is not valid: %s", imageNameToSplit) + } + url = urlToRestSplit[0] + imageNameToRestSplit := strings.Split(urlToRestSplit[1], ":") + // we only have an image name without tag and sha256 + if len(imageNameToRestSplit) == 1 { + image = imageNameToRestSplit[0] + return url, image, "", "", nil + } + // has tag and sha256 + if len(imageNameToRestSplit) == 3 { + image = imageNameToRestSplit[0] + tag = strings.Replace(imageNameToRestSplit[1], "@sha256", "", 1) + digest = imageNameToRestSplit[2] + return url, image, tag, digest, nil + } + // can be either with tag or sha256, which implies 'latest' tag + if len(imageNameToRestSplit) == 2 { + image = imageNameToRestSplit[0] + if strings.Contains(imageNameToRestSplit[1], "sha256") { + digest = imageNameToRestSplit[1] + return url, image, "", digest, nil + } + tag = strings.Replace(imageNameToRestSplit[1], "@sha256", "", 1) + return url, image, tag, "", nil + } + + return "", "", "", "", fmt.Errorf("image name is not valid: %s", imageNameToSplit) +} diff --git a/docker/resource_docker_service_funcs.go b/docker/resource_docker_service_funcs.go index 806a4a16..7e95a16d 100644 --- a/docker/resource_docker_service_funcs.go +++ b/docker/resource_docker_service_funcs.go @@ -62,13 +62,15 @@ func resourceDockerServiceCreate(d *schema.ResourceData, meta interface{}) error if v, ok := d.GetOk("auth"); ok { auth = authToServiceAuth(v.(map[string]interface{})) } else { - auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), meta.(*ProviderConfig).AuthConfigs.Configs) + authConfigs := meta.(*ProviderConfig).AuthConfigs.Configs + log.Printf("[DEBUG] Getting configs from '%v'", authConfigs) + auth = fromRegistryAuth(d.Get("task_spec.0.container_spec.0.image").(string), authConfigs) } - encodedJSON, err := json.Marshal(auth) - if err != nil { - return fmt.Errorf("error creating auth config: %s", err) - } - serviceOptions.EncodedRegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON) + + marshalledAuth, _ := json.Marshal(auth) // https://docs.docker.com/engine/api/v1.37/#section/Versioning + serviceOptions.EncodedRegistryAuth = base64.URLEncoding.EncodeToString(marshalledAuth) + serviceOptions.QueryRegistry = true + log.Printf("[DEBUG] Passing registry auth '%s'", serviceOptions.EncodedRegistryAuth) service, err := client.ServiceCreate(context.Background(), serviceSpec, serviceOptions) if err != nil { @@ -1284,15 +1286,15 @@ func authToServiceAuth(auth map[string]interface{}) types.AuthConfig { } // fromRegistryAuth extract the desired AuthConfiguration for the given image -func fromRegistryAuth(image string, configs map[string]types.AuthConfig) types.AuthConfig { - // Remove normalized prefixes to simlify substring +func fromRegistryAuth(image string, authConfigs map[string]types.AuthConfig) types.AuthConfig { + // Remove normalized prefixes to simplify substring image = strings.Replace(strings.Replace(image, "http://", "", 1), "https://", "", 1) // Get the registry with optional port lastBin := strings.Index(image, "/") // No auth given and image name has no slash like 'alpine:3.1' if lastBin != -1 { serverAddress := image[0:lastBin] - if fromRegistryAuth, ok := configs[normalizeRegistryAddress(serverAddress)]; ok { + if fromRegistryAuth, ok := authConfigs[normalizeRegistryAddress(serverAddress)]; ok { return fromRegistryAuth } } diff --git a/docker/resource_docker_service_test.go b/docker/resource_docker_service_test.go index 00721317..80fe24d8 100644 --- a/docker/resource_docker_service_test.go +++ b/docker/resource_docker_service_test.go @@ -2,11 +2,14 @@ package docker import ( "fmt" - "os" "regexp" + "strconv" + "strings" "testing" + "time" "context" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/hashicorp/terraform/helper/resource" @@ -69,20 +72,101 @@ func checkAttribute(t *testing.T, name, actual, expected string) error { return nil } +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_minimal(t *testing.T) { +func TestAccDockerService_minimalSpec(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec { @@ -94,20 +178,28 @@ func TestAccDockerService_minimal(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + resource.TestMatchResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:v1@sha256.*`)), ), }, }, + CheckDestroy: checkAndRemoveImages, }) } -func TestAccDockerService_full(t *testing.T) { +func TestAccDockerService_fullSpec(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_volume" "test_volume" { name = "tftest-volume" } @@ -128,7 +220,8 @@ func TestAccDockerService_full(t *testing.T) { } resource "docker_service" "foo" { - name = "tftest-service-basic" + provider = "docker.private" + name = "tftest-service-basic" task_spec { container_spec { @@ -186,7 +279,7 @@ func TestAccDockerService_full(t *testing.T) { stop_grace_period = "10s" healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] + test = ["CMD", "curl", "-f", "localhost:8080/health"] interval = "5s" timeout = "2s" retries = 4 @@ -242,6 +335,14 @@ func TestAccDockerService_full(t *testing.T) { prefs = [ "spread=node.role.manager", ] + + platforms = [ + { + architecture = "amd64" + os = "linux" + } + ] + } force_update = 0 @@ -299,7 +400,7 @@ func TestAccDockerService_full(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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", "task_spec.0.container_spec.0.labels.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"), @@ -328,7 +429,7 @@ func TestAccDockerService_full(t *testing.T) { 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", "http://localhost:8080/health"), + 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"), @@ -374,17 +475,26 @@ func TestAccDockerService_full(t *testing.T) { ), }, }, + CheckDestroy: checkAndRemoveImages, }) } -func TestAccDockerService_partialReplicated(t *testing.T) { +func TestAccDockerService_partialReplicationConfig(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec = { @@ -397,13 +507,21 @@ func TestAccDockerService_partialReplicated(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec = { @@ -418,13 +536,21 @@ func TestAccDockerService_partialReplicated(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec = { @@ -441,22 +567,31 @@ func TestAccDockerService_partialReplicated(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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"), ), }, }, + CheckDestroy: checkAndRemoveImages, }) } -func TestAccDockerService_basicGlobal(t *testing.T) { +func TestAccDockerService_globalReplicationMode(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec = { @@ -471,15 +606,16 @@ func TestAccDockerService_basicGlobal(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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"), ), }, }, + CheckDestroy: checkAndRemoveImages, }) } -func TestAccDockerService_GlobalAndReplicated(t *testing.T) { +func TestAccDockerService_ConflictingGlobalAndReplicated(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -504,17 +640,26 @@ func TestAccDockerService_GlobalAndReplicated(t *testing.T) { ExpectError: regexp.MustCompile(`.*conflicts with.*`), }, }, + CheckDestroy: checkAndRemoveImages, }) } -func TestAccDockerService_GlobalWithConvergeConfig(t *testing.T) { +func TestAccDockerService_ConflictingGlobalModeAndConverge(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" name = "tftest-service-basic" task_spec { container_spec = { @@ -533,425 +678,322 @@ func TestAccDockerService_GlobalWithConvergeConfig(t *testing.T) { ExpectError: regexp.MustCompile(`.*conflicts with.*`), }, }, - }) -} - -func TestAccDockerService_updateImage(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-fnf-service-up-image" - task_spec { - container_spec = { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-image"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-fnf-service-up-image" - task_spec { - container_spec = { - image = "127.0.0.1:15000/tftest-service:v2" - stop_grace_period = "10s" - - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-image"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseReplicas(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-fnf-service-up-crihiadr" - - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - start_period = "0s" - retries = 2 - } - - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8081" - } - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-fnf-service-up-crihiadr" - - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 4 - } - - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 6 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "6"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-fnf-service-up-crihiadr" - - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 4 - } - - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 3 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-fnf-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "3"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, + CheckDestroy: checkAndRemoveImages, }) } // Converging tests +func TestAccDockerService_privateImageConverge(t *testing.T) { + registry := "127.0.0.1:15000" + image := "127.0.0.1:15000/tftest-service" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + provider "docker" { + alias = "private" + registry_auth { + address = "%s" + } + } + + resource "docker_service" "bar" { + provider = "docker.private" + name = "tftest-service-bar" + task_spec { + container_spec { + image = "%s" + } + } + mode { + replicated { + replicas = 2 + } + } + + converge_config { + delay = "7s" + timeout = "3m" + } + } + `, registry, image), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchResourceAttr("docker_service.bar", "id", serviceIDRegex), + resource.TestCheckResourceAttr("docker_service.bar", "name", "tftest-service-bar"), + resource.TestMatchResourceAttr("docker_service.bar", "task_spec.0.container_spec.0.image", regexp.MustCompile(`127.0.0.1:15000/tftest-service:latest@sha256.*`)), + ), + }, + }, + CheckDestroy: checkAndRemoveImages, + }) +} + +func TestAccDockerService_updateMultiplePropertiesConverge(t *testing.T) { + // Step 1 + configData := "ewogICJwcmVmaXgiOiAiMTIzIgp9" + secretData := "ewogICJrZXkiOiAiUVdFUlRZIgp9" + image := "127.0.0.1:15000/tftest-service:v1" + mounts := ` + { + source = "${docker_volume.foo.name}" + target = "/mount/test" + type = "volume" + read_only = true + volume_options { + labels { + env = "dev" + terraform = "true" + } + } + } + ` + 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 := ` + { + 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 := ` + { + source = "${docker_volume.foo.name}" + target = "/mount/test" + type = "volume" + read_only = true + volume_options { + labels { + env = "dev" + terraform = "true" + } + } + }, + { + source = "${docker_volume.foo2.name}" + target = "/mount/test2" + type = "volume" + read_only = true + volume_options { + labels { + env = "dev" + terraform = "true" + } + } + } + ` + hosts2 := ` + { + host = "testhost2" + ip = "10.0.2.2" + } + ` + logging2 := ` + name = "json-file" + + options { + max-size = "15m" + max-file = "5" + } + ` + healthcheckTimeout2 := "800ms" + replicas2 := 6 + portsSpec2 := ` + { + target_port = "8080" + published_port = "8081" + }, + { + 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) }, + Providers: testAccProviders, + 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", "1"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), + 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.3541714906.target_port", "8080"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.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"), + 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", "1"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), + 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.3541714906.target_port", "8080"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.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"), + 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", "1"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), + resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), + 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.3541714906.target_port", "8080"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), + resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.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"), + 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: checkAndRemoveImages, + }) +} + func TestAccDockerService_nonExistingPrivateImageConverge(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -1024,21 +1066,29 @@ func TestAccDockerService_nonExistingPublicImageConverge(t *testing.T) { }) } -func TestAccDockerService_basicConvergeAndStopGracefully(t *testing.T) { +func TestAccDockerService_convergeAndStopGracefully(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { Config: ` + provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } + } + resource "docker_service" "foo" { + provider = "docker.private" 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", "http://localhost:8080/health"] + test = ["CMD", "curl", "-f", "localhost:8080/health"] interval = "5s" timeout = "2s" start_period = "0s" @@ -1068,2521 +1118,229 @@ func TestAccDockerService_basicConvergeAndStopGracefully(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-basic-converge"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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: checkAndRemoveImages, }) } func TestAccDockerService_updateFailsAndRollbackConverge(t *testing.T) { + image := "127.0.0.1:15000/tftest-service:v1" + imageFail := "127.0.0.1:15000/tftest-service:v3" + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-updateFailsAndRollbackConverge" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - - healthcheck { - test = ["CMD", "curl", "-f", "http://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" - } - } - `, + 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.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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: ` - resource "docker_service" "foo" { - name = "tftest-service-updateFailsAndRollbackConverge" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v3" - healthcheck { - test = ["CMD", "curl", "-f", "http://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" - } - } - `, + 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.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), + 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: checkAndRemoveImages, }) } -func TestAccDockerService_updateNetworksConverge(t *testing.T) { - // t.Skip("Skipped because response from daemon is not always consistent") - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_network" "test_network" { - name = "tftest-network" - driver = "overlay" - } - - resource "docker_network" "test_network2" { - name = "tftest-network2" - driver = "overlay" - } - - resource "docker_service" "foo" { - name = "tftest-service-up-network" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } - networks = ["${docker_network.test_network.id}"] - } - mode { - replicated { - replicas = 2 - } - } - - endpoint_spec { - mode = "vip" - - ports { - target_port = "8080" - } - } - - converge_config { - delay = "5s" - timeout = "60s" - } - - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-network"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "1"), - ), - }, - { - Config: ` - resource "docker_network" "test_network" { - name = "tftest-network" - driver = "overlay" - } - - resource "docker_network" "test_network2" { - name = "tftest-network2" - driver = "overlay" - } - - resource "docker_service" "foo" { - name = "tftest-service-up-network" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } - networks = ["${docker_network.test_network2.id}"] - } - mode { - replicated { - replicas = 2 - } - } - - endpoint_spec { - mode = "vip" - - ports { - target_port = "8080" - } - } - - converge_config { - delay = "5s" - timeout = "60s" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-network"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "1"), - ), - }, - { - Config: ` - resource "docker_network" "test_network" { - name = "tftest-network" - driver = "overlay" - } - - resource "docker_network" "test_network2" { - name = "tftest-network2" - driver = "overlay" - } - - resource "docker_service" "foo" { - name = "tftest-service-up-network" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } - networks = [ - "${docker_network.test_network.id}", - "${docker_network.test_network2.id}" - ] - } - - mode { - replicated { - replicas = 2 - } - } - - endpoint_spec { - mode = "vip" - - ports { - target_port = "8080" - } - } - - converge_config { - delay = "5s" - timeout = "60s" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-network"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "2"), - ), - }, - }, - }) +const updateMultiplePropertiesConfigConverge = ` +provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } } -func TestAccDockerService_updateMountsConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_volume" "foo" { - name = "tftest-volume" - } - - resource "docker_service" "foo" { - name = "tftest-service-up-mounts" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - mounts = [ - { - source = "${docker_volume.foo.name}" - target = "/mount/test" - type = "volume" - read_only = true - volume_options { - labels { - env = "dev" - terraform = "true" - } - } - } - ] - stop_grace_period = "10s" - } - } - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "5s" - timeout = "60s" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-mounts"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.#", "1"), - ), - }, - { - Config: ` - resource "docker_volume" "foo" { - name = "tftest-volume" - } - - resource "docker_volume" "foo2" { - name = "tftest-volume2" - } - - resource "docker_service" "foo" { - name = "tftest-service-up-mounts" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - mounts = [ - { - source = "${docker_volume.foo.name}" - target = "/mount/test" - type = "volume" - read_only = true - volume_options { - labels { - env = "dev" - terraform = "true" - } - } - }, - { - source = "${docker_volume.foo2.name}" - target = "/mount/test2" - type = "volume" - read_only = true - volume_options { - labels { - env = "dev" - terraform = "true" - } - } - } - ] - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "5s" - timeout = "60s" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-mounts"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.mounts.#", "2"), - ), - }, - }, - }) +resource "docker_volume" "foo" { + name = "tftest-volume" } -func TestAccDockerService_updateHostsConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-hosts" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - hosts = [ - { - host = "testhost" - ip = "10.0.1.0" - } - ] - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-hosts"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "1"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-hosts" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - hosts = [ - { - host = "testhost2" - ip = "10.0.2.2" - } - ] - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-hosts"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "1"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-hosts" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - hosts = [ - { - host = "testhost" - ip = "10.0.1.0" - }, - { - host = "testhost2" - ip = "10.0.2.2" - } - ] - stop_grace_period = "10s" - } - } - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-hosts"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.hosts.#", "2"), - ), - }, - }, - }) +resource "docker_volume" "foo2" { + name = "tftest-volume2" } -func TestAccDockerService_updateLoggingConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-logging" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } +resource "docker_config" "service_config" { + name = "tftest-myconfig-${uuid()}" + data = "%s" - log_driver { - name = "json-file" - - options { - max-size = "10m" - max-file = "3" - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-logging"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - 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-size", "10m"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "3"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-logging" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } - log_driver { - name = "json-file" - - options { - max-size = "15m" - max-file = "5" - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-logging"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - 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-size", "15m"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "5"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-logging" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-logging"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - ), - }, - }, - }) + lifecycle { + ignore_changes = ["name"] + create_before_destroy = true + } } -func TestAccDockerService_updateHealthcheckConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-healthcheck" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" +resource "docker_secret" "service_secret" { + name = "tftest-tftest-mysecret-${replace(timestamp(),":", ".")}" + data = "%s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-healthcheck"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-healthcheck" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 2 - } - } - } - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-healthcheck"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - }, - }) + lifecycle { + ignore_changes = ["name"] + create_before_destroy = true + } } -func TestAccDockerService_updateIncreaseReplicasConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-increase-replicas" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" +resource "docker_service" "foo" { + provider = "docker.private" + name = "tftest-fnf-service-up-crihiadr" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - } - } + task_spec { + container_spec { + image = "%s" - mode { - replicated { - replicas = 1 - } - } + mounts = [ + %s + ] - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } + hosts = [ + %s + ] - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } + configs = [ + { + config_id = "${docker_config.service_config.id}" + config_name = "${docker_config.service_config.name}" + file_name = "/configs.json" + }, + ] - converge_config { - delay = "7s" - timeout = "3m" - } + secrets = [ + { + secret_id = "${docker_secret.service_secret.id}" + secret_name = "${docker_secret.service_secret.name}" + file_name = "/secrets.json" + }, + ] - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-increase-replicas"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-increase-replicas" - 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 = "%s" + timeout = "%s" + start_period = "1s" + retries = 2 + } - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - } - } + stop_grace_period = "10s" + } - mode { - replicated { - replicas = 3 - } - } + log_driver { + %s + } - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } + } - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } + mode { + replicated { + replicas = %d + } + } - converge_config { - delay = "7s" - timeout = "3m" - } + update_config { + parallelism = 1 + delay = "1s" + failure_action = "pause" + monitor = "1s" + max_failure_ratio = "0.1" + order = "start-first" + } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-increase-replicas"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "3"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) + endpoint_spec { + ports = [ + %s + ] + } + + converge_config { + delay = "7s" + timeout = "3m" + } +} +` + +const updateFailsAndRollbackConvergeConfig = ` +provider "docker" { + alias = "private" + registry_auth { + address = "127.0.0.1:15000" + } } -func TestAccDockerService_updateDecreaseReplicasConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-decrease-replicas" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" +resource "docker_service" "foo" { + provider = "docker.private" + name = "tftest-service-updateFailsAndRollbackConverge" + task_spec { + container_spec { + image = "%s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - } - } + healthcheck { + test = ["CMD", "curl", "-f", "localhost:8080/health"] + interval = "5s" + timeout = "2s" + start_period = "0s" + retries = 4 + } + } + } - mode { - replicated { - replicas = 5 - } - } + mode { + replicated { + replicas = 2 + } + } - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } + update_config { + parallelism = 1 + delay = "5s" + failure_action = "rollback" + monitor = "10s" + max_failure_ratio = "0.0" + order = "stop-first" + } - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } + rollback_config { + parallelism = 1 + delay = "1s" + failure_action = "pause" + monitor = "4s" + max_failure_ratio = "0.0" + order = "stop-first" + } - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-decrease-replicas"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "5"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-decrease-replicas" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" + endpoint_spec { + mode = "vip" + ports { + name = "random" + protocol = "tcp" + target_port = "8080" + published_port = "8080" + publish_mode = "ingress" + } + } - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - } - } - - mode { - replicated { - replicas = 1 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-decrease-replicas"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateImageConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-image" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-image"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-image" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-image"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } - - converge_config { - delay = "7s" - timeout = "30s" - } - - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-config"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8080" - } - } - - converge_config { - delay = "7s" - timeout = "30s" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-config"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigAndSecretConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_secret" "service_secret" { - name = "tftest-tftest-mysecret-${replace(timestamp(),":", ".")}" - data = "ewogICJrZXkiOiAiUVdFUlRZIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config-secret" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - - 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", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-config-secret"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"), - 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", "endpoint_spec.0.ports.#", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_secret" "service_secret" { - name = "tftest-tftest-mysecret-${replace(timestamp(),":", ".")}" - data = "ewogICJrZXkiOiAiUVdFUlRZIgp9" # UPDATED to YXCVB - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config-secret" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - - 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", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-config-secret"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.order", "start-first"), - 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", "endpoint_spec.0.ports.#", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_updatePortConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-port" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8081" - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-port"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_service" "foo" { - name = "tftest-service-up-port" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - stop_grace_period = "10s" - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - } - } - - mode { - replicated { - replicas = 4 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-port"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "4"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigReplicasImageAndHealthConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-crihc" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8081" - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-crihc"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-crihc" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 4 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-crihc"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "4"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigAndDecreaseReplicasConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config-dec-repl" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 5 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-config-dec-repl"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "5"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-config-dec-repl" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 1 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.1" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_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-up-config-dec-repl"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.max_failure_ratio", "0.1"), - 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.83412866.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.83412866.published_port", "8080"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_updateConfigReplicasImageAndHealthIncreaseAndDecreaseReplicasConverge(t *testing.T) { - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiMTIzIgp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-crihiadr" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v1" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "1s" - timeout = "500ms" - retries = 2 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 2 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports { - target_port = "8080" - published_port = "8081" - } - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v1"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "2"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "500ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "2"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" # UPDATED to prefix: 567 - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-crihiadr" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 6 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "6"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - { - Config: ` - resource "docker_config" "service_config" { - name = "tftest-myconfig-${uuid()}" - data = "ewogICJwcmVmaXgiOiAiNTY3Igp9" - - lifecycle { - ignore_changes = ["name"] - create_before_destroy = true - } - } - - resource "docker_service" "foo" { - name = "tftest-service-up-crihiadr" - task_spec { - container_spec { - image = "127.0.0.1:15000/tftest-service:v2" - configs = [ - { - config_id = "${docker_config.service_config.id}" - config_name = "${docker_config.service_config.name}" - file_name = "/configs.json" - }, - ] - healthcheck { - test = ["CMD", "curl", "-f", "http://localhost:8080/health"] - interval = "2s" - timeout = "800ms" - retries = 4 - } - stop_grace_period = "10s" - } - } - - mode { - replicated { - replicas = 3 - } - } - - update_config { - parallelism = 1 - delay = "1s" - failure_action = "pause" - monitor = "1s" - max_failure_ratio = "0.5" - order = "start-first" - } - - endpoint_spec { - ports = [ - { - target_port = "8080" - published_port = "8081" - }, - { - target_port = "8080" - published_port = "8082" - } - ] - } - - converge_config { - delay = "7s" - timeout = "3m" - } - } - `, - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.foo", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.foo", "name", "tftest-service-up-crihiadr"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.image", "127.0.0.1:15000/tftest-service:v2"), - resource.TestCheckResourceAttr("docker_service.foo", "mode.0.replicated.0.replicas", "3"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.parallelism", "1"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.delay", "1s"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.failure_action", "pause"), - resource.TestCheckResourceAttr("docker_service.foo", "update_config.0.monitor", "1s"), - 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.3541714906.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.3541714906.published_port", "8081"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.target_port", "8080"), - resource.TestCheckResourceAttr("docker_service.foo", "endpoint_spec.0.ports.1884078451.published_port", "8082"), - 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", "http://localhost:8080/health"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.interval", "2s"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.timeout", "800ms"), - resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.container_spec.0.healthcheck.0.retries", "4"), - ), - }, - }, - }) -} - -func TestAccDockerService_privateConverge(t *testing.T) { - registry := os.Getenv("DOCKER_REGISTRY_ADDRESS") - image := os.Getenv("DOCKER_PRIVATE_IMAGE") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: fmt.Sprintf(` - provider "docker" { - alias = "private" - registry_auth { - address = "%s" - } - } - - resource "docker_service" "bar" { - provider = "docker.private" - name = "tftest-service-bar" - task_spec { - container_spec { - image = "%s" - } - } - mode { - replicated { - replicas = 2 - } - } - } - `, registry, image), - Check: resource.ComposeTestCheckFunc( - resource.TestMatchResourceAttr("docker_service.bar", "id", serviceIDRegex), - resource.TestCheckResourceAttr("docker_service.bar", "name", "tftest-service-bar"), - resource.TestCheckResourceAttr("docker_service.bar", "task_spec.0.container_spec.0.image", image), - ), - }, - }, - }) + 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 { client := testAccProvider.Meta().(*ProviderConfig).DockerClient @@ -3602,3 +1360,56 @@ func isServiceRemoved(serviceName string) resource.TestCheckFunc { 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(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(context.Background(), types.ImageListOptions{ + Filters: filters, + }) + if err != nil { + return err + } + + retryDeleteCount := 0 + for i := 0; i < len(images); { + image := images[i] + _, err := client.ImageRemove(context.Background(), 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(context.Background(), types.ImageListOptions{ + Filters: filters, + }) + if err != nil { + return err + } + + if len(imagesAfterDelete) != 0 { + return fmt.Errorf("Expected images of patter '%s' to be deleted, but there is/are still %d", imagePattern, len(imagesAfterDelete)) + } + + return nil +} diff --git a/go.mod b/go.mod index 840b287b..040241c6 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/blang/semver v3.5.1+incompatible // indirect github.com/docker/distribution v0.0.0-20180522175653-f0cc92778478 // indirect github.com/docker/docker v0.0.0-20180530012807-65bd038fc5e4 + github.com/docker/docker-credential-helpers v0.6.2 github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.0.0-20171221200356-d59758554a3d github.com/gogo/protobuf v0.0.0-20180121160031-26de2f9a7d3b // indirect diff --git a/go.sum b/go.sum index 35b0772a..ac5296a2 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/docker/distribution v0.0.0-20180522175653-f0cc92778478 h1:yCzgNaIN+6i github.com/docker/distribution v0.0.0-20180522175653-f0cc92778478/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v0.0.0-20180530012807-65bd038fc5e4 h1:l3X/U8oT8xvht8bB/kHUntALLPIYSzmUf4Wyr6UnGWU= github.com/docker/docker v0.0.0-20180530012807-65bd038fc5e4/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.2 h1:CrW9H1VMf3a4GrtyAi7IUJjkJVpwBBpX0+mvkvYJaus= +github.com/docker/docker-credential-helpers v0.6.2/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.0.0-20171221200356-d59758554a3d h1:QEYTOUa4JROW5CSAHU8qc0rD8uhx1tsUkBsSDk8P5eM= diff --git a/scripts/runAccTests.sh b/scripts/runAccTests.sh index 6559077d..4629fd6d 100755 --- a/scripts/runAccTests.sh +++ b/scripts/runAccTests.sh @@ -2,32 +2,63 @@ set -e log() { - echo "" - echo "##################################" - echo "-------> $1" - echo "##################################" + echo "####################" + echo "## -> $1 " + echo "####################" } setup() { - export DOCKER_REGISTRY_ADDRESS="127.0.0.1:15000" - export DOCKER_REGISTRY_USER="testuser" - export DOCKER_REGISTRY_PASS="testpwd" - export DOCKER_PRIVATE_IMAGE="127.0.0.1:15000/tftest-service:v1" - sh "$(pwd)"/scripts/testing/setup_private_registry.sh + # Create self signed certs + mkdir -p "$(pwd)"/scripts/testing/certs + openssl req \ + -newkey rsa:2048 \ + -nodes \ + -x509 \ + -days 365 \ + -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=127.0.0.1" \ + -keyout "$(pwd)"/scripts/testing/certs/registry_auth.key \ + -out "$(pwd)"/scripts/testing/certs/registry_auth.crt + # Create auth + mkdir -p "$(pwd)"/scripts/testing/auth + # Start registry + docker run --rm --entrypoint htpasswd registry:2 -Bbn testuser testpwd > "$(pwd)"/scripts/testing/auth/htpasswd + docker run -d -p 15000:5000 --rm --name private_registry \ + -v "$(pwd)"/scripts/testing/auth:/auth \ + -e "REGISTRY_AUTH=htpasswd" \ + -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ + -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ + -v "$(pwd)"/scripts/testing/certs:/certs \ + -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry_auth.crt" \ + -e "REGISTRY_HTTP_TLS_KEY=/certs/registry_auth.key" \ + registry:2 + # wait a bit for travis... + sleep 5 + # Login to private registry + docker login -u testuser -p testpwd 127.0.0.1:15000 + # Build private images + for i in $(seq 1 3); do + docker build -t tftest-service --build-arg JS_FILE_PATH=server_v${i}.js "$(pwd)"/scripts/testing -f "$(pwd)"/scripts/testing/Dockerfile + docker tag tftest-service 127.0.0.1:15000/tftest-service:v${i} + docker push 127.0.0.1:15000/tftest-service:v${i} + docker tag tftest-service 127.0.0.1:15000/tftest-service + docker push 127.0.0.1:15000/tftest-service + done + # Remove images from host machine before starting the tests + for i in $(docker images -aq 127.0.0.1:15000/tftest-service); do docker rmi -f "$i"; done } run() { + go clean -testcache TF_ACC=1 go test ./docker -v -timeout 120m # for a single test comment the previous line and uncomment the next line - #TF_LOG=INFO TF_ACC=1 go test -v github.com/terraform-providers/terraform-provider-docker/docker -run ^TestAccDockerContainer_port$ -timeout 360s + #TF_LOG=INFO TF_ACC=1 go test -v ./docker -run ^TestAccDockerImage_data_private_config_file$ -timeout 360s # keep the return value for the scripts to fail and clean properly return $? } cleanup() { - unset DOCKER_REGISTRY_ADDRESS DOCKER_REGISTRY_USER DOCKER_REGISTRY_PASS DOCKER_PRIVATE_IMAGE echo "### unsetted env ###" for p in $(docker container ls -f 'name=private_registry' -q); do docker stop $p; done echo "### stopped private registry ###" diff --git a/scripts/testing/dockerconfig.json b/scripts/testing/dockerconfig.json new file mode 100644 index 00000000..02ffea58 --- /dev/null +++ b/scripts/testing/dockerconfig.json @@ -0,0 +1,7 @@ +{ + "auths": { + "127.0.0.1:15000": { + "auth": "dGVzdHVzZXI6dGVzdHB3ZA==" + } + } +} \ No newline at end of file diff --git a/scripts/testing/setup_private_registry.sh b/scripts/testing/setup_private_registry.sh deleted file mode 100755 index c7236b4b..00000000 --- a/scripts/testing/setup_private_registry.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -set -e - -# Create private registry -## Create self signed certs -mkdir -p "$(pwd)"/scripts/testing/certs -openssl req \ - -newkey rsa:2048 \ - -nodes \ - -x509 \ - -days 365 \ - -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=127.0.0.1" \ - -keyout "$(pwd)"/scripts/testing/certs/registry_auth.key \ - -out "$(pwd)"/scripts/testing/certs/registry_auth.crt -## Create auth -mkdir -p "$(pwd)"/scripts/testing/auth -# Start registry -docker run --rm --entrypoint htpasswd registry:2 -Bbn testuser testpwd > "$(pwd)"/scripts/testing/auth/htpasswd -docker run -d -p 15000:5000 --rm --name private_registry \ - -v "$(pwd)"/scripts/testing/auth:/auth \ - -e "REGISTRY_AUTH=htpasswd" \ - -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ - -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ - -v "$(pwd)"/scripts/testing/certs:/certs \ - -e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry_auth.crt" \ - -e "REGISTRY_HTTP_TLS_KEY=/certs/registry_auth.key" \ - registry:2 -# wait a bit for travis... -sleep 5 -# Login to private registry -docker login -u testuser -p testpwd 127.0.0.1:15000 -# Build private images -for i in $(seq 1 3); do - docker build -t tftest-service --build-arg JS_FILE_PATH=server_v${i}.js "$(pwd)"/scripts/testing -f "$(pwd)"/scripts/testing/Dockerfile - docker tag tftest-service 127.0.0.1:15000/tftest-service:v${i} - docker push 127.0.0.1:15000/tftest-service:v${i} -done diff --git a/vendor/github.com/docker/docker-credential-helpers/LICENSE b/vendor/github.com/docker/docker-credential-helpers/LICENSE new file mode 100644 index 00000000..1ea555e2 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 David Calavera + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/docker/docker-credential-helpers/client/client.go b/vendor/github.com/docker/docker-credential-helpers/client/client.go new file mode 100644 index 00000000..d1d0434c --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/client.go @@ -0,0 +1,121 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + + "github.com/docker/docker-credential-helpers/credentials" +) + +// isValidCredsMessage checks if 'msg' contains invalid credentials error message. +// It returns whether the logs are free of invalid credentials errors and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername. +func isValidCredsMessage(msg string) error { + if credentials.IsCredentialsMissingServerURLMessage(msg) { + return credentials.NewErrCredentialsMissingServerURL() + } + + if credentials.IsCredentialsMissingUsernameMessage(msg) { + return credentials.NewErrCredentialsMissingUsername() + } + + return nil +} + +// Store uses an external program to save credentials. +func Store(program ProgramFunc, creds *credentials.Credentials) error { + cmd := program("store") + + buffer := new(bytes.Buffer) + if err := json.NewEncoder(buffer).Encode(creds); err != nil { + return err + } + cmd.Input(buffer) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// Get executes an external program to get the credentials from a native store. +func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error) { + cmd := program("get") + cmd.Input(strings.NewReader(serverURL)) + + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if credentials.IsErrCredentialsNotFoundMessage(t) { + return nil, credentials.NewErrCredentialsNotFound() + } + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t) + } + + resp := &credentials.Credentials{ + ServerURL: serverURL, + } + + if err := json.NewDecoder(bytes.NewReader(out)).Decode(resp); err != nil { + return nil, err + } + + return resp, nil +} + +// Erase executes a program to remove the server credentials from the native store. +func Erase(program ProgramFunc, serverURL string) error { + cmd := program("erase") + cmd.Input(strings.NewReader(serverURL)) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t) + } + + return nil +} + +// List executes a program to list server credentials in the native store. +func List(program ProgramFunc) (map[string]string, error) { + cmd := program("list") + cmd.Input(strings.NewReader("unused")) + out, err := cmd.Output() + if err != nil { + t := strings.TrimSpace(string(out)) + + if isValidErr := isValidCredsMessage(t); isValidErr != nil { + err = isValidErr + } + + return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t) + } + + var resp map[string]string + if err = json.NewDecoder(bytes.NewReader(out)).Decode(&resp); err != nil { + return nil, err + } + + return resp, nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/client/command.go b/vendor/github.com/docker/docker-credential-helpers/client/command.go new file mode 100644 index 00000000..8da33430 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/client/command.go @@ -0,0 +1,56 @@ +package client + +import ( + "fmt" + "io" + "os" + "os/exec" +) + +// Program is an interface to execute external programs. +type Program interface { + Output() ([]byte, error) + Input(in io.Reader) +} + +// ProgramFunc is a type of function that initializes programs based on arguments. +type ProgramFunc func(args ...string) Program + +// NewShellProgramFunc creates programs that are executed in a Shell. +func NewShellProgramFunc(name string) ProgramFunc { + return NewShellProgramFuncWithEnv(name, nil) +} + +// NewShellProgramFuncWithEnv creates programs that are executed in a Shell with environment variables +func NewShellProgramFuncWithEnv(name string, env *map[string]string) ProgramFunc { + return func(args ...string) Program { + return &Shell{cmd: createProgramCmdRedirectErr(name, args, env)} + } +} + +func createProgramCmdRedirectErr(commandName string, args []string, env *map[string]string) *exec.Cmd { + programCmd := exec.Command(commandName, args...) + programCmd.Env = os.Environ() + if env != nil { + for k, v := range *env { + programCmd.Env = append(programCmd.Env, fmt.Sprintf("%s=%s", k, v)) + } + } + programCmd.Stderr = os.Stderr + return programCmd +} + +// Shell invokes shell commands to talk with a remote credentials helper. +type Shell struct { + cmd *exec.Cmd +} + +// Output returns responses from the remote credentials helper. +func (s *Shell) Output() ([]byte, error) { + return s.cmd.Output() +} + +// Input sets the input to send to a remote credentials helper. +func (s *Shell) Input(in io.Reader) { + s.cmd.Stdin = in +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go new file mode 100644 index 00000000..da8b594e --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go @@ -0,0 +1,186 @@ +package credentials + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strings" +) + +// Credentials holds the information shared between docker and the credentials store. +type Credentials struct { + ServerURL string + Username string + Secret string +} + +// isValid checks the integrity of Credentials object such that no credentials lack +// a server URL or a username. +// It returns whether the credentials are valid and the error if it isn't. +// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername +func (c *Credentials) isValid() (bool, error) { + if len(c.ServerURL) == 0 { + return false, NewErrCredentialsMissingServerURL() + } + + if len(c.Username) == 0 { + return false, NewErrCredentialsMissingUsername() + } + + return true, nil +} + +// CredsLabel holds the way Docker credentials should be labeled as such in credentials stores that allow labelling. +// That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain, +// Windows credentials manager and Linux libsecret. Default value is "Docker Credentials" +var CredsLabel = "Docker Credentials" + +// SetCredsLabel is a simple setter for CredsLabel +func SetCredsLabel(label string) { + CredsLabel = label +} + +// Serve initializes the credentials helper and parses the action argument. +// This function is designed to be called from a command line interface. +// It uses os.Args[1] as the key for the action. +// It uses os.Stdin as input and os.Stdout as output. +// This function terminates the program with os.Exit(1) if there is an error. +func Serve(helper Helper) { + var err error + if len(os.Args) != 2 { + err = fmt.Errorf("Usage: %s ", os.Args[0]) + } + + if err == nil { + err = HandleCommand(helper, os.Args[1], os.Stdin, os.Stdout) + } + + if err != nil { + fmt.Fprintf(os.Stdout, "%v\n", err) + os.Exit(1) + } +} + +// HandleCommand uses a helper and a key to run a credential action. +func HandleCommand(helper Helper, key string, in io.Reader, out io.Writer) error { + switch key { + case "store": + return Store(helper, in) + case "get": + return Get(helper, in, out) + case "erase": + return Erase(helper, in) + case "list": + return List(helper, out) + case "version": + return PrintVersion(out) + } + return fmt.Errorf("Unknown credential action `%s`", key) +} + +// Store uses a helper and an input reader to save credentials. +// The reader must contain the JSON serialization of a Credentials struct. +func Store(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + var creds Credentials + if err := json.NewDecoder(buffer).Decode(&creds); err != nil { + return err + } + + if ok, err := creds.isValid(); !ok { + return err + } + + return helper.Add(&creds) +} + +// Get retrieves the credentials for a given server url. +// The reader must contain the server URL to search. +// The writer is used to write the JSON serialization of the credentials. +func Get(helper Helper, reader io.Reader, writer io.Writer) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + username, secret, err := helper.Get(serverURL) + if err != nil { + return err + } + + resp := Credentials{ + ServerURL: serverURL, + Username: username, + Secret: secret, + } + + buffer.Reset() + if err := json.NewEncoder(buffer).Encode(resp); err != nil { + return err + } + + fmt.Fprint(writer, buffer.String()) + return nil +} + +// Erase removes credentials from the store. +// The reader must contain the server URL to remove. +func Erase(helper Helper, reader io.Reader) error { + scanner := bufio.NewScanner(reader) + + buffer := new(bytes.Buffer) + for scanner.Scan() { + buffer.Write(scanner.Bytes()) + } + + if err := scanner.Err(); err != nil && err != io.EOF { + return err + } + + serverURL := strings.TrimSpace(buffer.String()) + if len(serverURL) == 0 { + return NewErrCredentialsMissingServerURL() + } + + return helper.Delete(serverURL) +} + +//List returns all the serverURLs of keys in +//the OS store as a list of strings +func List(helper Helper, writer io.Writer) error { + accts, err := helper.List() + if err != nil { + return err + } + return json.NewEncoder(writer).Encode(accts) +} + +//PrintVersion outputs the current version. +func PrintVersion(writer io.Writer) error { + fmt.Fprintln(writer, Version) + return nil +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/error.go b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go new file mode 100644 index 00000000..fe6a5aef --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/error.go @@ -0,0 +1,102 @@ +package credentials + +const ( + // ErrCredentialsNotFound standardizes the not found error, so every helper returns + // the same message and docker can handle it properly. + errCredentialsNotFoundMessage = "credentials not found in native keychain" + + // ErrCredentialsMissingServerURL and ErrCredentialsMissingUsername standardize + // invalid credentials or credentials management operations + errCredentialsMissingServerURLMessage = "no credentials server URL" + errCredentialsMissingUsernameMessage = "no credentials username" +) + +// errCredentialsNotFound represents an error +// raised when credentials are not in the store. +type errCredentialsNotFound struct{} + +// Error returns the standard error message +// for when the credentials are not in the store. +func (errCredentialsNotFound) Error() string { + return errCredentialsNotFoundMessage +} + +// NewErrCredentialsNotFound creates a new error +// for when the credentials are not in the store. +func NewErrCredentialsNotFound() error { + return errCredentialsNotFound{} +} + +// IsErrCredentialsNotFound returns true if the error +// was caused by not having a set of credentials in a store. +func IsErrCredentialsNotFound(err error) bool { + _, ok := err.(errCredentialsNotFound) + return ok +} + +// IsErrCredentialsNotFoundMessage returns true if the error +// was caused by not having a set of credentials in a store. +// +// This function helps to check messages returned by an +// external program via its standard output. +func IsErrCredentialsNotFoundMessage(err string) bool { + return err == errCredentialsNotFoundMessage +} + +// errCredentialsMissingServerURL represents an error raised +// when the credentials object has no server URL or when no +// server URL is provided to a credentials operation requiring +// one. +type errCredentialsMissingServerURL struct{} + +func (errCredentialsMissingServerURL) Error() string { + return errCredentialsMissingServerURLMessage +} + +// errCredentialsMissingUsername represents an error raised +// when the credentials object has no username or when no +// username is provided to a credentials operation requiring +// one. +type errCredentialsMissingUsername struct{} + +func (errCredentialsMissingUsername) Error() string { + return errCredentialsMissingUsernameMessage +} + +// NewErrCredentialsMissingServerURL creates a new error for +// errCredentialsMissingServerURL. +func NewErrCredentialsMissingServerURL() error { + return errCredentialsMissingServerURL{} +} + +// NewErrCredentialsMissingUsername creates a new error for +// errCredentialsMissingUsername. +func NewErrCredentialsMissingUsername() error { + return errCredentialsMissingUsername{} +} + +// IsCredentialsMissingServerURL returns true if the error +// was an errCredentialsMissingServerURL. +func IsCredentialsMissingServerURL(err error) bool { + _, ok := err.(errCredentialsMissingServerURL) + return ok +} + +// IsCredentialsMissingServerURLMessage checks for an +// errCredentialsMissingServerURL in the error message. +func IsCredentialsMissingServerURLMessage(err string) bool { + return err == errCredentialsMissingServerURLMessage +} + +// IsCredentialsMissingUsername returns true if the error +// was an errCredentialsMissingUsername. +func IsCredentialsMissingUsername(err error) bool { + _, ok := err.(errCredentialsMissingUsername) + return ok +} + +// IsCredentialsMissingUsernameMessage checks for an +// errCredentialsMissingUsername in the error message. +func IsCredentialsMissingUsernameMessage(err string) bool { + return err == errCredentialsMissingUsernameMessage +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go new file mode 100644 index 00000000..135acd25 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/helper.go @@ -0,0 +1,14 @@ +package credentials + +// Helper is the interface a credentials store helper must implement. +type Helper interface { + // Add appends credentials to the store. + Add(*Credentials) error + // Delete removes credentials from the store. + Delete(serverURL string) error + // Get retrieves credentials from the store. + // It returns username and secret as strings. + Get(serverURL string) (string, string, error) + // List returns the stored serverURLs and their associated usernames. + List() (map[string]string, error) +} diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/version.go b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go new file mode 100644 index 00000000..ecc23e68 --- /dev/null +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go @@ -0,0 +1,4 @@ +package credentials + +// Version holds a string describing the current version +const Version = "0.6.2" diff --git a/vendor/modules.txt b/vendor/modules.txt index 7cfd2fcc..0c8d4ea9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -69,6 +69,9 @@ github.com/docker/docker/api github.com/docker/docker/api/types/events github.com/docker/docker/api/types/image github.com/docker/docker/api/types/time +# github.com/docker/docker-credential-helpers v0.6.2 +github.com/docker/docker-credential-helpers/client +github.com/docker/docker-credential-helpers/credentials # github.com/docker/go-connections v0.4.0 github.com/docker/go-connections/nat github.com/docker/go-connections/sockets