diff --git a/docker/resource_docker_container.go b/docker/resource_docker_container.go index 27993327..0abffb0a 100644 --- a/docker/resource_docker_container.go +++ b/docker/resource_docker_container.go @@ -398,9 +398,10 @@ func resourceDockerContainer() *schema.Resource { }, "network_mode": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateStringMatchesPattern(`^(bridge|host|none|container:.+|service:.+)$`), }, "networks": &schema.Schema{ diff --git a/docker/resource_docker_container_funcs.go b/docker/resource_docker_container_funcs.go index f62e62c5..6f7302c9 100644 --- a/docker/resource_docker_container_funcs.go +++ b/docker/resource_docker_container_funcs.go @@ -211,6 +211,10 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err endpointConfig.Aliases = stringSetToStringSlice(v.(*schema.Set)) } + if err := client.NetworkDisconnect(context.Background(), "bridge", retContainer.ID, false); err != nil { + return fmt.Errorf("Unable to disconnect the default network: %s", err) + } + for _, rawNetwork := range v.(*schema.Set).List() { networkID := rawNetwork.(string) if err := client.NetworkConnect(context.Background(), networkID, retContainer.ID, endpointConfig); err != nil { diff --git a/docker/resource_docker_network.go b/docker/resource_docker_network.go index 0978f20c..978e1430 100644 --- a/docker/resource_docker_network.go +++ b/docker/resource_docker_network.go @@ -22,6 +22,12 @@ func resourceDockerNetwork() *schema.Resource { ForceNew: true, }, + "labels": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + "check_duplicate": &schema.Schema{ Type: schema.TypeBool, Optional: true, @@ -49,6 +55,24 @@ func resourceDockerNetwork() *schema.Resource { ForceNew: true, }, + "attachable": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "ingress": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "ipv6": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "ipam_driver": &schema.Schema{ Type: schema.TypeString, Optional: true, diff --git a/docker/resource_docker_network_funcs.go b/docker/resource_docker_network_funcs.go index 0e079c65..908c856c 100644 --- a/docker/resource_docker_network_funcs.go +++ b/docker/resource_docker_network_funcs.go @@ -17,6 +17,9 @@ func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error client := meta.(*ProviderConfig).DockerClient createOpts := types.NetworkCreate{} + if v, ok := d.GetOk("labels"); ok { + createOpts.Labels = mapTypeMapValsToString(v.(map[string]interface{})) + } if v, ok := d.GetOk("check_duplicate"); ok { createOpts.CheckDuplicate = v.(bool) } @@ -29,6 +32,15 @@ func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error if v, ok := d.GetOk("internal"); ok { createOpts.Internal = v.(bool) } + if v, ok := d.GetOk("attachable"); ok { + createOpts.Attachable = v.(bool) + } + if v, ok := d.GetOk("ingress"); ok { + createOpts.Ingress = v.(bool) + } + if v, ok := d.GetOk("ipv6"); ok { + createOpts.EnableIPv6 = v.(bool) + } ipamOpts := &network.IPAM{} ipamOptsSet := false @@ -128,6 +140,9 @@ func resourceDockerNetworkReadRefreshFunc( log.Printf("[DEBUG] Docker network inspect: %s", jsonObj) d.Set("internal", retNetwork.Internal) + d.Set("attachable", retNetwork.Attachable) + d.Set("ingress", retNetwork.Ingress) + d.Set("ipv6", retNetwork.EnableIPv6) d.Set("driver", retNetwork.Driver) d.Set("scope", retNetwork.Scope) if retNetwork.Scope == "overlay" { diff --git a/docker/resource_docker_network_test.go b/docker/resource_docker_network_test.go index 694ec376..3534343e 100644 --- a/docker/resource_docker_network_test.go +++ b/docker/resource_docker_network_test.go @@ -95,6 +95,78 @@ func testAccNetworkInternal(network *types.NetworkResource, internal bool) resou const testAccDockerNetworkInternalConfig = ` resource "docker_network" "foobar" { name = "foobar" - internal = "true" + internal = true +} +` + +func TestAccDockerNetwork_attachable(t *testing.T) { + var n types.NetworkResource + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerNetworkAttachableConfig, + Check: resource.ComposeTestCheckFunc( + testAccNetwork("docker_network.foobar", &n), + testAccNetworkAttachable(&n, true), + ), + }, + }, + }) +} + +func testAccNetworkAttachable(network *types.NetworkResource, attachable bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if network.Attachable != attachable { + return fmt.Errorf("Bad value for attribute 'attachable': %t", network.Attachable) + } + return nil + } +} + +const testAccDockerNetworkAttachableConfig = ` +resource "docker_network" "foobar" { + name = "foobar" + attachable = true +} +` + +func TestAccDockerNetwork_labels(t *testing.T) { + var n types.NetworkResource + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerNetworkLabelsConfig, + Check: resource.ComposeTestCheckFunc( + testAccNetwork("docker_network.foobar", &n), + testAccNetworkLabel(&n, "com.docker.compose.network", "foobar"), + testAccNetworkLabel(&n, "com.docker.compose.project", "test"), + ), + }, + }, + }) +} + +func testAccNetworkLabel(network *types.NetworkResource, name string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if network.Labels[name] != value { + return fmt.Errorf("Bad value for label '%s': %s", name, network.Labels[name]) + } + return nil + } +} + +const testAccDockerNetworkLabelsConfig = ` +resource "docker_network" "foobar" { + name = "test_foobar" + labels { + "com.docker.compose.network" = "foobar" + "com.docker.compose.project" = "test" + } } ` diff --git a/docker/resource_docker_secret.go b/docker/resource_docker_secret.go index 010d8346..5fbb34e5 100644 --- a/docker/resource_docker_secret.go +++ b/docker/resource_docker_secret.go @@ -31,6 +31,12 @@ func resourceDockerSecret() *schema.Resource { ForceNew: true, ValidateFunc: validateStringIsBase64Encoded(), }, + + "labels": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, }, } } @@ -46,6 +52,10 @@ func resourceDockerSecretCreate(d *schema.ResourceData, meta interface{}) error Data: data, } + if v, ok := d.GetOk("labels"); ok { + secretSpec.Annotations.Labels = mapTypeMapValsToString(v.(map[string]interface{})) + } + secret, err := client.SecretCreate(context.Background(), secretSpec) if err != nil { return err diff --git a/docker/resource_docker_secret_test.go b/docker/resource_docker_secret_test.go index 204d2be1..b544d846 100644 --- a/docker/resource_docker_secret_test.go +++ b/docker/resource_docker_secret_test.go @@ -30,6 +30,7 @@ func TestAccDockerSecret_basic(t *testing.T) { }, }) } + func TestAccDockerSecret_basicUpdatable(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -72,6 +73,32 @@ func TestAccDockerSecret_basicUpdatable(t *testing.T) { }) } +func TestAccDockerSecret_labels(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckDockerSecretDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: ` + resource "docker_secret" "foo" { + name = "foo-secret" + data = "Ymxhc2RzYmxhYmxhMTI0ZHNkd2VzZA==" + labels { + "test1" = "foo" + "test2" = "bar" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("docker_secret.foo", "labels.test1", "foo"), + resource.TestCheckResourceAttr("docker_secret.foo", "labels.test2", "bar"), + ), + }, + }, + }) +} + ///////////// // Helpers ///////////// diff --git a/docker/resource_docker_volume.go b/docker/resource_docker_volume.go index ce78755e..c0193b19 100644 --- a/docker/resource_docker_volume.go +++ b/docker/resource_docker_volume.go @@ -25,6 +25,11 @@ func resourceDockerVolume() *schema.Resource { Computed: true, ForceNew: true, }, + "labels": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, "driver": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -53,6 +58,9 @@ func resourceDockerVolumeCreate(d *schema.ResourceData, meta interface{}) error if v, ok := d.GetOk("name"); ok { createOpts.Name = v.(string) } + if v, ok := d.GetOk("labels"); ok { + createOpts.Labels = mapTypeMapValsToString(v.(map[string]interface{})) + } if v, ok := d.GetOk("driver"); ok { createOpts.Driver = v.(string) } diff --git a/docker/resource_docker_volume_test.go b/docker/resource_docker_volume_test.go index d12f6367..cd7ed911 100644 --- a/docker/resource_docker_volume_test.go +++ b/docker/resource_docker_volume_test.go @@ -57,3 +57,41 @@ resource "docker_volume" "foo" { name = "testAccDockerVolume_basic" } ` + +func TestAccDockerVolume_labels(t *testing.T) { + var v types.Volume + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDockerVolumeLabelsConfig, + Check: resource.ComposeTestCheckFunc( + checkDockerVolume("docker_volume.foo", &v), + testAccVolumeLabel(&v, "com.docker.compose.project", "test"), + testAccVolumeLabel(&v, "com.docker.compose.volume", "foo"), + ), + }, + }, + }) +} + +func testAccVolumeLabel(volume *types.Volume, name string, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if volume.Labels[name] != value { + return fmt.Errorf("Bad value for label '%s': %s", name, volume.Labels[name]) + } + return nil + } +} + +const testAccDockerVolumeLabelsConfig = ` +resource "docker_volume" "foo" { + name = "test_foo" + labels { + "com.docker.compose.project" = "test" + "com.docker.compose.volume" = "foo" + } +} +` diff --git a/website/docs/r/network.html.markdown b/website/docs/r/network.html.markdown index e1e58f3f..73f3771b 100644 --- a/website/docs/r/network.html.markdown +++ b/website/docs/r/network.html.markdown @@ -29,6 +29,7 @@ resource "docker_network" "private_network" { The following arguments are supported: * `name` - (Required, string) The name of the Docker network. +* `labels` - (Optional, map of string/string key/value pairs) User-defined key/value metadata. * `check_duplicate` - (Optional, boolean) Requests daemon to check for networks with same name. * `driver` - (Optional, string) Name of the network driver to use. Defaults to @@ -37,6 +38,12 @@ The following arguments are supported: the drivers. * `internal` - (Optional, boolean) Restrict external access to the network. Defaults to `false`. +* `attachable` - (Optional, boolean) Enable manual container attachment to the network. + Defaults to `false`. +* `ingress` - (Optional, boolean) Create swarm routing-mesh network. + Defaults to `false`. +* `ipv6` - (Optional, boolean) Enable IPv6 networking. + Defaults to `false`. * `ipam_driver` - (Optional, string) Driver used by the custom IP scheme of the network. * `ipam_config` - (Optional, block) See [IPAM config](#ipam_config) below for diff --git a/website/docs/r/secret.html.markdown b/website/docs/r/secret.html.markdown index 4215c1d8..1c90a68a 100644 --- a/website/docs/r/secret.html.markdown +++ b/website/docs/r/secret.html.markdown @@ -55,7 +55,7 @@ The following arguments are supported: * `name` - (Required, string) The name of the Docker secret. * `data` - (Required, string) The base64 encoded data of the secret. - +* `labels` - (Optional, map of string/string key/value pairs) User-defined key/value metadata. ## Attributes Reference diff --git a/website/docs/r/volume.html.markdown b/website/docs/r/volume.html.markdown index f5d9dee9..57d10429 100644 --- a/website/docs/r/volume.html.markdown +++ b/website/docs/r/volume.html.markdown @@ -30,6 +30,7 @@ The following arguments are supported: * `name` - (Optional, string) The name of the Docker volume (generated if not provided). +* `labels` - (Optional, map of string/string key/value pairs) User-defined key/value metadata. * `driver` - (Optional, string) Driver type for the volume (defaults to local). * `driver_opts` - (Optional, map of strings) Options specific to the driver.