mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2026-01-18 12:42:56 -05:00
feat(service): add alias for networks (#241)
* feat(service): outlines alias for networks * feat: Add driver_opts sub-attribute. * fix: network driver options type conversions. * fix: Temporarily fix docker_service tests. Co-authored-by: Martin <Junkern@users.noreply.github.com> Co-authored-by: Martin Wentzel <martin.wentzel@kreuzwerker.de>
This commit is contained in:
parent
5ffe0f3628
commit
a5332be18d
7 changed files with 159 additions and 16 deletions
|
|
@ -345,7 +345,8 @@ Optional:
|
|||
|
||||
- `force_update` (Number) A counter that triggers an update even if no relevant parameters have been changed. See the [spec](https://github.com/docker/swarmkit/blob/master/api/specs.proto#L126).
|
||||
- `log_driver` (Block List, Max: 1) Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified (see [below for nested schema](#nestedblock--task_spec--log_driver))
|
||||
- `networks` (Set of String) Ids of the networks in which the container will be put in
|
||||
- `networks` (Set of String, Deprecated) Ids of the networks in which the container will be put in
|
||||
- `networks_advanced` (Block Set) The networks the container is attached to (see [below for nested schema](#nestedblock--task_spec--networks_advanced))
|
||||
- `placement` (Block List, Max: 1) The placement preferences (see [below for nested schema](#nestedblock--task_spec--placement))
|
||||
- `resources` (Block List, Max: 1) Resource requirements which apply to each individual container created as part of the service (see [below for nested schema](#nestedblock--task_spec--resources))
|
||||
- `restart_policy` (Block List, Max: 1) Specification for the restart policy which applies to containers created as part of this service. (see [below for nested schema](#nestedblock--task_spec--restart_policy))
|
||||
|
|
@ -556,6 +557,19 @@ Optional:
|
|||
- `options` (Map of String) The options for the logging driver
|
||||
|
||||
|
||||
<a id="nestedblock--task_spec--networks_advanced"></a>
|
||||
### Nested Schema for `task_spec.networks_advanced`
|
||||
|
||||
Required:
|
||||
|
||||
- `name` (String) The name/id of the network.
|
||||
|
||||
Optional:
|
||||
|
||||
- `aliases` (Set of String) The network aliases of the container in the specific network.
|
||||
- `driver_opts` (Set of String) An array of driver options for the network, e.g. `opts1=value`
|
||||
|
||||
|
||||
<a id="nestedblock--task_spec--placement"></a>
|
||||
### Nested Schema for `task_spec.placement`
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,21 @@ func stringSetToStringSlice(stringSet *schema.Set) []string {
|
|||
return ret
|
||||
}
|
||||
|
||||
func stringSetToMapStringString(stringSet *schema.Set) map[string]string {
|
||||
ret := map[string]string{}
|
||||
if stringSet == nil {
|
||||
return ret
|
||||
}
|
||||
for _, envVal := range stringSet.List() {
|
||||
envValSplit := strings.SplitN(envVal.(string), "=", 2)
|
||||
if len(envValSplit) != 2 {
|
||||
continue
|
||||
}
|
||||
ret[envValSplit[0]] = envValSplit[1]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func mapTypeMapValsToString(typeMap map[string]interface{}) map[string]string {
|
||||
mapped := make(map[string]string, len(typeMap))
|
||||
for k, v := range typeMap {
|
||||
|
|
|
|||
|
|
@ -681,11 +681,44 @@ func resourceDockerService() *schema.Resource {
|
|||
ValidateDiagFunc: validateStringMatchesPattern("^(container|plugin)$"),
|
||||
},
|
||||
"networks": {
|
||||
Type: schema.TypeSet,
|
||||
Description: "Ids of the networks in which the container will be put in",
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
Type: schema.TypeSet,
|
||||
Description: "Ids of the networks in which the container will be put in",
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
Deprecated: "Use networks_advanced instead",
|
||||
ConflictsWith: []string{"task_spec.0.networks_advanced"},
|
||||
},
|
||||
"networks_advanced": {
|
||||
Type: schema.TypeSet,
|
||||
Description: "The networks the container is attached to",
|
||||
Optional: true,
|
||||
ConflictsWith: []string{"task_spec.0.networks"},
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Description: "The name/id of the network.",
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"aliases": {
|
||||
Type: schema.TypeSet,
|
||||
Description: "The network aliases of the container in the specific network.",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
"driver_opts": {
|
||||
Type: schema.TypeSet,
|
||||
Description: "An array of driver options for the network, e.g. `opts1=value`",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"log_driver": {
|
||||
Type: schema.TypeList,
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ func resourceDockerServiceReadRefreshFunc(ctx context.Context,
|
|||
d.Set("name", service.Spec.Name)
|
||||
d.Set("labels", mapToLabelSet(service.Spec.Labels))
|
||||
|
||||
if err = d.Set("task_spec", flattenTaskSpec(service.Spec.TaskTemplate)); err != nil {
|
||||
if err = d.Set("task_spec", flattenTaskSpec(service.Spec.TaskTemplate, d)); err != nil {
|
||||
log.Printf("[WARN] failed to set task spec from API: %s", err)
|
||||
}
|
||||
if err = d.Set("mode", flattenServiceMode(service.Spec.Mode)); err != nil {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
// flatten API objects to the terraform schema
|
||||
// ////////////
|
||||
// see https://learn.hashicorp.com/tutorials/terraform/provider-create?in=terraform/providers#add-flattening-functions
|
||||
func flattenTaskSpec(in swarm.TaskSpec) []interface{} {
|
||||
func flattenTaskSpec(in swarm.TaskSpec, d *schema.ResourceData) []interface{} {
|
||||
m := make(map[string]interface{})
|
||||
if in.ContainerSpec != nil {
|
||||
m["container_spec"] = flattenContainerSpec(in.ContainerSpec)
|
||||
|
|
@ -37,7 +37,22 @@ func flattenTaskSpec(in swarm.TaskSpec) []interface{} {
|
|||
m["runtime"] = in.Runtime
|
||||
}
|
||||
if len(in.Networks) > 0 {
|
||||
m["networks"] = flattenTaskNetworks(in.Networks)
|
||||
// We check which networks are set and need to retrieve the resource data
|
||||
// therefore. See in the method 'createServiceTaskSpec'
|
||||
v := d.Get("task_spec").([]interface{})
|
||||
// TODO mavogel: in the last cycle run v is empty
|
||||
// so we check but then the import state fails
|
||||
if len(v) > 0 {
|
||||
rawTaskSpec := v[0].(map[string]interface{})
|
||||
if v, ok := rawTaskSpec["networks"]; ok && len(v.(*schema.Set).List()) > 0 {
|
||||
log.Printf("[DEBUG] flatten networks with length: %d", rawTaskSpec["networks"])
|
||||
m["networks"] = flattenTaskNetworks(in.Networks)
|
||||
}
|
||||
if v, ok := rawTaskSpec["networks_advanced"]; ok && len(v.(*schema.Set).List()) > 0 {
|
||||
log.Printf("[DEBUG] flatten networks_advanced with length: %d", rawTaskSpec["networks_advanced"])
|
||||
m["networks_advanced"] = flattenTaskNetworksAdvanced(in.Networks)
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.LogDriver != nil {
|
||||
m["log_driver"] = flattenTaskLogDriver(in.LogDriver)
|
||||
|
|
@ -513,6 +528,31 @@ func flattenTaskNetworks(in []swarm.NetworkAttachmentConfig) *schema.Set {
|
|||
return schema.NewSet(schema.HashString, out)
|
||||
}
|
||||
|
||||
func flattenTaskNetworksAdvanced(in []swarm.NetworkAttachmentConfig) *schema.Set {
|
||||
out := make([]interface{}, len(in))
|
||||
for i, v := range in {
|
||||
m := make(map[string]interface{})
|
||||
m["name"] = v.Target
|
||||
m["driver_opts"] = stringSliceToSchemaSet(mapTypeMapValsToStringSlice(mapStringStringToMapStringInterface(v.DriverOpts)))
|
||||
if len(v.Aliases) > 0 {
|
||||
m["aliases"] = stringSliceToSchemaSet(v.Aliases)
|
||||
}
|
||||
out[i] = m
|
||||
}
|
||||
taskSpecResource := resourceDockerService().Schema["task_spec"].Elem.(*schema.Resource)
|
||||
networksAdvancedResource := taskSpecResource.Schema["networks_advanced"].Elem.(*schema.Resource)
|
||||
f := schema.HashResource(networksAdvancedResource)
|
||||
return schema.NewSet(f, out)
|
||||
}
|
||||
|
||||
func stringSliceToSchemaSet(in []string) *schema.Set {
|
||||
out := make([]interface{}, len(in))
|
||||
for i, v := range in {
|
||||
out[i] = v
|
||||
}
|
||||
return schema.NewSet(schema.HashString, out)
|
||||
}
|
||||
|
||||
func flattenTaskLogDriver(in *swarm.Driver) []interface{} {
|
||||
if in == nil {
|
||||
return make([]interface{}, 0)
|
||||
|
|
@ -656,6 +696,13 @@ func createServiceTaskSpec(d *schema.ResourceData) (swarm.TaskSpec, error) {
|
|||
}
|
||||
taskSpec.Networks = networks
|
||||
}
|
||||
if rawNetworksSpec, ok := rawTaskSpec["networks_advanced"]; ok {
|
||||
networks, err := createServiceAdvancedNetworks(rawNetworksSpec)
|
||||
if err != nil {
|
||||
return taskSpec, err
|
||||
}
|
||||
taskSpec.Networks = networks
|
||||
}
|
||||
if rawLogDriverSpec, ok := rawTaskSpec["log_driver"]; ok {
|
||||
logDriver, err := createLogDriver(rawLogDriverSpec)
|
||||
if err != nil {
|
||||
|
|
@ -1071,7 +1118,7 @@ func createPlacement(v interface{}) (*swarm.Placement, error) {
|
|||
return &placement, nil
|
||||
}
|
||||
|
||||
// createServiceNetworks creates the networks the service will be attachted to
|
||||
// createServiceNetworks creates the networks the service will be attachted to. Is deprecated
|
||||
func createServiceNetworks(v interface{}) ([]swarm.NetworkAttachmentConfig, error) {
|
||||
networks := []swarm.NetworkAttachmentConfig{}
|
||||
if len(v.(*schema.Set).List()) > 0 {
|
||||
|
|
@ -1085,6 +1132,27 @@ func createServiceNetworks(v interface{}) ([]swarm.NetworkAttachmentConfig, erro
|
|||
return networks, nil
|
||||
}
|
||||
|
||||
// createServiceAdvancedNetworks creates the networks the service will be attachted to
|
||||
func createServiceAdvancedNetworks(v interface{}) ([]swarm.NetworkAttachmentConfig, error) {
|
||||
networks := []swarm.NetworkAttachmentConfig{}
|
||||
if len(v.(*schema.Set).List()) > 0 {
|
||||
for _, rawNetwork := range v.(*schema.Set).List() {
|
||||
rawNetwork := rawNetwork.(map[string]interface{})
|
||||
networkID := rawNetwork["name"].(string)
|
||||
networkAliases := stringSetToStringSlice(rawNetwork["aliases"].(*schema.Set))
|
||||
network := swarm.NetworkAttachmentConfig{
|
||||
Target: networkID,
|
||||
Aliases: networkAliases,
|
||||
}
|
||||
if driverOpts, ok := rawNetwork["driver_opts"]; ok {
|
||||
network.DriverOpts = stringSetToMapStringString(driverOpts.(*schema.Set))
|
||||
}
|
||||
networks = append(networks, network)
|
||||
}
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// createLogDriver creates the log driver for the service
|
||||
func createLogDriver(v interface{}) (*swarm.Driver, error) {
|
||||
logDriver := swarm.Driver{}
|
||||
|
|
|
|||
|
|
@ -501,7 +501,11 @@ func TestAccDockerService_fullSpec(t *testing.T) {
|
|||
}
|
||||
|
||||
if len(s.Spec.TaskTemplate.Networks) != 1 ||
|
||||
s.Spec.TaskTemplate.Networks[0].Target == "" {
|
||||
s.Spec.TaskTemplate.Networks[0].Target == "" ||
|
||||
len(s.Spec.TaskTemplate.Networks[0].Aliases) == 0 ||
|
||||
s.Spec.TaskTemplate.Networks[0].Aliases[0] != "tftest-foobar" ||
|
||||
s.Spec.TaskTemplate.Networks[0].DriverOpts == nil ||
|
||||
!mapEquals("foo", "bar", s.Spec.TaskTemplate.Networks[0].DriverOpts) {
|
||||
return fmt.Errorf("Service Spec.TaskTemplate.Networks is wrong: %s", s.Spec.TaskTemplate.Networks)
|
||||
}
|
||||
|
||||
|
|
@ -639,7 +643,8 @@ func TestAccDockerService_fullSpec(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.prefs.0", "spread=node.role.manager"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.placement.0.max_replicas", "2"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.force_update", "0"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "1"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks.#", "0"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.networks_advanced.#", "1"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.name", "json-file"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-file", "3"),
|
||||
resource.TestCheckResourceAttr("docker_service.foo", "task_spec.0.log_driver.0.options.max-size", "10m"),
|
||||
|
|
@ -667,9 +672,10 @@ func TestAccDockerService_fullSpec(t *testing.T) {
|
|||
),
|
||||
},
|
||||
{
|
||||
ResourceName: "docker_service.foo",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ResourceName: "docker_service.foo",
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"task_spec.0.networks_advanced"},
|
||||
},
|
||||
},
|
||||
CheckDestroy: func(state *terraform.State) error {
|
||||
|
|
|
|||
|
|
@ -175,7 +175,14 @@ resource "docker_service" "foo" {
|
|||
|
||||
force_update = 0
|
||||
runtime = "container"
|
||||
networks = [docker_network.test_network.id]
|
||||
|
||||
networks_advanced {
|
||||
name = docker_network.test_network.id
|
||||
aliases = ["tftest-foobar"]
|
||||
driver_opts = [
|
||||
"foo=bar"
|
||||
]
|
||||
}
|
||||
|
||||
log_driver {
|
||||
name = "json-file"
|
||||
|
|
|
|||
Loading…
Reference in a new issue