mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2026-01-03 05:19:34 -05:00
Add labels to support docker stacks (#92)
* Add network labels and options attachable, ingress and ipv6 * Add volume labels * Add secret labels * Updates documentation
This commit is contained in:
parent
bcc62489b0
commit
eca96b960b
12 changed files with 212 additions and 5 deletions
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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" {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/////////////
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue