Adds container static IPv4/IPv6 address. Marks network and network_alias as deprecated. Closes #105.

This commit is contained in:
Manuel Vogel 2018-10-29 06:36:21 +01:00
parent 496abe0578
commit bc56f62190
No known key found for this signature in database
GPG key ID: 533006C7B61DB1CE
5 changed files with 417 additions and 25 deletions

View file

@ -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{

View file

@ -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)
}

View file

@ -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"
}
]
}
`

View file

@ -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"
}
}

View file

@ -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.
<a id="networks_advanced"></a>
### 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.
<a id="devices"></a>
### Devices