states/statefile: tolerate and ignore invalid depends_on in version 3
The state refactoring command "terraform state mv" in Terraform 0.11 does not update existing dependency addresses recorded in the state when it moves objects around, and Terraform only updates the dependency addresses in the state when it performs a full update on a resource instance, and so it's a common problem for folks updating from Terraform 0.11 with resource names that are not valid identifiers to run into state upgrade errors even though they have followed the instructions produced by "terraform 0.12checklist". Dependencies are synced from config during every refresh walk anyway, so in practice we can get away with just discarding invalid dependency addresses and letting the refresh walk update them. In practice these addresses are unlikely to be pointing at a resource that actually exists anyway, because if so Terraform 0.12's configuration parser wouldn't be able to interpret it. Discarding invalid dependency addresses allows the state upgrade to complete successfully in such cases and thus gives the refresh step an opportunity to repair the problem.
This commit is contained in:
parent
98b0e62fb9
commit
1ae47ae314
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"terraform_version": "0.7.13",
|
||||||
|
"serial": 0,
|
||||||
|
"lineage": "f2968801-fa14-41ab-a044-224f3a4adf04",
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"root"
|
||||||
|
],
|
||||||
|
"outputs": {
|
||||||
|
"numbers": {
|
||||||
|
"sensitive": false,
|
||||||
|
"type": "string",
|
||||||
|
"value": "0,1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"null_resource.bar": {
|
||||||
|
"type": "null_resource",
|
||||||
|
"depends_on": [
|
||||||
|
"null_resource.valid",
|
||||||
|
"null_resource.1invalid"
|
||||||
|
],
|
||||||
|
"primary": {
|
||||||
|
"id": "5388490630832483079",
|
||||||
|
"attributes": {
|
||||||
|
"id": "5388490630832483079",
|
||||||
|
"triggers.%": "1",
|
||||||
|
"triggers.whaaat": "0,1"
|
||||||
|
},
|
||||||
|
"meta": {},
|
||||||
|
"tainted": false
|
||||||
|
},
|
||||||
|
"deposed": [],
|
||||||
|
"provider": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"depends_on": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"serial": 0,
|
||||||
|
"lineage": "f2968801-fa14-41ab-a044-224f3a4adf04",
|
||||||
|
"terraform_version": "0.7.13",
|
||||||
|
"outputs": {
|
||||||
|
"numbers": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "0,1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "null_resource",
|
||||||
|
"name": "bar",
|
||||||
|
"provider": "provider.null",
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes_flat": {
|
||||||
|
"id": "5388490630832483079",
|
||||||
|
"triggers.%": "1",
|
||||||
|
"triggers.whaaat": "0,1"
|
||||||
|
},
|
||||||
|
"depends_on": [
|
||||||
|
"null_resource.valid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package statefile
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -299,13 +300,33 @@ func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies := make([]string, len(rsOld.Dependencies))
|
dependencies := make([]string, 0, len(rsOld.Dependencies))
|
||||||
for i, v := range rsOld.Dependencies {
|
for _, v := range rsOld.Dependencies {
|
||||||
depStr, err := parseLegacyDependency(v)
|
depStr, err := parseLegacyDependency(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid dependency reference %q: %s", v, err)
|
// We just drop invalid dependencies on the floor here, because
|
||||||
|
// they tend to get left behind in Terraform 0.11 when resources
|
||||||
|
// are renamed or moved between modules and there's no automatic
|
||||||
|
// way to fix them here. In practice it shouldn't hurt to miss
|
||||||
|
// a few dependency edges in the state because a subsequent plan
|
||||||
|
// will run a refresh walk first and re-synchronize the
|
||||||
|
// dependencies with the configuration.
|
||||||
|
//
|
||||||
|
// There is one rough edges where this can cause an incorrect
|
||||||
|
// result, though: If the first command the user runs after
|
||||||
|
// upgrading to Terraform 0.12 uses -refresh=false and thus
|
||||||
|
// prevents the dependency reorganization from occurring _and_
|
||||||
|
// that initial plan discovered "orphaned" resources (not present
|
||||||
|
// in configuration any longer) then when the plan is applied the
|
||||||
|
// destroy ordering will be incorrect for the instances of those
|
||||||
|
// resources. We expect that is a rare enough situation that it
|
||||||
|
// isn't a big deal, and even when it _does_ occur it's common for
|
||||||
|
// the apply to succeed anyway unless many separate resources with
|
||||||
|
// complex inter-dependencies are all orphaned at once.
|
||||||
|
log.Printf("statefile: ignoring invalid dependency address %q while upgrading from state version 3 to version 4: %s", v, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
dependencies[i] = depStr
|
dependencies = append(dependencies, depStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &instanceObjectStateV4{
|
return &instanceObjectStateV4{
|
||||||
|
|
Loading…
Reference in New Issue