mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2025-12-20 22:59:42 -05:00
Container network fixes (#104)
* Feat/net-attr add IP address of each network to the computed attributes from #50. * marks ip_address as deprecated and adds network data for a container. Closes #9 * adds wait for removal of a container. Closes #98 * removes validator for container network_mode and checks error handling if container disconnect from default network fails. Closes #107
This commit is contained in:
parent
15522080d6
commit
2360eb088f
7 changed files with 260 additions and 26 deletions
|
|
@ -1,16 +1,19 @@
|
||||||
## 1.1.0 (Unreleased)
|
## 1.1.0 (Unreleased)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
* Adds labels for `network`, `volume` and `secret` to support docker stacks. [[92](https://github.com/terraform-providers/terraform-provider-docker/pull/92)]
|
* Adds labels for `network`, `volume` and `secret` to support docker stacks. [[#92](https://github.com/terraform-providers/terraform-provider-docker/pull/92)]
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
* Fixes that new network were appended to the default bridge [GH-10]
|
* Fixes that new network were appended to the default bridge [GH-10]
|
||||||
* Fixes that container resource returns a non-existent IP address [GH-36]
|
* Fixes that container resource returns a non-existent IP address [GH-36]
|
||||||
|
* Fixes container's ip_address is empty when using custom network [GH-9] and [[#50](https://github.com/terraform-providers/terraform-provider-docker/pull/50)]
|
||||||
|
* Fixes terraform destroy failing to remove a bridge network [GH-98] and [[#50](https://github.com/terraform-providers/terraform-provider-docker/pull/50)]
|
||||||
|
|
||||||
|
|
||||||
## 1.0.4 (October 17, 2018)
|
## 1.0.4 (October 17, 2018)
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
* Support and fix for random external ports for containers [[#102](https://github.com/terraform-providers/terraform-provider-docker/issues/102)] and ([103](https://github.com/terraform-providers/terraform-provider-docker/pull/103))
|
* Support and fix for random external ports for containers [[#102](https://github.com/terraform-providers/terraform-provider-docker/issues/102)] and ([#103](https://github.com/terraform-providers/terraform-provider-docker/pull/103))
|
||||||
|
|
||||||
## 1.0.3 (October 12, 2018)
|
## 1.0.3 (October 12, 2018)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -291,18 +291,21 @@ func resourceDockerContainer() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
"ip_address": &schema.Schema{
|
"ip_address": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
Deprecated: "Use ip_adresses_data instead. This field exposes the data of the container's first network.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ip_prefix_length": &schema.Schema{
|
"ip_prefix_length": &schema.Schema{
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
Deprecated: "Use ip_prefix_length from ip_adresses_data instead. This field exposes the data of the container's first network.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"gateway": &schema.Schema{
|
"gateway": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
Deprecated: "Use gateway from ip_adresses_data instead. This field exposes the data of the container's first network.",
|
||||||
},
|
},
|
||||||
|
|
||||||
"bridge": &schema.Schema{
|
"bridge": &schema.Schema{
|
||||||
|
|
@ -310,6 +313,31 @@ func resourceDockerContainer() *schema.Resource {
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"network_data": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"network_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"ip_address": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"ip_prefix_length": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"gateway": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"privileged": &schema.Schema{
|
"privileged": &schema.Schema{
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
|
@ -398,10 +426,9 @@ func resourceDockerContainer() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
"network_mode": &schema.Schema{
|
"network_mode": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
ValidateFunc: validateStringMatchesPattern(`^(bridge|host|none|container:.+|service:.+)$`),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"networks": &schema.Schema{
|
"networks": &schema.Schema{
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,9 @@ func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := client.NetworkDisconnect(context.Background(), "bridge", retContainer.ID, false); err != nil {
|
if err := client.NetworkDisconnect(context.Background(), "bridge", retContainer.ID, false); err != nil {
|
||||||
return fmt.Errorf("Unable to disconnect the default network: %s", err)
|
if !strings.Contains(err.Error(), "is not connected to the network bridge") {
|
||||||
|
return fmt.Errorf("Unable to disconnect the default network: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rawNetwork := range v.(*schema.Set).List() {
|
for _, rawNetwork := range v.(*schema.Set).List() {
|
||||||
|
|
@ -300,7 +302,7 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonObj, _ := json.MarshalIndent(container, "", "\t")
|
jsonObj, _ := json.MarshalIndent(container, "", "\t")
|
||||||
log.Printf("[DEBUG] Docker container inspect: %s", jsonObj)
|
log.Printf("[INFO] Docker container inspect: %s", jsonObj)
|
||||||
|
|
||||||
if container.State.Running ||
|
if container.State.Running ||
|
||||||
!container.State.Running && !d.Get("must_run").(bool) {
|
!container.State.Running && !d.Get("must_run").(bool) {
|
||||||
|
|
@ -333,13 +335,27 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
||||||
|
|
||||||
// Read Network Settings
|
// Read Network Settings
|
||||||
if container.NetworkSettings != nil {
|
if container.NetworkSettings != nil {
|
||||||
|
// TODO remove deprecated attributes in next major
|
||||||
d.Set("ip_address", container.NetworkSettings.IPAddress)
|
d.Set("ip_address", container.NetworkSettings.IPAddress)
|
||||||
d.Set("ip_prefix_length", container.NetworkSettings.IPPrefixLen)
|
d.Set("ip_prefix_length", container.NetworkSettings.IPPrefixLen)
|
||||||
d.Set("gateway", container.NetworkSettings.Gateway)
|
d.Set("gateway", container.NetworkSettings.Gateway)
|
||||||
|
if container.NetworkSettings != nil && len(container.NetworkSettings.Networks) > 0 {
|
||||||
|
// Still support deprecated outputs
|
||||||
|
for _, settings := range container.NetworkSettings.Networks {
|
||||||
|
d.Set("ip_address", settings.IPAddress)
|
||||||
|
d.Set("ip_prefix_length", settings.IPPrefixLen)
|
||||||
|
d.Set("gateway", settings.Gateway)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d.Set("bridge", container.NetworkSettings.Bridge)
|
d.Set("bridge", container.NetworkSettings.Bridge)
|
||||||
if err := d.Set("ports", flattenContainerPorts(container.NetworkSettings.Ports)); err != nil {
|
if err := d.Set("ports", flattenContainerPorts(container.NetworkSettings.Ports)); err != nil {
|
||||||
log.Printf("[WARN] failed to set ports from API: %s", err)
|
log.Printf("[WARN] failed to set ports from API: %s", err)
|
||||||
}
|
}
|
||||||
|
if err := d.Set("network_data", flattenContainerNetworks(container.NetworkSettings)); err != nil {
|
||||||
|
log.Printf("[WARN] failed to set network settings from API: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -372,6 +388,16 @@ func resourceDockerContainerDelete(d *schema.ResourceData, meta interface{}) err
|
||||||
return fmt.Errorf("Error deleting container %s: %s", d.Id(), err)
|
return fmt.Errorf("Error deleting container %s: %s", d.Id(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
waitOkC, errorC := client.ContainerWait(context.Background(), d.Id(), container.WaitConditionRemoved)
|
||||||
|
select {
|
||||||
|
case waitOk := <-waitOkC:
|
||||||
|
log.Printf("[INFO] Container exited with code [%v]: '%s'", waitOk.StatusCode, d.Id())
|
||||||
|
case err := <-errorC:
|
||||||
|
if !(strings.Contains(err.Error(), "No such container") || strings.Contains(err.Error(), "is already in progress")) {
|
||||||
|
return fmt.Errorf("Error waiting for container removal '%s': %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -394,6 +420,23 @@ func flattenContainerPorts(in nat.PortMap) []interface{} {
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
func flattenContainerNetworks(in *types.NetworkSettings) []interface{} {
|
||||||
|
var out = make([]interface{}, 0)
|
||||||
|
if in == nil || in.Networks == nil || len(in.Networks) == 0 {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
networks := in.Networks
|
||||||
|
for networkName, networkData := range networks {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
m["network_name"] = networkName
|
||||||
|
m["ip_address"] = networkData.IPAddress
|
||||||
|
m["ip_prefix_length"] = networkData.IPPrefixLen
|
||||||
|
m["gateway"] = networkData.Gateway
|
||||||
|
out = append(out, m)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// TODO move to separate flattener file
|
// TODO move to separate flattener file
|
||||||
func stringListToStringSlice(stringList []interface{}) []string {
|
func stringListToStringSlice(stringList []interface{}) []string {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,73 @@ func TestAccDockerContainer_basic(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
func TestAccDockerContainer_basic_network(t *testing.T) {
|
||||||
|
var c types.ContainerJSON
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDockerContainerWith2BridgeNetworkConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccContainerRunning("docker_container.foo", &c),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.foo", "bridge", ""),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "gateway"),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.foo", "network_data.#", "2"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.network_name"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.gateway"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.network_name"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.gateway"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccDockerContainer_2networks_withmode(t *testing.T) {
|
||||||
|
var c types.ContainerJSON
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDockerContainer2NetworksConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccContainerRunning("docker_container.foo", &c),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.foo", "bridge", ""),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "gateway"),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.foo", "network_data.#", "2"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.network_name"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.0.gateway"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.network_name"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.foo", "network_data.1.gateway"),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.bar", "network_alias.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.bar", "bridge", ""),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "gateway"),
|
||||||
|
resource.TestCheckResourceAttr("docker_container.bar", "network_data.#", "1"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "network_data.0.network_name"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "network_data.0.ip_address"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "network_data.0.ip_prefix_length"),
|
||||||
|
resource.TestCheckResourceAttrSet("docker_container.bar", "network_data.0.gateway"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccDockerContainerPath_validation(t *testing.T) {
|
func TestAccDockerContainerPath_validation(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
|
|
@ -687,6 +754,29 @@ resource "docker_container" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccDockerContainerWith2BridgeNetworkConfig = `
|
||||||
|
resource "docker_network" "tftest" {
|
||||||
|
name = "tftest-contnw"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_network" "tftest_2" {
|
||||||
|
name = "tftest-contnw-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_image" "foo" {
|
||||||
|
name = "nginx:latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_container" "foo" {
|
||||||
|
name = "tf-test"
|
||||||
|
image = "${docker_image.foo.latest}"
|
||||||
|
networks = [
|
||||||
|
"${docker_network.tftest.name}",
|
||||||
|
"${docker_network.tftest_2.name}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const testAccDockerContainerVolumeConfig = `
|
const testAccDockerContainerVolumeConfig = `
|
||||||
resource "docker_image" "foo" {
|
resource "docker_image" "foo" {
|
||||||
name = "nginx:latest"
|
name = "nginx:latest"
|
||||||
|
|
@ -885,3 +975,33 @@ resource "docker_container" "foo" {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const testAccDockerContainer2NetworksConfig = `
|
||||||
|
resource "docker_image" "foo" {
|
||||||
|
name = "nginx:latest"
|
||||||
|
keep_locally = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_network" "test_network_1" {
|
||||||
|
name = "tftest-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_network" "test_network_2" {
|
||||||
|
name = "tftest-2"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_container" "foo" {
|
||||||
|
name = "tf-test"
|
||||||
|
image = "${docker_image.foo.latest}"
|
||||||
|
network_mode = "${docker_network.test_network_1.name}"
|
||||||
|
networks = ["${docker_network.test_network_2.name}"]
|
||||||
|
network_alias = ["tftest-container"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "docker_container" "bar" {
|
||||||
|
name = "tf-test-bar"
|
||||||
|
image = "${docker_image.foo.latest}"
|
||||||
|
network_mode = "bridge"
|
||||||
|
networks = ["${docker_network.test_network_2.name}"]
|
||||||
|
network_alias = ["tftest-container-foo"]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@ package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceDockerNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
@ -90,10 +92,21 @@ func resourceDockerNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceDockerNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceDockerNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*ProviderConfig).DockerClient
|
log.Printf("[INFO] Waiting for network: '%s' to be removed: max '%v seconds'", d.Id(), 30)
|
||||||
|
|
||||||
if err := client.NetworkRemove(context.Background(), d.Id()); err != nil {
|
stateConf := &resource.StateChangeConf{
|
||||||
return fmt.Errorf("Error deleting network %s: %s", d.Id(), err)
|
Pending: []string{"pending"},
|
||||||
|
Target: []string{"removed"},
|
||||||
|
Refresh: resourceDockerNetworkRemoveRefreshFunc(d, meta),
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
MinTimeout: 5 * time.Second,
|
||||||
|
Delay: 2 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait, catching any errors
|
||||||
|
_, err := stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
|
|
@ -133,7 +146,7 @@ func resourceDockerNetworkReadRefreshFunc(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[WARN] Network (%s) not found, removing from state", networkID)
|
log.Printf("[WARN] Network (%s) not found, removing from state", networkID)
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return networkID, "removed", err
|
return networkID, "removed", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonObj, _ := json.MarshalIndent(retNetwork, "", "\t")
|
jsonObj, _ := json.MarshalIndent(retNetwork, "", "\t")
|
||||||
|
|
@ -160,3 +173,26 @@ func resourceDockerNetworkReadRefreshFunc(
|
||||||
return networkID, "all_fields", nil
|
return networkID, "all_fields", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceDockerNetworkRemoveRefreshFunc(
|
||||||
|
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
client := meta.(*ProviderConfig).DockerClient
|
||||||
|
networkID := d.Id()
|
||||||
|
|
||||||
|
_, _, err := client.NetworkInspectWithRaw(context.Background(), networkID, types.NetworkInspectOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[INFO] Network (%s) not found. Already removed", networkID)
|
||||||
|
return networkID, "removed", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.NetworkRemove(context.Background(), networkID); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "has active endpoints") {
|
||||||
|
return networkID, "pending", nil
|
||||||
|
}
|
||||||
|
return networkID, "other", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return networkID, "removed", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
|
@ -275,6 +276,7 @@ func deleteService(serviceID string, d *schema.ResourceData, client *client.Clie
|
||||||
destroyGraceSeconds, _ := time.ParseDuration(v.(string))
|
destroyGraceSeconds, _ := time.ParseDuration(v.(string))
|
||||||
log.Printf("[INFO] Waiting for container: '%s' to exit: max %v", containerID, destroyGraceSeconds)
|
log.Printf("[INFO] Waiting for container: '%s' to exit: max %v", containerID, destroyGraceSeconds)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), destroyGraceSeconds)
|
ctx, cancel := context.WithTimeout(context.Background(), destroyGraceSeconds)
|
||||||
|
// TODO why defer? see container_resource with handling return channels! why not remove then wait?
|
||||||
defer cancel()
|
defer cancel()
|
||||||
exitCode, _ := client.ContainerWait(ctx, containerID, container.WaitConditionRemoved)
|
exitCode, _ := client.ContainerWait(ctx, containerID, container.WaitConditionRemoved)
|
||||||
log.Printf("[INFO] Container exited with code [%v]: '%s'", exitCode, containerID)
|
log.Printf("[INFO] Container exited with code [%v]: '%s'", exitCode, containerID)
|
||||||
|
|
|
||||||
|
|
@ -205,13 +205,16 @@ the following:
|
||||||
|
|
||||||
The following attributes are exported:
|
The following attributes are exported:
|
||||||
|
|
||||||
* `ip_address` - The IP address of the container as read from its
|
* `network_data` - (Map of a block) The IP addresses of the container on each
|
||||||
|
network. Key are the network names, values are the IP addresses.
|
||||||
|
* `ip_address` - The IP address of the container.
|
||||||
|
* `ip_prefix_length` - The IP prefix length of the container.
|
||||||
|
* `gateway` - The network gateway of the container.
|
||||||
|
* `bridge` - The network bridge of the container as read from its NetworkSettings.
|
||||||
|
* `ip_address` - *Deprecated:* Use `network_data` instead. The IP address of the container's first network it.
|
||||||
|
* `ip_prefix_length` - *Deprecated:* Use `network_data` instead. The IP prefix length of the container as read from its
|
||||||
NetworkSettings.
|
NetworkSettings.
|
||||||
* `ip_prefix_length` - The IP prefix length of the container as read from its
|
* `gateway` - *Deprecated:* Use `network_data` instead. The network gateway of the container as read from its
|
||||||
NetworkSettings.
|
|
||||||
* `gateway` - The network gateway of the container as read from its
|
|
||||||
NetworkSettings.
|
|
||||||
* `bridge` - The network bridge of the container as read from its
|
|
||||||
NetworkSettings.
|
NetworkSettings.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue