diff --git a/docs/resources/image.md b/docs/resources/image.md index 3c559b30..666c5552 100644 --- a/docs/resources/image.md +++ b/docs/resources/image.md @@ -88,6 +88,7 @@ resource "docker_image" "zoo" { - `build` (Block Set, Max: 1) Configuration to build an image. Please see [docker build command reference](https://docs.docker.com/engine/reference/commandline/build/#options) too. (see [below for nested schema](#nestedblock--build)) - `force_remove` (Boolean) If true, then the image is removed forcibly when the resource is destroyed. - `keep_locally` (Boolean) If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker local storage on destroy operation. +- `platform` (String) The platform to use when pulling the image. Defaults to the platform of the current machine. - `pull_trigger` (String, Deprecated) A value which cause an image pull when changed - `pull_triggers` (Set of String) List of values which cause an image pull when changed. This is used to store the image digest from the registry when using the [docker_registry_image](../data-sources/registry_image.md). - `triggers` (Map of String) A map of arbitrary strings that, when changed, will force the `docker_image` resource to be replaced. This can be used to rebuild an image when contents of source code folders change diff --git a/internal/provider/resource_docker_container_funcs.go b/internal/provider/resource_docker_container_funcs.go index a97482cf..fbb7b796 100644 --- a/internal/provider/resource_docker_container_funcs.go +++ b/internal/provider/resource_docker_container_funcs.go @@ -51,7 +51,7 @@ func resourceDockerContainerCreate(ctx context.Context, d *schema.ResourceData, client := meta.(*ProviderConfig).DockerClient authConfigs := meta.(*ProviderConfig).AuthConfigs image := d.Get("image").(string) - _, err = findImage(ctx, image, client, authConfigs) + _, err = findImage(ctx, image, client, authConfigs, "") if err != nil { return diag.Errorf("Unable to create container with image %s: %s", image, err) } diff --git a/internal/provider/resource_docker_image.go b/internal/provider/resource_docker_image.go index 02478b5f..7f572ae7 100644 --- a/internal/provider/resource_docker_image.go +++ b/internal/provider/resource_docker_image.go @@ -161,6 +161,13 @@ func resourceDockerImage() *schema.Resource { Optional: true, ForceNew: true, }, + "platform": { + Type: schema.TypeString, + Description: "The platform to use when pulling the image. Defaults to the platform of the current machine.", + Optional: true, + Default: "", + ForceNew: true, + }, }, } } diff --git a/internal/provider/resource_docker_image_funcs.go b/internal/provider/resource_docker_image_funcs.go index cccd39f0..e6adf67f 100644 --- a/internal/provider/resource_docker_image_funcs.go +++ b/internal/provider/resource_docker_image_funcs.go @@ -43,7 +43,7 @@ func resourceDockerImageCreate(ctx context.Context, d *schema.ResourceData, meta } } } - apiImage, err := findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs) + apiImage, err := findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs, d.Get("platform").(string)) if err != nil { return diag.Errorf("Unable to read Docker image into resource: %s", err) } @@ -89,7 +89,7 @@ func resourceDockerImageUpdate(ctx context.Context, d *schema.ResourceData, meta // the value of "latest" or others client := meta.(*ProviderConfig).DockerClient imageName := d.Get("name").(string) - apiImage, err := findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs) + apiImage, err := findImage(ctx, imageName, client, meta.(*ProviderConfig).AuthConfigs, d.Get("platform").(string)) if err != nil { return diag.Errorf("Unable to read Docker image into resource: %s", err) } @@ -195,7 +195,7 @@ func fetchLocalImages(ctx context.Context, data *Data, client *client.Client) er return nil } -func pullImage(ctx context.Context, data *Data, client *client.Client, authConfig *AuthConfigs, image string) error { +func pullImage(ctx context.Context, data *Data, client *client.Client, authConfig *AuthConfigs, image string, platform string) error { pullOpts := parseImageOptions(image) auth := types.AuthConfig{} @@ -210,6 +210,7 @@ func pullImage(ctx context.Context, data *Data, client *client.Client, authConfi out, err := client.ImagePull(ctx, image, types.ImagePullOptions{ RegistryAuth: base64.URLEncoding.EncodeToString(encodedJSON), + Platform: platform, }) if err != nil { return fmt.Errorf("error pulling image %s: %w", image, err) @@ -282,7 +283,7 @@ func parseImageOptions(image string) internalPullImageOptions { return pullOpts } -func findImage(ctx context.Context, imageName string, client *client.Client, authConfig *AuthConfigs) (*types.ImageSummary, error) { +func findImage(ctx context.Context, imageName string, client *client.Client, authConfig *AuthConfigs, platform string) (*types.ImageSummary, error) { if imageName == "" { return nil, fmt.Errorf("empty image name is not allowed") } @@ -292,7 +293,6 @@ func findImage(ctx context.Context, imageName string, client *client.Client, aut if err := fetchLocalImages(ctx, &data, client); err != nil { return nil, err } - foundImage, err := searchLocalImages(ctx, client, data, imageName) if err != nil { return nil, fmt.Errorf("findImage1: error looking up local image %q: %w", imageName, err) @@ -300,8 +300,7 @@ func findImage(ctx context.Context, imageName string, client *client.Client, aut if foundImage != nil { return foundImage, nil } - - if err := pullImage(ctx, &data, client, authConfig, imageName); err != nil { + if err := pullImage(ctx, &data, client, authConfig, imageName, platform); err != nil { return nil, fmt.Errorf("unable to pull image %s: %s", imageName, err) } diff --git a/internal/provider/resource_docker_image_test.go b/internal/provider/resource_docker_image_test.go index 8ed76a2d..57be2157 100644 --- a/internal/provider/resource_docker_image_test.go +++ b/internal/provider/resource_docker_image_test.go @@ -314,6 +314,25 @@ func TestAccDockerImage_tag_sha265(t *testing.T) { }) } +func TestAccDockerImage_platform(t *testing.T) { + ctx := context.Background() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: func(state *terraform.State) error { + return testAccDockerImageDestroy(ctx, state) + }, + Steps: []resource.TestStep{ + { + Config: loadTestConfiguration(t, RESOURCE, "docker_image", "testAccDockerImagePlatform"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("docker_image.foo", "image_id", "sha256:8336f9f1d0946781f428a155536995f0d8a31209d65997e2a379a23e7a441b78"), + ), + }, + }, + }) +} + func TestAccDockerImage_build(t *testing.T) { ctx := context.Background() wd, _ := os.Getwd() diff --git a/testdata/resources/docker_image/testAccDockerImagePlatform.tf b/testdata/resources/docker_image/testAccDockerImagePlatform.tf new file mode 100644 index 00000000..a3fbc573 --- /dev/null +++ b/testdata/resources/docker_image/testAccDockerImagePlatform.tf @@ -0,0 +1,4 @@ +resource "docker_image" "foo" { + name = "busybox:1.34.0" + platform = "linux/amd64" +}