mirror of
https://github.com/kreuzwerker/terraform-provider-docker.git
synced 2025-12-20 22:59:42 -05:00
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:
parent
a853943351
commit
1ac859bd81
3 changed files with 175 additions and 4 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
115
docker/resource_docker_container_migrate.go
Normal file
115
docker/resource_docker_container_migrate.go
Normal 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
|
||||
}
|
||||
Loading…
Reference in a new issue