diff --git a/docs/resources/image.md b/docs/resources/image.md
index 666c5552..477e1d7c 100644
--- a/docs/resources/image.md
+++ b/docs/resources/image.md
@@ -104,17 +104,68 @@ resource "docker_image" "zoo" {
### Nested Schema for `build`
+Optional:
+
+- `auth_config` (Block List) The configuration for the authentication (see [below for nested schema](#nestedblock--build--auth_config))
+- `build_arg` (Map of String) Set build-time variables
+- `build_args` (Map of String) Pairs for build-time variables in the form TODO
+- `build_id` (String) BuildID is an optional identifier that can be passed together with the build request. The same identifier can be used to gracefully cancel the build with the cancel request.
+- `cache_from` (List of String) Images to consider as cache sources
+- `cgroup_parent` (String) Optional parent cgroup for the container
+- `context` (String) Value to specify the build context. Currently, only a `PATH` context is supported. You can use the helper function '${path.cwd}/context-dir'. Please see https://docs.docker.com/build/building/context/ for more information about build contexts.
+- `cpu_period` (Number) The length of a CPU period in microseconds
+- `cpu_quota` (Number) Microseconds of CPU time that the container can get in a CPU period
+- `cpu_set_cpus` (String) CPUs in which to allow execution (e.g., `0-3`, `0`, `1`)
+- `cpu_set_mems` (String) MEMs in which to allow execution (`0-3`, `0`, `1`)
+- `cpu_shares` (Number) CPU shares (relative weight)
+- `dockerfile` (String) Name of the Dockerfile. Defaults to `Dockerfile`.
+- `extra_hosts` (List of String) A list of hostnames/IP mappings to add to the container’s /etc/hosts file. Specified in the form ["hostname:IP"]
+- `force_remove` (Boolean) Always remove intermediate containers
+- `isolation` (String) Isolation represents the isolation technology of a container. The supported values are
+- `label` (Map of String) Set metadata for an image
+- `labels` (Map of String) User-defined key/value metadata
+- `memory` (Number) Set memory limit for build
+- `memory_swap` (Number) Total memory (memory + swap), -1 to enable unlimited swap
+- `network_mode` (String) Set the networking mode for the RUN instructions during build
+- `no_cache` (Boolean) Do not use the cache when building the image
+- `path` (String, Deprecated) Context path
+- `platform` (String) Set platform if server is multi-platform capable
+- `pull_parent` (Boolean) Attempt to pull the image even if an older image exists locally
+- `remote_context` (String) A Git repository URI or HTTP/HTTPS context URI
+- `remove` (Boolean) Remove intermediate containers after a successful build. Defaults to `true`.
+- `security_opt` (List of String) The security options
+- `session_id` (String) Set an ID for the build session
+- `shm_size` (Number) Size of /dev/shm in bytes. The size must be greater than 0
+- `squash` (Boolean) If true the new layers are squashed into a new image with a single new layer
+- `suppress_output` (Boolean) Suppress the build output and print image ID on success
+- `tag` (List of String) Name and optionally a tag in the 'name:tag' format
+- `target` (String) Set the target build stage to build
+- `ulimit` (Block List) Configuration for ulimits (see [below for nested schema](#nestedblock--build--ulimit))
+- `version` (String) Version of the underlying builder to use
+
+
+### Nested Schema for `build.auth_config`
+
Required:
-- `path` (String) Context path
+- `host_name` (String) hostname of the registry
Optional:
-- `build_arg` (Map of String) Set build-time variables
-- `dockerfile` (String) Name of the Dockerfile. Defaults to `Dockerfile`.
-- `force_remove` (Boolean) Always remove intermediate containers
-- `label` (Map of String) Set metadata for an image
-- `no_cache` (Boolean) Do not use cache when building the image
-- `remove` (Boolean) Remove intermediate containers after a successful build. Defaults to `true`.
-- `tag` (List of String) Name and optionally a tag in the 'name:tag' format
-- `target` (String) Set the target build stage to build
+- `auth` (String) the auth token
+- `email` (String) the user emal
+- `identity_token` (String) the identity token
+- `password` (String) the registry password
+- `registry_token` (String) the registry token
+- `server_address` (String) the server address
+- `user_name` (String) the registry user name
+
+
+
+### Nested Schema for `build.ulimit`
+
+Required:
+
+- `hard` (Number) soft limit
+- `name` (String) type of ulimit, e.g. `nofile`
+- `soft` (Number) hard limit
diff --git a/docs/resources/registry_image.md b/docs/resources/registry_image.md
index 1394deb4..bebf9bc5 100644
--- a/docs/resources/registry_image.md
+++ b/docs/resources/registry_image.md
@@ -33,10 +33,10 @@ resource "docker_registry_image" "helloworld" {
### Optional
-- `build` (Block List, Max: 1) Definition for building the image (see [below for nested schema](#nestedblock--build))
+- `build` (Block List, Max: 1, Deprecated) Definition for building the image (see [below for nested schema](#nestedblock--build))
- `insecure_skip_verify` (Boolean) If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false`
- `keep_remotely` (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 registry on destroy operation. Defaults to `false`
-- `triggers` (Map of String) A map of arbitrary strings that, when changed, will force the `docker_registry_image` resource to be replaced. This can be used to rebuild an image when contents of source code folders change or to repush a local image
+- `triggers` (Map of String) A map of arbitrary strings that, when changed, will force the `docker_registry_image` resource to be replaced. This can be used to repush a local image
### Read-Only
diff --git a/internal/provider/resource_docker_image.go b/internal/provider/resource_docker_image.go
index 7f572ae7..768d9565 100644
--- a/internal/provider/resource_docker_image.go
+++ b/internal/provider/resource_docker_image.go
@@ -96,8 +96,9 @@ func resourceDockerImage() *schema.Resource {
"path": {
Type: schema.TypeString,
Description: "Context path",
- Required: true,
+ Optional: true,
ForceNew: true,
+ Deprecated: "Use context instead. This attribute will be removed in the next major release.",
},
"dockerfile": {
Type: schema.TypeString,
@@ -114,27 +115,12 @@ func resourceDockerImage() *schema.Resource {
Type: schema.TypeString,
},
},
- "force_remove": {
- Type: schema.TypeBool,
- Description: "Always remove intermediate containers",
- Optional: true,
- },
"remove": {
Type: schema.TypeBool,
- Description: "Remove intermediate containers after a successful build. Defaults to `true`.",
+ Description: "Remove intermediate containers after a successful build. Defaults to `true`.",
Default: true,
Optional: true,
},
- "no_cache": {
- Type: schema.TypeBool,
- Description: "Do not use cache when building the image",
- Optional: true,
- },
- "target": {
- Type: schema.TypeString,
- Description: "Set the target build stage to build",
- Optional: true,
- },
"build_arg": {
Type: schema.TypeMap,
Description: "Set build-time variables",
@@ -152,6 +138,270 @@ func resourceDockerImage() *schema.Resource {
Type: schema.TypeString,
},
},
+ "suppress_output": {
+ Type: schema.TypeBool,
+ Description: "Suppress the build output and print image ID on success",
+ Optional: true,
+ ForceNew: true,
+ },
+ "remote_context": {
+ Type: schema.TypeString,
+ Description: "A Git repository URI or HTTP/HTTPS context URI",
+ Optional: true,
+ ForceNew: true,
+ },
+ "no_cache": {
+ Type: schema.TypeBool,
+ Description: "Do not use the cache when building the image",
+ Optional: true,
+ ForceNew: true,
+ },
+ "force_remove": {
+ Type: schema.TypeBool,
+ Description: "Always remove intermediate containers",
+ Optional: true,
+ ForceNew: true,
+ },
+ "pull_parent": {
+ Type: schema.TypeBool,
+ Description: "Attempt to pull the image even if an older image exists locally",
+ Optional: true,
+ ForceNew: true,
+ },
+ "isolation": {
+ Type: schema.TypeString,
+ Description: "Isolation represents the isolation technology of a container. The supported values are ",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cpu_set_cpus": {
+ Type: schema.TypeString,
+ Description: "CPUs in which to allow execution (e.g., `0-3`, `0`, `1`)",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cpu_set_mems": {
+ Type: schema.TypeString,
+ Description: "MEMs in which to allow execution (`0-3`, `0`, `1`)",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cpu_shares": {
+ Type: schema.TypeInt,
+ Description: "CPU shares (relative weight)",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cpu_quota": {
+ Type: schema.TypeInt,
+ Description: "Microseconds of CPU time that the container can get in a CPU period",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cpu_period": {
+ Type: schema.TypeInt,
+ Description: "The length of a CPU period in microseconds",
+ Optional: true,
+ ForceNew: true,
+ },
+ "memory": {
+ Type: schema.TypeInt,
+ Description: "Set memory limit for build",
+ Optional: true,
+ ForceNew: true,
+ },
+ "memory_swap": {
+ Type: schema.TypeInt,
+ Description: "Total memory (memory + swap), -1 to enable unlimited swap",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cgroup_parent": {
+ Type: schema.TypeString,
+ Description: "Optional parent cgroup for the container",
+ Optional: true,
+ ForceNew: true,
+ },
+ "network_mode": {
+ Type: schema.TypeString,
+ Description: "Set the networking mode for the RUN instructions during build",
+ Optional: true,
+ ForceNew: true,
+ },
+ "shm_size": {
+ Type: schema.TypeInt,
+ Description: "Size of /dev/shm in bytes. The size must be greater than 0",
+ Optional: true,
+ ForceNew: true,
+ },
+ "ulimit": {
+ Type: schema.TypeList,
+ Description: "Configuration for ulimits",
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Description: "type of ulimit, e.g. `nofile`",
+ Required: true,
+ ForceNew: true,
+ },
+ "hard": {
+ Type: schema.TypeInt,
+ Description: "soft limit",
+ Required: true,
+ ForceNew: true,
+ },
+ "soft": {
+ Type: schema.TypeInt,
+ Description: "hard limit",
+ Required: true,
+ ForceNew: true,
+ },
+ },
+ },
+ },
+ "build_args": {
+ Type: schema.TypeMap,
+ Description: "Pairs for build-time variables in the form TODO",
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ Description: "The argument",
+ },
+ },
+ "auth_config": {
+ Type: schema.TypeList,
+ Description: "The configuration for the authentication",
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "host_name": {
+ Type: schema.TypeString,
+ Description: "hostname of the registry",
+ Required: true,
+ },
+ "user_name": {
+ Type: schema.TypeString,
+ Description: "the registry user name",
+ Optional: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Description: "the registry password",
+ Optional: true,
+ },
+ "auth": {
+ Type: schema.TypeString,
+ Description: "the auth token",
+ Optional: true,
+ },
+ "email": {
+ Type: schema.TypeString,
+ Description: "the user emal",
+ Optional: true,
+ },
+ "server_address": {
+ Type: schema.TypeString,
+ Description: "the server address",
+ Optional: true,
+ },
+ "identity_token": {
+ Type: schema.TypeString,
+ Description: "the identity token",
+ Optional: true,
+ },
+ "registry_token": {
+ Type: schema.TypeString,
+ Description: "the registry token",
+ Optional: true,
+ },
+ },
+ },
+ },
+ "context": {
+ Type: schema.TypeString,
+ Description: "Value to specify the build context. Currently, only a `PATH` context is supported. You can use the helper function '${path.cwd}/context-dir'. Please see https://docs.docker.com/build/building/context/ for more information about build contexts.",
+ Optional: true,
+ ForceNew: true,
+ },
+ "labels": {
+ Type: schema.TypeMap,
+ Description: "User-defined key/value metadata",
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ Description: "The key/value pair",
+ },
+ },
+ "squash": {
+ Type: schema.TypeBool,
+ Description: "If true the new layers are squashed into a new image with a single new layer",
+ Optional: true,
+ ForceNew: true,
+ },
+ "cache_from": {
+ Type: schema.TypeList,
+ Description: "Images to consider as cache sources",
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ Description: "The image",
+ },
+ },
+ "security_opt": {
+ Type: schema.TypeList,
+ Description: "The security options",
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ Description: "The option",
+ },
+ },
+ "extra_hosts": {
+ Type: schema.TypeList,
+ Description: "A list of hostnames/IP mappings to add to the container’s /etc/hosts file. Specified in the form [\"hostname:IP\"]",
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ Description: "",
+ },
+ },
+ "target": {
+ Type: schema.TypeString,
+ Description: "Set the target build stage to build",
+ Optional: true,
+ ForceNew: true,
+ },
+ "session_id": {
+ Type: schema.TypeString,
+ Description: "Set an ID for the build session",
+ Optional: true,
+ ForceNew: true,
+ },
+ "platform": {
+ Type: schema.TypeString,
+ Description: "Set platform if server is multi-platform capable",
+ Optional: true,
+ ForceNew: true,
+ },
+ "version": {
+ Type: schema.TypeString,
+ Description: "Version of the underlying builder to use",
+ Optional: true,
+ ForceNew: true,
+ },
+ "build_id": {
+ Type: schema.TypeString,
+ Description: "BuildID is an optional identifier that can be passed together with the build request. The same identifier can be used to gracefully cancel the build with the cancel request.",
+ Optional: true,
+ ForceNew: true,
+ },
},
},
},
diff --git a/internal/provider/resource_docker_image_funcs.go b/internal/provider/resource_docker_image_funcs.go
index e6adf67f..8c6d0e0b 100644
--- a/internal/provider/resource_docker_image_funcs.go
+++ b/internal/provider/resource_docker_image_funcs.go
@@ -325,9 +325,8 @@ func buildDockerImage(ctx context.Context, rawBuild map[string]interface{}, imag
err error
)
- buildOptions := types.ImageBuildOptions{}
-
- buildOptions.Dockerfile = rawBuild["dockerfile"].(string)
+ log.Printf("[DEBUG] Building docker image")
+ buildOptions := createImageBuildOptions(rawBuild)
tags := []string{imageName}
for _, t := range rawBuild["tag"].([]interface{}) {
@@ -335,29 +334,26 @@ func buildDockerImage(ctx context.Context, rawBuild map[string]interface{}, imag
}
buildOptions.Tags = tags
- buildOptions.ForceRemove = rawBuild["force_remove"].(bool)
- buildOptions.Remove = rawBuild["remove"].(bool)
- buildOptions.NoCache = rawBuild["no_cache"].(bool)
- buildOptions.Target = rawBuild["target"].(string)
-
- buildArgs := make(map[string]*string)
- for k, v := range rawBuild["build_arg"].(map[string]interface{}) {
- val := v.(string)
- buildArgs[k] = &val
+ // Supporting both "path" and "context" for backwards compatibility
+ // TODO: remove "path" in the next major release
+ pathAttribute := rawBuild["path"].(string)
+ contextAttribute := rawBuild["context"].(string)
+ if len(pathAttribute) == 0 && len(contextAttribute) == 0 {
+ return errors.New("one of path or context must be configured")
}
- buildOptions.BuildArgs = buildArgs
- log.Printf("[DEBUG] Build Args: %v\n", buildArgs)
- labels := make(map[string]string)
- for k, v := range rawBuild["label"].(map[string]interface{}) {
- labels[k] = v.(string)
+ buildContext := ""
+ if len(pathAttribute) > 0 {
+ // add existingAttribute to provider create API call
+ buildContext = pathAttribute
+ } else {
+ // add newAttribute to provider create API call
+ buildContext = contextAttribute
}
- buildOptions.Labels = labels
- log.Printf("[DEBUG] Labels: %v\n", labels)
-
+ // End backwards compatibility, remove until here
enableBuildKitIfSupported(ctx, client, &buildOptions)
- buildCtx, relDockerfile, err := prepareBuildContext(rawBuild["path"].(string), buildOptions.Dockerfile)
+ buildCtx, relDockerfile, err := prepareBuildContext(buildContext, buildOptions.Dockerfile)
if err != nil {
return err
}
diff --git a/internal/provider/resource_docker_image_test.go b/internal/provider/resource_docker_image_test.go
index 57be2157..cde3ec7d 100644
--- a/internal/provider/resource_docker_image_test.go
+++ b/internal/provider/resource_docker_image_test.go
@@ -13,12 +13,115 @@ import (
"testing"
"github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/container"
+ "github.com/docker/go-units"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
var contentDigestRegexp = regexp.MustCompile(`\A[A-Za-z0-9_\+\.-]+:[A-Fa-f0-9]+\z`)
+func TestAccDockerRegistryImageResource_mapping(t *testing.T) {
+ assert := func(condition bool, msg string) {
+ if !condition {
+ t.Errorf("assertion failed: wrong build parameter %s", msg)
+ }
+ }
+
+ dummyProvider := New("dev")()
+ dummyResource := dummyProvider.ResourcesMap["docker_image"]
+ dummyResource.CreateContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+
+ if value, ok := d.GetOk("build"); ok {
+ for _, rawBuild := range value.(*schema.Set).List() {
+ build := rawBuild.(map[string]interface{})
+ // build := d.Get("build").([]interface{})[0].(map[string]interface{})
+ options := createImageBuildOptions(build)
+
+ assert(options.SuppressOutput == true, "SuppressOutput")
+ assert(options.RemoteContext == "fooRemoteContext", "RemoteContext")
+ assert(options.NoCache == true, "NoCache")
+ assert(options.Remove == true, "Remove")
+ assert(options.ForceRemove == true, "ForceRemove")
+ assert(options.PullParent == true, "PullParent")
+ assert(options.Isolation == container.Isolation("hyperv"), "Isolation")
+ assert(options.CPUSetCPUs == "fooCpuSetCpus", "CPUSetCPUs")
+ assert(options.CPUSetMems == "fooCpuSetMems", "CPUSetMems")
+ assert(options.CPUShares == int64(4), "CPUShares")
+ assert(options.CPUQuota == int64(5), "CPUQuota")
+ assert(options.CPUPeriod == int64(6), "CPUPeriod")
+ assert(options.Memory == int64(1), "Memory")
+ assert(options.MemorySwap == int64(2), "MemorySwap")
+ assert(options.CgroupParent == "fooCgroupParent", "CgroupParent")
+ assert(options.NetworkMode == "fooNetworkMode", "NetworkMode")
+ assert(options.ShmSize == int64(3), "ShmSize")
+ assert(options.Dockerfile == "fooDockerfile", "Dockerfile")
+ assert(len(options.Ulimits) == 1, "Ulimits")
+ assert(reflect.DeepEqual(*options.Ulimits[0], units.Ulimit{
+ Name: "foo",
+ Hard: int64(1),
+ Soft: int64(2),
+ }), "Ulimits")
+ assert(len(options.BuildArgs) == 1, "BuildArgs")
+ // DevSkim: ignore DS137138
+ assert(*options.BuildArgs["HTTP_PROXY"] == "http://10.20.30.2:1234", "BuildArgs")
+ assert(len(options.AuthConfigs) == 1, "AuthConfigs")
+ assert(reflect.DeepEqual(options.AuthConfigs["foo.host"], types.AuthConfig{
+ Username: "fooUserName",
+ Password: "fooPassword",
+ Auth: "fooAuth",
+ Email: "fooEmail",
+ ServerAddress: "fooServerAddress",
+ IdentityToken: "fooIdentityToken",
+ RegistryToken: "fooRegistryToken",
+ }), "AuthConfigs")
+ assert(reflect.DeepEqual(options.Labels, map[string]string{"foo": "bar"}), "Labels")
+ assert(options.Squash == true, "Squash")
+ assert(reflect.DeepEqual(options.CacheFrom, []string{"fooCacheFrom", "barCacheFrom"}), "CacheFrom")
+ assert(reflect.DeepEqual(options.SecurityOpt, []string{"fooSecurityOpt", "barSecurityOpt"}), "SecurityOpt")
+ assert(reflect.DeepEqual(options.ExtraHosts, []string{"fooExtraHost", "barExtraHost"}), "ExtraHosts")
+ assert(options.Target == "fooTarget", "Target")
+ assert(options.SessionID == "fooSessionId", "SessionID")
+ assert(options.Platform == "fooPlatform", "Platform")
+ assert(options.Version == types.BuilderVersion("1"), "Version")
+ assert(options.BuildID == "fooBuildId", "BuildID")
+ // output
+ d.SetId("foo")
+ }
+ }
+ return nil
+ }
+ dummyResource.UpdateContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ return nil
+ }
+ dummyResource.DeleteContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ return nil
+ }
+ dummyResource.ReadContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ d.Set("id", "foo")
+ return nil
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: map[string]func() (*schema.Provider, error){
+ "docker": func() (*schema.Provider, error) {
+ return dummyProvider, nil
+ },
+ },
+ Steps: []resource.TestStep{
+ {
+ Config: loadTestConfiguration(t, RESOURCE, "docker_image", "testBuildDockerImageMappingConfig"),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet("docker_image.foo", "id"),
+ ),
+ },
+ },
+ })
+}
+
func TestAccDockerImage_basic(t *testing.T) {
// run a Docker container which refers the Docker image to test "force_remove" option
containerName := "test-docker-image-force-remove"
@@ -396,6 +499,113 @@ func TestAccDockerImage_buildOutsideContext(t *testing.T) {
})
}
+func TestAccDockerImageResource_build(t *testing.T) {
+ name := "tftest-dockerregistryimage:1.0"
+ wd, _ := os.Getwd()
+ context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context")), "\\", "\\\\")
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_image", "testBuildDockerImageNoKeepConfig"), name, context),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet("docker_image.foo", "image_id"),
+ ),
+ },
+ },
+ })
+}
+
+// Test for https://github.com/kreuzwerker/terraform-provider-docker/issues/249
+func TestAccDockerImageResource_whitelistDockerignore(t *testing.T) {
+ name := "tftest-dockerregistryimage-whitelistdockerignore:1.0"
+ wd, _ := os.Getwd()
+ context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_file_whitelist_dockerignore")), "\\", "\\\\")
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_image", "testDockerImageFilePermissions"), name, context, "Dockerfile"),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet("docker_image.file_permissions", "image_id"),
+ ),
+ },
+ },
+ })
+}
+
+// Tests for issue https://github.com/kreuzwerker/terraform-provider-docker/issues/293
+// First we check if we can build the docker_registry_image resource at all
+// TODO in a second step we want to check whether the file has the correct permissions
+func TestAccDockerImageResource_correctFilePermissions(t *testing.T) {
+ name := "tftest-dockerregistryimage-filepermissions:1.0"
+ wd, _ := os.Getwd()
+ context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_file_permissions")), "\\", "\\\\")
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_image", "testDockerImageFilePermissions"), name, context, "Dockerfile"),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet("docker_image.file_permissions", "image_id"),
+ ),
+ // TODO another check which starts the the newly built docker image and checks the file permissions to see if they are correct
+ },
+ },
+ })
+}
+
+func TestAccDockerImageResource_buildWithDockerignore(t *testing.T) {
+ name := "tftest-dockerregistryimage-ignore:1.0"
+ wd, _ := os.Getwd()
+ ctx := context.Background()
+ context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context_dockerignore")), "\\", "\\\\")
+ ignoredFile := context + "/to_be_ignored"
+ expectedSha := ""
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_image", "testBuildDockerImageNoKeepJustCache"), "one", name, context),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrSet("docker_image.one", "image_id"),
+ resource.TestCheckResourceAttrWith("docker_image.one", "image_id", func(value string) error {
+ expectedSha = value
+ return nil
+ }),
+ ),
+ },
+ {
+ PreConfig: func() {
+ // create a file that should be ignored
+ f, err := os.OpenFile(ignoredFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+ if err != nil {
+ panic("failed to create test file")
+ }
+ f.Close()
+ },
+ Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_image", "testBuildDockerImageNoKeepJustCache"), "two", name, context),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttrWith("docker_image.two", "image_id", func(value string) error {
+ if value != expectedSha {
+ return fmt.Errorf("Image sha256_digest changed, expected %#v, got %#v", expectedSha, value)
+ }
+ return nil
+ }),
+ ),
+ },
+ },
+ CheckDestroy: func(state *terraform.State) error {
+ return testAccDockerImageDestroy(ctx, state)
+ },
+ })
+}
+
func testAccImageCreated(resourceName string, image *types.ImageInspect) resource.TestCheckFunc {
return func(s *terraform.State) error {
ctx := context.Background()
diff --git a/internal/provider/resource_docker_registry_image.go b/internal/provider/resource_docker_registry_image.go
index 1fc40348..66a68c58 100644
--- a/internal/provider/resource_docker_registry_image.go
+++ b/internal/provider/resource_docker_registry_image.go
@@ -42,6 +42,7 @@ func resourceDockerRegistryImage() *schema.Resource {
Description: "Definition for building the image",
Optional: true,
MaxItems: 1,
+ Deprecated: "Use `docker_image` resource instead. This field will be removed in the next major release.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"suppress_output": {
@@ -333,7 +334,7 @@ func resourceDockerRegistryImage() *schema.Resource {
},
"triggers": {
- Description: "A map of arbitrary strings that, when changed, will force the `docker_registry_image` resource to be replaced. This can be used to rebuild an image when contents of source code folders change or to repush a local image",
+ Description: "A map of arbitrary strings that, when changed, will force the `docker_registry_image` resource to be replaced. This can be used to repush a local image",
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
diff --git a/internal/provider/resource_docker_registry_image_test.go b/internal/provider/resource_docker_registry_image_test.go
index 975df5c5..575a83c4 100644
--- a/internal/provider/resource_docker_registry_image_test.go
+++ b/internal/provider/resource_docker_registry_image_test.go
@@ -1,137 +1,17 @@
package provider
import (
- "context"
"fmt"
- "io/ioutil"
"os"
"path/filepath"
- "reflect"
"regexp"
"strings"
"testing"
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/api/types/container"
- "github.com/docker/go-units"
- "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)
-func TestAccDockerRegistryImageResource_mapping(t *testing.T) {
- assert := func(condition bool, msg string) {
- if !condition {
- t.Errorf("assertion failed: wrong build parameter %s", msg)
- }
- }
-
- dummyProvider := New("dev")()
- dummyResource := dummyProvider.ResourcesMap["docker_registry_image"]
- dummyResource.CreateContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- build := d.Get("build").([]interface{})[0].(map[string]interface{})
- options := createImageBuildOptions(build)
-
- assert(options.SuppressOutput == true, "SuppressOutput")
- assert(options.RemoteContext == "fooRemoteContext", "RemoteContext")
- assert(options.NoCache == true, "NoCache")
- assert(options.Remove == true, "Remove")
- assert(options.ForceRemove == true, "ForceRemove")
- assert(options.PullParent == true, "PullParent")
- assert(options.Isolation == container.Isolation("hyperv"), "Isolation")
- assert(options.CPUSetCPUs == "fooCpuSetCpus", "CPUSetCPUs")
- assert(options.CPUSetMems == "fooCpuSetMems", "CPUSetMems")
- assert(options.CPUShares == int64(4), "CPUShares")
- assert(options.CPUQuota == int64(5), "CPUQuota")
- assert(options.CPUPeriod == int64(6), "CPUPeriod")
- assert(options.Memory == int64(1), "Memory")
- assert(options.MemorySwap == int64(2), "MemorySwap")
- assert(options.CgroupParent == "fooCgroupParent", "CgroupParent")
- assert(options.NetworkMode == "fooNetworkMode", "NetworkMode")
- assert(options.ShmSize == int64(3), "ShmSize")
- assert(options.Dockerfile == "fooDockerfile", "Dockerfile")
- assert(len(options.Ulimits) == 1, "Ulimits")
- assert(reflect.DeepEqual(*options.Ulimits[0], units.Ulimit{
- Name: "foo",
- Hard: int64(1),
- Soft: int64(2),
- }), "Ulimits")
- assert(len(options.BuildArgs) == 1, "BuildArgs")
- // DevSkim: ignore DS137138
- assert(*options.BuildArgs["HTTP_PROXY"] == "http://10.20.30.2:1234", "BuildArgs")
- assert(len(options.AuthConfigs) == 1, "AuthConfigs")
- assert(reflect.DeepEqual(options.AuthConfigs["foo.host"], types.AuthConfig{
- Username: "fooUserName",
- Password: "fooPassword",
- Auth: "fooAuth",
- Email: "fooEmail",
- ServerAddress: "fooServerAddress",
- IdentityToken: "fooIdentityToken",
- RegistryToken: "fooRegistryToken",
- }), "AuthConfigs")
- assert(reflect.DeepEqual(options.Labels, map[string]string{"foo": "bar"}), "Labels")
- assert(options.Squash == true, "Squash")
- assert(reflect.DeepEqual(options.CacheFrom, []string{"fooCacheFrom", "barCacheFrom"}), "CacheFrom")
- assert(reflect.DeepEqual(options.SecurityOpt, []string{"fooSecurityOpt", "barSecurityOpt"}), "SecurityOpt")
- assert(reflect.DeepEqual(options.ExtraHosts, []string{"fooExtraHost", "barExtraHost"}), "ExtraHosts")
- assert(options.Target == "fooTarget", "Target")
- assert(options.SessionID == "fooSessionId", "SessionID")
- assert(options.Platform == "fooPlatform", "Platform")
- assert(options.Version == types.BuilderVersion("1"), "Version")
- assert(options.BuildID == "fooBuildId", "BuildID")
- // output
- d.SetId("foo")
- d.Set("sha256_digest", "bar")
- return nil
- }
- dummyResource.UpdateContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- return nil
- }
- dummyResource.DeleteContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- return nil
- }
- dummyResource.ReadContext = func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- d.Set("sha256_digest", "bar")
- return nil
- }
-
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: map[string]func() (*schema.Provider, error){
- "docker": func() (*schema.Provider, error) {
- return dummyProvider, nil
- },
- },
- Steps: []resource.TestStep{
- {
- Config: loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageMappingConfig"),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.foo", "sha256_digest"),
- ),
- },
- },
- })
-}
-
-func TestAccDockerRegistryImageResource_build(t *testing.T) {
- pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage:1.0")
- wd, _ := os.Getwd()
- context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context")), "\\", "\\\\")
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: providerFactories,
- Steps: []resource.TestStep{
- {
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepConfig"), pushOptions.Registry, pushOptions.Name, context),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.foo", "sha256_digest"),
- ),
- },
- },
- CheckDestroy: testDockerRegistryImageNotInRegistry(pushOptions),
- })
-}
func TestAccDockerRegistryImageResource_build_insecure_registry(t *testing.T) {
pushOptions := createPushImageOptions("127.0.0.1:15001/tftest-dockerregistryimage:1.0")
wd, _ := os.Getwd()
@@ -173,125 +53,6 @@ func TestAccDockerRegistryImageResource_buildAndKeep(t *testing.T) {
})
}
-func TestAccDockerRegistryImageResource_buildWithDockerignore(t *testing.T) {
- pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage-ignore:1.0")
- wd, _ := os.Getwd()
- context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context_dockerignore")), "\\", "\\\\")
- ignoredFile := context + "/to_be_ignored"
- expectedSha := ""
-
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: providerFactories,
- Steps: []resource.TestStep{
- {
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepJustCache"), pushOptions.Registry, "one", pushOptions.Name, context),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.one", "sha256_digest"),
- resource.TestCheckResourceAttrWith("docker_registry_image.one", "sha256_digest", func(value string) error {
- expectedSha = value
- return nil
- }),
- ),
- },
- {
- PreConfig: func() {
- // create a file that should be ignored
- f, err := os.OpenFile(ignoredFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- if err != nil {
- panic("failed to create test file")
- }
- f.Close()
- },
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageNoKeepJustCache"), pushOptions.Registry, "two", pushOptions.Name, context),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrWith("docker_registry_image.two", "sha256_digest", func(value string) error {
- if value != expectedSha {
- return fmt.Errorf("Image sha256_digest changed, expected %#v, got %#v", expectedSha, value)
- }
- return nil
- }),
- ),
- },
- },
- CheckDestroy: resource.ComposeTestCheckFunc(
- testDockerRegistryImageNotInRegistry(pushOptions),
- func(*terraform.State) error {
- err := os.Remove(ignoredFile)
- if err != nil {
- return fmt.Errorf("failed to remove ignored file: %w", err)
- }
- return nil
- },
- ),
- })
-}
-
-// Tests for issue https://github.com/kreuzwerker/terraform-provider-docker/issues/293
-// First we check if we can build the docker_registry_image resource at all
-// TODO in a second step we want to check whether the file has the correct permissions
-func TestAccDockerRegistryImageResource_correctFilePermissions(t *testing.T) {
- pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage-filepermissions:1.0")
- wd, _ := os.Getwd()
- context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_file_permissions")), "\\", "\\\\")
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: providerFactories,
- Steps: []resource.TestStep{
- {
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testDockerRegistryImageFilePermissions"), pushOptions.Registry, pushOptions.Name, context, "Dockerfile"),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.file_permissions", "sha256_digest"),
- ),
- // TODO another check which starts the the newly built docker image and checks the file permissions to see if they are correct
- },
- },
- })
-}
-
-// Test for https://github.com/kreuzwerker/terraform-provider-docker/issues/249
-func TestAccDockerRegistryImageResource_whitelistDockerignore(t *testing.T) {
- pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage-whitelistdockerignore:1.0")
- wd, _ := os.Getwd()
- context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_file_whitelist_dockerignore")), "\\", "\\\\")
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: providerFactories,
- Steps: []resource.TestStep{
- {
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testDockerRegistryImageFilePermissions"), pushOptions.Registry, pushOptions.Name, context, "Dockerfile"),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.file_permissions", "sha256_digest"),
- ),
- },
- },
- })
-}
-
-// Test for https://github.com/kreuzwerker/terraform-provider-docker/issues/249
-func TestAccDockerRegistryImageResource_DockerfileOutsideContext(t *testing.T) {
- pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage-dockerfileoutsidecontext:1.0")
- wd, _ := os.Getwd()
- dfPath := filepath.Join(wd, "..", "Dockerfile")
- if err := ioutil.WriteFile(dfPath, []byte(testDockerFileExample), 0o644); err != nil {
- t.Fatalf("failed to create a Dockerfile %s for test: %+v", dfPath, err)
- }
- defer os.Remove(dfPath)
- context := strings.ReplaceAll((filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_file_permissions")), "\\", "\\\\")
- resource.Test(t, resource.TestCase{
- PreCheck: func() { testAccPreCheck(t) },
- ProviderFactories: providerFactories,
- Steps: []resource.TestStep{
- {
- Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testDockerRegistryImageDockerfileOutsideContext"), pushOptions.Registry, pushOptions.Name, context),
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("docker_registry_image.outside_context", "sha256_digest"),
- ),
- },
- },
- })
-}
-
func TestAccDockerRegistryImageResource_pushMissingImage(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
diff --git a/testdata/resources/docker_image/testBuildDockerImageKeepConfig.tf b/testdata/resources/docker_image/testBuildDockerImageKeepConfig.tf
new file mode 100644
index 00000000..d68c7a15
--- /dev/null
+++ b/testdata/resources/docker_image/testBuildDockerImageKeepConfig.tf
@@ -0,0 +1,14 @@
+provider "docker" {
+ alias = "private"
+}
+resource "docker_image" "foo" {
+ provider = "docker.private"
+ name = "%s"
+
+ build {
+ context = "%s"
+ remove = true
+ force_remove = true
+ no_cache = true
+ }
+}
diff --git a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageMappingConfig.tf b/testdata/resources/docker_image/testBuildDockerImageMappingConfig.tf
similarity index 95%
rename from testdata/resources/docker_registry_image/testBuildDockerRegistryImageMappingConfig.tf
rename to testdata/resources/docker_image/testBuildDockerImageMappingConfig.tf
index 0ca9a067..53787491 100644
--- a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageMappingConfig.tf
+++ b/testdata/resources/docker_image/testBuildDockerImageMappingConfig.tf
@@ -1,6 +1,5 @@
-resource "docker_registry_image" "foo" {
+resource "docker_image" "foo" {
name = "localhost:15000/foo:1.0"
- insecure_skip_verify = true
build {
suppress_output = true
remote_context = "fooRemoteContext"
diff --git a/testdata/resources/docker_image/testBuildDockerImageNoKeepConfig.tf b/testdata/resources/docker_image/testBuildDockerImageNoKeepConfig.tf
new file mode 100644
index 00000000..d68c7a15
--- /dev/null
+++ b/testdata/resources/docker_image/testBuildDockerImageNoKeepConfig.tf
@@ -0,0 +1,14 @@
+provider "docker" {
+ alias = "private"
+}
+resource "docker_image" "foo" {
+ provider = "docker.private"
+ name = "%s"
+
+ build {
+ context = "%s"
+ remove = true
+ force_remove = true
+ no_cache = true
+ }
+}
diff --git a/testdata/resources/docker_image/testBuildDockerImageNoKeepJustCache.tf b/testdata/resources/docker_image/testBuildDockerImageNoKeepJustCache.tf
new file mode 100644
index 00000000..25d1bfcb
--- /dev/null
+++ b/testdata/resources/docker_image/testBuildDockerImageNoKeepJustCache.tf
@@ -0,0 +1,14 @@
+provider "docker" {
+ alias = "private"
+}
+resource "docker_image" "%s" {
+ provider = "docker.private"
+ name = "%s"
+
+ build {
+ context = "%s"
+ remove = false
+ force_remove = false
+ no_cache = false
+ }
+}
diff --git a/testdata/resources/docker_registry_image/testDockerRegistryImageFilePermissions.tf b/testdata/resources/docker_image/testDockerImageFilePermissions.tf
similarity index 59%
rename from testdata/resources/docker_registry_image/testDockerRegistryImageFilePermissions.tf
rename to testdata/resources/docker_image/testDockerImageFilePermissions.tf
index d1d35565..e97d29a0 100644
--- a/testdata/resources/docker_registry_image/testDockerRegistryImageFilePermissions.tf
+++ b/testdata/resources/docker_image/testDockerImageFilePermissions.tf
@@ -1,14 +1,10 @@
provider "docker" {
alias = "private"
- registry_auth {
- address = "%s"
- }
}
-resource "docker_registry_image" "file_permissions" {
+resource "docker_image" "file_permissions" {
provider = "docker.private"
name = "%s"
- insecure_skip_verify = true
build {
context = "%s"
diff --git a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageKeepConfig.tf b/testdata/resources/docker_registry_image/testBuildDockerRegistryImageKeepConfig.tf
index 0553e1c6..310d49c6 100644
--- a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageKeepConfig.tf
+++ b/testdata/resources/docker_registry_image/testBuildDockerRegistryImageKeepConfig.tf
@@ -4,16 +4,18 @@ provider "docker" {
address = "%s"
}
}
-resource "docker_registry_image" "foo" {
- provider = "docker.private"
- name = "%s"
- insecure_skip_verify = true
- keep_remotely = true
+resource "docker_image" "foo_image" {
+ provider = "docker.private"
+ name = "%s"
build {
- context = "%s"
- remove = true
- force_remove = true
- no_cache = true
+ context = "%s"
}
}
+
+resource "docker_registry_image" "foo" {
+ provider = "docker.private"
+ name = docker_image.foo_image.name
+ insecure_skip_verify = true
+ keep_remotely = true
+}
diff --git a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageNoKeepConfig.tf b/testdata/resources/docker_registry_image/testBuildDockerRegistryImageNoKeepConfig.tf
index 9c37bd0f..63108b94 100644
--- a/testdata/resources/docker_registry_image/testBuildDockerRegistryImageNoKeepConfig.tf
+++ b/testdata/resources/docker_registry_image/testBuildDockerRegistryImageNoKeepConfig.tf
@@ -4,15 +4,17 @@ provider "docker" {
address = "%s"
}
}
-resource "docker_registry_image" "foo" {
- provider = "docker.private"
- name = "%s"
- insecure_skip_verify = true
+resource "docker_image" "foo_image" {
+ provider = "docker.private"
+ name = "%s"
build {
- context = "%s"
- remove = true
- force_remove = true
- no_cache = true
+ context = "%s"
}
}
+
+resource "docker_registry_image" "foo" {
+ provider = "docker.private"
+ name = docker_image.foo_image.name
+ insecure_skip_verify = true
+}