Fix container ports issue for asc ordering (#115)

* Attempts to fix container ports issue by converting list to a set.
* Revert "Attempts to fix container ports issue by converting list to a set." This reverts commit 5660759875ca65e2b8c5b9aa8b25cc3ff7eee2d9.
* adds migration state func for container. Suppresses diff for port migration
* Updates changelog for v1.1.1
This commit is contained in:
Manuel Vogel 2019-03-05 15:55:17 +01:00 committed by GitHub
parent a853943351
commit 1ac859bd81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 175 additions and 4 deletions

View file

@ -1,5 +1,10 @@
## 1.1.1 (Unreleased)
BUG FIXES
* Fixes no more 'force new resource' for container ports when
there are no changes. This was caused to the ascending order. See [GH-110]
for details and [[#115](https://github.com/terraform-providers/terraform-provider-docker/pull/115)]
DOCS
* Corrects `networks_advanced` section [GH-109]
* Corrects `tmpfs_options` section [GH-122]

View file

@ -1,6 +1,8 @@
package docker
import (
"log"
"github.com/hashicorp/terraform/helper/schema"
)
@ -10,6 +12,8 @@ func resourceDockerContainer() *schema.Resource {
Read: resourceDockerContainerRead,
Update: resourceDockerContainerUpdate,
Delete: resourceDockerContainerDelete,
MigrateState: resourceDockerContainerMigrateState,
SchemaVersion: 1,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
@ -259,6 +263,7 @@ func resourceDockerContainer() *schema.Resource {
},
},
},
DiffSuppressFunc: suppressIfPortsDidNotChangeForMigrationV0ToV1(),
},
"host": &schema.Schema{
@ -601,3 +606,49 @@ func resourceDockerContainer() *schema.Resource {
},
}
}
func suppressIfPortsDidNotChangeForMigrationV0ToV1() schema.SchemaDiffSuppressFunc {
return func(k, old, new string, d *schema.ResourceData) bool {
portsOldRaw, portsNewRaw := d.GetChange("ports")
portsOld := portsOldRaw.([]interface{})
portsNew := portsNewRaw.([]interface{})
if len(portsOld) != len(portsNew) {
log.Printf("[DEBUG] suppress diff ports: old and new don't have the same length")
return false
}
log.Printf("[DEBUG] suppress diff ports: old and new have same length")
for _, portOld := range portsOld {
portOldMapped := portOld.(map[string]interface{})
oldInternalPort := portOldMapped["internal"]
portFound := false
for _, portNew := range portsNew {
portNewMapped := portNew.(map[string]interface{})
newInternalPort := portNewMapped["internal"]
// port is still there in new
if newInternalPort == oldInternalPort {
log.Printf("[DEBUG] suppress diff ports: comparing port '%v'", oldInternalPort)
portFound = true
if portNewMapped["external"] != portOldMapped["external"] {
log.Printf("[DEBUG] suppress diff ports: 'external' changed for '%v'", oldInternalPort)
return false
}
if portNewMapped["ip"] != portOldMapped["ip"] {
log.Printf("[DEBUG] suppress diff ports: 'ip' changed for '%v'", oldInternalPort)
return false
}
if portNewMapped["protocol"] != portOldMapped["protocol"] {
log.Printf("[DEBUG] suppress diff ports: 'protocol' changed for '%v'", oldInternalPort)
return false
}
}
}
// port was deleted or exchanges in new
if !portFound {
log.Printf("[DEBUG] suppress diff ports: port was deleted '%v'", oldInternalPort)
return false
}
}
return true
}
}

View file

@ -0,0 +1,115 @@
package docker
import (
"fmt"
"log"
"sort"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func resourceDockerContainerMigrateState(
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
switch v {
case 0:
log.Println("[INFO] Found Docker Container State v0; migrating to v1")
return migrateDockerContainerMigrateStateV0toV1(is, meta)
default:
return is, fmt.Errorf("Unexpected schema version: %d", v)
}
}
func migrateDockerContainerMigrateStateV0toV1(is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
if is.Empty() {
log.Println("[DEBUG] Empty InstanceState; nothing to migrate.")
return is, nil
}
log.Printf("[DEBUG] Docker Container Attributes before Migration: %#v", is.Attributes)
err := updateV0ToV1PortsOrder(is, meta)
log.Printf("[DEBUG] Docker Container Attributes after State Migration: %#v", is.Attributes)
return is, err
}
type mappedPort struct {
internal int
external int
ip string
protocol string
}
type byPort []mappedPort
func (s byPort) Len() int {
return len(s)
}
func (s byPort) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s byPort) Less(i, j int) bool {
return s[i].internal < s[j].internal
}
func updateV0ToV1PortsOrder(is *terraform.InstanceState, meta interface{}) error {
reader := &schema.MapFieldReader{
Schema: resourceDockerContainer().Schema,
Map: schema.BasicMapReader(is.Attributes),
}
writer := &schema.MapFieldWriter{
Schema: resourceDockerContainer().Schema,
}
result, err := reader.ReadField([]string{"ports"})
if err != nil {
return err
}
if result.Value == nil {
return nil
}
// map the ports into a struct, so they can be sorted easily
portsMapped := make([]mappedPort, 0)
portsRaw := result.Value.([]interface{})
for _, portRaw := range portsRaw {
if portRaw == nil {
continue
}
portTyped := portRaw.(map[string]interface{})
portMapped := mappedPort{
internal: portTyped["internal"].(int),
external: portTyped["external"].(int),
ip: portTyped["ip"].(string),
protocol: portTyped["protocol"].(string),
}
portsMapped = append(portsMapped, portMapped)
}
sort.Sort(byPort(portsMapped))
// map the sorted ports to an output structure tf can write
outputPorts := make([]interface{}, 0)
for _, mappedPort := range portsMapped {
outputPort := make(map[string]interface{}, 0)
outputPort["internal"] = mappedPort.internal
outputPort["external"] = mappedPort.external
outputPort["ip"] = mappedPort.ip
outputPort["protocol"] = mappedPort.protocol
outputPorts = append(outputPorts, outputPort)
}
// store them back to state
if err := writer.WriteField([]string{"ports"}, outputPorts); err != nil {
return err
}
for k, v := range writer.Map() {
is.Attributes[k] = v
}
return nil
}