diff --git a/docker/resource_docker_container.go b/docker/resource_docker_container.go
index f69ac100..6f683512 100644
--- a/docker/resource_docker_container.go
+++ b/docker/resource_docker_container.go
@@ -448,11 +448,13 @@ func resourceDockerContainer() *schema.Resource {
},
"network_alias": &schema.Schema{
- Type: schema.TypeSet,
- Optional: true,
- ForceNew: true,
- Elem: &schema.Schema{Type: schema.TypeString},
- Set: schema.HashString,
+ Type: schema.TypeSet,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Set: schema.HashString,
+ Description: "Set an alias for the container in all specified networks",
+ Deprecated: "Use networks_advanced instead. Will be removed in v2.0.0",
},
"network_mode": &schema.Schema{
@@ -462,11 +464,44 @@ func resourceDockerContainer() *schema.Resource {
},
"networks": &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Set: schema.HashString,
+ Deprecated: "Use networks_advanced instead. Will be removed in v2.0.0",
+ },
+
+ "networks_advanced": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
- Elem: &schema.Schema{Type: schema.TypeString},
- Set: schema.HashString,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "name": &schema.Schema{
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "aliases": &schema.Schema{
+ Type: schema.TypeSet,
+ Optional: true,
+ ForceNew: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Set: schema.HashString,
+ },
+ "ipv4_address": &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ },
+ "ipv6_address": &schema.Schema{
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ },
+ },
+ },
},
"pid_mode": &schema.Schema{
diff --git a/docker/resource_docker_container_funcs.go b/docker/resource_docker_container_funcs.go
index fbdb934f..da6719a4 100644
--- a/docker/resource_docker_container_funcs.go
+++ b/docker/resource_docker_container_funcs.go
@@ -238,12 +238,28 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
d.SetId(retContainer.ID)
+ // Still support the deprecated properties
if v, ok := d.GetOk("networks"); ok {
+ if err := client.NetworkDisconnect(context.Background(), "bridge", retContainer.ID, false); err != nil {
+ if !strings.Contains(err.Error(), "is not connected to the network bridge") {
+ return fmt.Errorf("Unable to disconnect the default network: %s", err)
+ }
+ }
endpointConfig := &network.EndpointSettings{}
if v, ok := d.GetOk("network_alias"); ok {
endpointConfig.Aliases = stringSetToStringSlice(v.(*schema.Set))
}
+ for _, rawNetwork := range v.(*schema.Set).List() {
+ networkID := rawNetwork.(string)
+ if err := client.NetworkConnect(context.Background(), networkID, retContainer.ID, endpointConfig); err != nil {
+ return fmt.Errorf("Unable to connect to network '%s': %s", networkID, err)
+ }
+ }
+ }
+
+ // But overwrite them with the future ones, if set
+ if v, ok := d.GetOk("networks_advanced"); ok {
if err := client.NetworkDisconnect(context.Background(), "bridge", retContainer.ID, false); err != nil {
if !strings.Contains(err.Error(), "is not connected to the network bridge") {
return fmt.Errorf("Unable to disconnect the default network: %s", err)
@@ -251,7 +267,21 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
}
for _, rawNetwork := range v.(*schema.Set).List() {
- networkID := rawNetwork.(string)
+ networkID := rawNetwork.(map[string]interface{})["name"].(string)
+
+ endpointConfig := &network.EndpointSettings{}
+ endpointIPAMConfig := &network.EndpointIPAMConfig{}
+ if v, ok := rawNetwork.(map[string]interface{})["aliases"]; ok {
+ endpointConfig.Aliases = stringSetToStringSlice(v.(*schema.Set))
+ }
+ if v, ok := rawNetwork.(map[string]interface{})["ipv4_address"]; ok {
+ endpointIPAMConfig.IPv4Address = v.(string)
+ }
+ if v, ok := rawNetwork.(map[string]interface{})["ipv6_address"]; ok {
+ endpointIPAMConfig.IPv6Address = v.(string)
+ }
+ endpointConfig.IPAMConfig = endpointIPAMConfig
+
if err := client.NetworkConnect(context.Background(), networkID, retContainer.ID, endpointConfig); err != nil {
return fmt.Errorf("Unable to connect to network '%s': %s", networkID, err)
}
diff --git a/docker/resource_docker_container_test.go b/docker/resource_docker_container_test.go
index c6021f39..0d1b5e10 100644
--- a/docker/resource_docker_container_test.go
+++ b/docker/resource_docker_container_test.go
@@ -458,6 +458,7 @@ func TestAccDockerContainer_device(t *testing.T) {
},
})
}
+
func TestAccDockerContainer_port_internal(t *testing.T) {
var c types.ContainerJSON
@@ -504,6 +505,7 @@ func TestAccDockerContainer_port_internal(t *testing.T) {
},
})
}
+
func TestAccDockerContainer_port_multiple_internal(t *testing.T) {
var c types.ContainerJSON
@@ -619,6 +621,7 @@ func TestAccDockerContainer_port(t *testing.T) {
},
})
}
+
func TestAccDockerContainer_multiple_ports(t *testing.T) {
var c types.ContainerJSON
@@ -821,6 +824,123 @@ func TestAccDockerContainer_nostart(t *testing.T) {
})
}
+func TestAccDockerContainer_ipv4address(t *testing.T) {
+ var c types.ContainerJSON
+
+ testCheck := func(*terraform.State) error {
+ networks := c.NetworkSettings.Networks
+
+ if len(networks) != 1 {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if _, ok := networks["tf-test"]; !ok {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig == nil {
+ return fmt.Errorf("Container doesn't have a correct IPAM config")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig.IPv4Address != "10.0.1.123" {
+ return fmt.Errorf("Container doesn't have a correct IPv4 address")
+ }
+
+ return nil
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerContainerNetworksIPv4AddressConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testAccContainerRunning("docker_container.foo", &c),
+ testCheck,
+ resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccDockerContainer_ipv6address(t *testing.T) {
+ var c types.ContainerJSON
+
+ testCheck := func(*terraform.State) error {
+ networks := c.NetworkSettings.Networks
+
+ if len(networks) != 1 {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if _, ok := networks["tf-test"]; !ok {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig == nil {
+ return fmt.Errorf("Container doesn't have a correct IPAM config")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig.IPv6Address != "fd00:0:0:0::123" {
+ return fmt.Errorf("Container doesn't have a correct IPv6 address")
+ }
+
+ return nil
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerContainerNetworksIPv6AddressConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testAccContainerRunning("docker_container.foo", &c),
+ testCheck,
+ resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccDockerContainer_dualstackaddress(t *testing.T) {
+ var c types.ContainerJSON
+
+ testCheck := func(*terraform.State) error {
+ networks := c.NetworkSettings.Networks
+
+ if len(networks) != 1 {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if _, ok := networks["tf-test"]; !ok {
+ return fmt.Errorf("Container doesn't have a correct network")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig == nil {
+ return fmt.Errorf("Container doesn't have a correct IPAM config")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig.IPv4Address != "10.0.1.123" {
+ return fmt.Errorf("Container doesn't have a correct IPv4 address")
+ }
+ if c.NetworkSettings.Networks["tf-test"].IPAMConfig.IPv6Address != "fd00:0:0:0::123" {
+ return fmt.Errorf("Container doesn't have a correct IPv6 address")
+ }
+
+ return nil
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerContainerNetworksDualStackAddressConfig,
+ Check: resource.ComposeTestCheckFunc(
+ testAccContainerRunning("docker_container.foo", &c),
+ testCheck,
+ resource.TestCheckResourceAttr("docker_container.foo", "name", "tf-test"),
+ ),
+ },
+ },
+ })
+}
+
func testAccContainerRunning(n string, container *types.ContainerJSON) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@@ -1073,8 +1193,12 @@ resource "docker_container" "foo" {
}
network_mode = "bridge"
- networks = ["${docker_network.test_network.name}"]
- network_alias = ["tftest"]
+ networks_advanced = [
+ {
+ name = "${docker_network.test_network.name}"
+ aliases = ["tftest"]
+ }
+ ]
host {
host = "testhost"
@@ -1134,8 +1258,8 @@ resource "docker_container" "foo" {
image = "${docker_image.foo.latest}"
devices {
- host_path = "/dev/zero"
- container_path = "/dev/zero_test"
+ host_path = "/dev/zero"
+ container_path = "/dev/zero_test"
}
}
`
@@ -1320,3 +1444,77 @@ resource "docker_container" "foo" {
must_run = false
}
`
+const testAccDockerContainerNetworksIPv4AddressConfig = `
+resource "docker_network" "test" {
+ name = "tf-test"
+ ipam_config {
+ subnet = "10.0.1.0/24"
+ }
+}
+resource "docker_image" "foo" {
+ name = "nginx:latest"
+ keep_locally = true
+}
+resource "docker_container" "foo" {
+ name = "tf-test"
+ image = "${docker_image.foo.latest}"
+ networks_advanced = [
+ {
+ name = "${docker_network.test.name}",
+ ipv4_address = "10.0.1.123"
+ }
+ ]
+}
+`
+const testAccDockerContainerNetworksIPv6AddressConfig = `
+resource "docker_network" "test" {
+ name = "tf-test"
+ ipv6 = true
+ ipam_config {
+ subnet = "fd00::1/64"
+ }
+}
+resource "docker_image" "foo" {
+ name = "nginx:latest"
+ keep_locally = true
+}
+resource "docker_container" "foo" {
+ name = "tf-test"
+ image = "${docker_image.foo.latest}"
+ networks_advanced = [
+ {
+ name = "${docker_network.test.name}",
+ ipv6_address = "fd00:0:0:0::123"
+ }
+ ]
+}
+`
+const testAccDockerContainerNetworksDualStackAddressConfig = `
+resource "docker_network" "test" {
+ name = "tf-test"
+ ipv6 = true
+ ipam_config = [
+ {
+ subnet = "10.0.1.0/24"
+ },
+ {
+ subnet = "fd00::1/64"
+ }
+ ]
+}
+resource "docker_image" "foo" {
+ name = "nginx:latest"
+ keep_locally = true
+}
+resource "docker_container" "foo" {
+ name = "tf-test"
+ image = "${docker_image.foo.latest}"
+ networks_advanced = [
+ {
+ name = "${docker_network.test.name}",
+ ipv4_address = "10.0.1.123"
+ ipv6_address = "fd00:0:0:0::123"
+ }
+ ]
+}
+`
diff --git a/docker/resource_docker_network_test.go b/docker/resource_docker_network_test.go
index 3534343e..d670bc9e 100644
--- a/docker/resource_docker_network_test.go
+++ b/docker/resource_docker_network_test.go
@@ -75,7 +75,7 @@ func TestAccDockerNetwork_internal(t *testing.T) {
resource.TestStep{
Config: testAccDockerNetworkInternalConfig,
Check: resource.ComposeTestCheckFunc(
- testAccNetwork("docker_network.foobar", &n),
+ testAccNetwork("docker_network.foo", &n),
testAccNetworkInternal(&n, true),
),
},
@@ -93,8 +93,8 @@ func testAccNetworkInternal(network *types.NetworkResource, internal bool) resou
}
const testAccDockerNetworkInternalConfig = `
-resource "docker_network" "foobar" {
- name = "foobar"
+resource "docker_network" "foo" {
+ name = "bar"
internal = true
}
`
@@ -109,7 +109,7 @@ func TestAccDockerNetwork_attachable(t *testing.T) {
resource.TestStep{
Config: testAccDockerNetworkAttachableConfig,
Check: resource.ComposeTestCheckFunc(
- testAccNetwork("docker_network.foobar", &n),
+ testAccNetwork("docker_network.foo", &n),
testAccNetworkAttachable(&n, true),
),
},
@@ -127,12 +127,128 @@ func testAccNetworkAttachable(network *types.NetworkResource, attachable bool) r
}
const testAccDockerNetworkAttachableConfig = `
-resource "docker_network" "foobar" {
- name = "foobar"
+resource "docker_network" "foo" {
+ name = "bar"
attachable = true
}
`
+//func TestAccDockerNetwork_ingress(t *testing.T) {
+// var n types.NetworkResource
+//
+// resource.Test(t, resource.TestCase{
+// PreCheck: func() { testAccPreCheck(t) },
+// Providers: testAccProviders,
+// Steps: []resource.TestStep{
+// resource.TestStep{
+// Config: testAccDockerNetworkIngressConfig,
+// Check: resource.ComposeTestCheckFunc(
+// testAccNetwork("docker_network.foo", &n),
+// testAccNetworkIngress(&n, true),
+// ),
+// },
+// },
+// })
+//}
+//
+//func testAccNetworkIngress(network *types.NetworkResource, internal bool) resource.TestCheckFunc {
+// return func(s *terraform.State) error {
+// if network.Internal != internal {
+// return fmt.Errorf("Bad value for attribute 'ingress': %t", network.Ingress)
+// }
+// return nil
+// }
+//}
+//
+//const testAccDockerNetworkIngressConfig = `
+//resource "docker_network" "foo" {
+// name = "bar"
+// ingress = true
+//}
+//`
+
+func TestAccDockerNetwork_ipv4(t *testing.T) {
+ var n types.NetworkResource
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerNetworkIPv4Config,
+ Check: resource.ComposeTestCheckFunc(
+ testAccNetwork("docker_network.foo", &n),
+ testAccNetworkIPv4(&n, true),
+ ),
+ },
+ },
+ })
+}
+
+func testAccNetworkIPv4(network *types.NetworkResource, internal bool) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ if len(network.IPAM.Config) != 1 {
+ return fmt.Errorf("Bad value for IPAM configuration count: %d", len(network.IPAM.Config))
+ }
+ if network.IPAM.Config[0].Subnet != "10.0.1.0/24" {
+ return fmt.Errorf("Bad value for attribute 'subnet': %v", network.IPAM.Config[0].Subnet)
+ }
+ return nil
+ }
+}
+
+const testAccDockerNetworkIPv4Config = `
+resource "docker_network" "foo" {
+ name = "bar"
+ ipam_config {
+ subnet = "10.0.1.0/24"
+ }
+}
+`
+
+func TestAccDockerNetwork_ipv6(t *testing.T) {
+ var n types.NetworkResource
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ resource.TestStep{
+ Config: testAccDockerNetworkIPv6Config,
+ Check: resource.ComposeTestCheckFunc(
+ testAccNetwork("docker_network.foo", &n),
+ testAccNetworkIPv6(&n, true),
+ ),
+ },
+ },
+ })
+}
+
+func testAccNetworkIPv6(network *types.NetworkResource, internal bool) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ if !network.EnableIPv6 {
+ return fmt.Errorf("Bad value for attribute 'ipv6': %t", network.EnableIPv6)
+ }
+ if len(network.IPAM.Config) != 2 {
+ return fmt.Errorf("Bad value for IPAM configuration count: %d", len(network.IPAM.Config))
+ }
+ if network.IPAM.Config[1].Subnet != "fd00::1/64" {
+ return fmt.Errorf("Bad value for attribute 'subnet': %v", network.IPAM.Config[0].Subnet)
+ }
+ return nil
+ }
+}
+
+const testAccDockerNetworkIPv6Config = `
+resource "docker_network" "foo" {
+ name = "bar"
+ ipv6 = true
+ ipam_config {
+ subnet = "fd00::1/64"
+ }
+}
+`
+
func TestAccDockerNetwork_labels(t *testing.T) {
var n types.NetworkResource
@@ -143,8 +259,8 @@ func TestAccDockerNetwork_labels(t *testing.T) {
resource.TestStep{
Config: testAccDockerNetworkLabelsConfig,
Check: resource.ComposeTestCheckFunc(
- testAccNetwork("docker_network.foobar", &n),
- testAccNetworkLabel(&n, "com.docker.compose.network", "foobar"),
+ testAccNetwork("docker_network.foo", &n),
+ testAccNetworkLabel(&n, "com.docker.compose.network", "foo"),
testAccNetworkLabel(&n, "com.docker.compose.project", "test"),
),
},
@@ -162,10 +278,10 @@ func testAccNetworkLabel(network *types.NetworkResource, name string, value stri
}
const testAccDockerNetworkLabelsConfig = `
-resource "docker_network" "foobar" {
- name = "test_foobar"
+resource "docker_network" "foo" {
+ name = "test_foo"
labels {
- "com.docker.compose.network" = "foobar"
+ "com.docker.compose.network" = "foo"
"com.docker.compose.project" = "test"
}
}
diff --git a/website/docs/r/container.html.markdown b/website/docs/r/container.html.markdown
index d5e8c730..314ac3a6 100644
--- a/website/docs/r/container.html.markdown
+++ b/website/docs/r/container.html.markdown
@@ -89,10 +89,11 @@ data is stored in them. See [the docker documentation][linkdoc] for more details
Defaults to "json-file".
* `log_opts` - (Optional, map of strings) Key/value pairs to use as options for
the logging driver.
-* `network_alias` - (Optional, set of strings) Network aliases of the container for user-defined networks only.
+* `network_alias` - (Optional, set of strings) Network aliases of the container for user-defined networks only. *Deprecated:* use `networks_advanced` instead.
* `network_mode` - (Optional, string) Network mode of the container.
* `networks` - (Optional, set of strings) Id of the networks in which the
- container is.
+ container is. *Deprecated:* use `networks_advanced` instead.
+* `networks_advanced` - (Optional, block) See [Networks Advanced](#networks_advanced) below for details.
* `destroy_grace_seconds` - (Optional, int) If defined will attempt to stop the container before destroying. Container will be destroyed after `n` seconds or on successful stop.
* `upload` - (Optional, block) See [File Upload](#upload) below for details.
* `ulimit` - (Optional, block) See [Ulimits](#ulimits) below for
@@ -181,6 +182,18 @@ Each `upload` supports the following
executable permission.
Defaults to false.
+
+### Network advanced
+
+`networks_advanced` is a block within the configuration that can be repeated to specify
+files to upload to the container before starting it.
+Each `networks_advanced` supports the following:
+
+* `name` - (Required, string) A content of a file to upload.
+* `aliases` - (Optional, set of strings) The network aliases of the container in the specific network.
+* `ipv4_address` - (Optional, string) The IPV4 address of the container in the specific network.
+* `ipv6_address` - (Optional, string) The IPV6 address of the container in the specific network.
+
### Devices