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:
Boris HUISGEN 2018-10-18 12:39:58 +02:00 committed by Manuel Vogel
parent bcc62489b0
commit eca96b960b
12 changed files with 212 additions and 5 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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