terraform-provider-docker icon indicating copy to clipboard operation
terraform-provider-docker copied to clipboard

docker_container image forces replacement, when providing manual image (e.g. `nginx:1`)

Open nidotls opened this issue 3 years ago • 5 comments

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Terraform (and docker Provider) Version

Terraform v1.2.6
on linux_amd64
+ provider registry.terraform.io/kreuzwerker/docker v2.20.0

Affected Resource(s)

  • docker_container
  • docker_image
  • docker_registry_image

Terraform Configuration Files

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "2.20.0"
    }
  }
}

provider "docker" {
  host = "tcp://localhost:2375"
}

# get latest v1 image
data "docker_registry_image" "nginx" {
  name = "nginx:1"
}

# pull latest v1 image
resource "docker_image" "nginx" {
  name          = data.docker_registry_image.nginx.name
  pull_triggers = [data.docker_registry_image.nginx.sha256_digest]
  force_remove  = false
  keep_locally  = true
}

# use latest v1 image
resource "docker_container" "nginx" {
  name  = "nginx"
  image = docker_image.nginx.name
}

Debug Output

https://gist.github.com/thenilsdev/8961e159203c9429601d2eda0aa3f67a

Panic Output

Expected Behaviour

I use watchtower on a staging system. Watchtower checks in regular intervals if all images are up to date. Watchtower does not change nginx:1.2.2 to nginx:1.2.3 but compares the latest nginx:1 sha256 value with the existing one and updates to it. For watchtower to work, the image must be present in the container (e.g. nginx:1). In the examples here only the sha256 value is used, which would work but watchtower would not be able to update, since there is not raw image (e.g. nginx:1).

Output with my example code:

$ docker inspect nginx --format "{{.Image}}" 
sha256:b692a91e4e1582db97076184dae0b2f4a7a86b68c4fe6f91affa50ae06369bf5
$ docker inspect nginx --format "{{.Config.Image}}"
nginx:1

When I change docker_container image to docker_image.nginx.latest it outputs the following:

$ docker inspect nginx --format "{{.Image}}"
sha256:b692a91e4e1582db97076184dae0b2f4a7a86b68c4fe6f91affa50ae06369bf5
$ docker inspect nginx --format "{{.Config.Image}}"
sha256:b692a91e4e1582db97076184dae0b2f4a7a86b68c4fe6f91affa50ae06369bf5

So it would be enough if {{.Config.Image}} could be set from outside, so watchtower can check this value.

Actual Behaviour

Also without changing the registry, docker_container tries to change image from the sha256 to the image name every time

image             = "sha256:b692a91e4e1582db97076184dae0b2f4a7a86b68c4fe6f91affa50ae06369bf5" -> "nginx:1" # forces replacement

Steps to Reproduce

  1. terraform apply
  2. terraform apply
  3. terraform apply

Important Factoids

References

nidotls avatar Aug 09 '22 15:08 nidotls

To be honest, I still don't fully understand your problem regarding watchtower, but I still did some digging and research.

When running docker run --name nginx_test -d nginx:1.23 and docker inspect nginx_test --format '{{.Image}}' we get sha256:23fc29c9268915db1c2d3007ce77cd2d8b499707538b69ea78a2ae860258fac6.

That means, docker automatically converts the nginx:1.23 to an sha and sets the .Image value, we have no influence on that.

So, although the actual behaviour may seem like a bug, it is the correct behaviour. Theoretically, when we compose the diff, we could internally pull the sha value of nginx:1 and compare it to the actual value of the running container.

And the value {{.Config.Image}} is controlled by us, it is currently the same as the value of docker_container.image.

Could you elaborate again what your ideal solution would look like, so you can use watchtower?

Junkern avatar Dec 16 '22 15:12 Junkern

Hello, I just found the ticket and basically I have the same issue. I deployed a container with a static image name, and when I make another plan, terraform compare the sha256 of the image and the name, and consider that the image has changed. So each time, the container is destroyed then deployed. How can we fix that?

Tchoupinax avatar Mar 15 '23 21:03 Tchoupinax

@Junkern, reading your paragraph, I understood that Docker transform the name of the image to the hash string. So, I tried to replace the image by the sha256 string.

~ hostname  = "5b2166d4c897" -> (known after apply)
~ id        = "5b2166d4c897d9dcdbcbbb9547acf0b3f163e0370b7c2d9cb1eccf3276a039fb" -> (known after apply)
~ image     = "sha256:d125b701503f942a08f443c7bff54a0600ec327da3ce2fd46086db3d4b5d3ca3" -> "woodpeckerci/woodpecker-server:next-alpine" # forces replacement
~ init      = false -> (known after apply)
~ ipc_mode  = "private" -> (known after apply)

But the issue is, when I made the change, I discovered that terraform compared the sha256 with the name of the image... So I think that we could find the current name of the image.

  # docker_image.woodpecker_server_image must be replaced
-/+ resource "docker_image" "woodpecker_server_image" {
      ~ id          = "sha256:d125b701503f942a08f443c7bff54a0600ec327da3ce2fd46086db3d4b5d3ca3woodpeckerci/woodpecker-server:next-alpine" -> (known after apply)
      ~ image_id    = "sha256:d125b701503f942a08f443c7bff54a0600ec327da3ce2fd46086db3d4b5d3ca3" -> (known after apply)
      ~ name        = "woodpeckerci/woodpecker-server:next-alpine" -> "sha256:d125b701503f942a08f443c7bff54a0600ec327da3ce2fd46086db3d4b5d3ca3" # forces replacement
      ~ repo_digest = "woodpeckerci/woodpecker-server@sha256:caac5d3e341ebe2bc9e0883c072d27fc3656dd91fbc49c8d8eb304e9593996dd" -> (known after apply)
    }

Tchoupinax avatar Apr 04 '23 19:04 Tchoupinax

Hello,

Finally a friend of mine gave me the tip: we should not use the name attribute but the image_id, then it works. For the original example, it would do:

# use latest v1 image
resource "docker_container" "nginx" {
  name  = "nginx"
  image = docker_image.nginx.image_id
}

Note that even if it works well, when you list running containers on the machine to this the image, you only see hash and not more the name of the image. I don't know exactly if it's a bug or not (that it does not work with .name) but with image_id the expected behaviour with Terraform is reached!

Tchoupinax avatar Jul 11 '23 19:07 Tchoupinax

A workaround I found is using label.

data "docker_image" "ubuntu" {
  name = "ubuntu:22.04"
}

resource "docker_container" "ubuntu" {
  name  = "foo"
  image = data.docker_image.ubuntu.name
  labels = [
     {
        label = "image.id"
        value = data.docker_image.ubuntu.id
     }
  ]
  lifecycle {
    ignore_changes = [name]
  }
}

This way, we can keep the image name as ubuntu:22.04 and the resource will automatically changes if the image name changes in our code (since it will also change image.id and trigger a replacement)

vnghia avatar Jul 24 '23 06:07 vnghia