read+write the new dependencies in the statefile

The test fixture did not like having modules when using the generic json
map, so read and compare the states in the final *File datastructure.
This commit is contained in:
James Bardin 2019-10-15 18:10:54 -04:00
parent 2c3c011f20
commit 5a0a0020a0
9 changed files with 169 additions and 24 deletions

View File

@ -88,6 +88,7 @@ func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObjec
Value: val, Value: val,
Status: os.Status, Status: os.Status,
Dependencies: os.Dependencies, Dependencies: os.Dependencies,
DependsOn: os.DependsOn,
Private: os.Private, Private: os.Private,
}, nil }, nil
} }

View File

@ -172,6 +172,7 @@ func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
AttrsFlat: attrsFlat, AttrsFlat: attrsFlat,
AttrsJSON: attrsJSON, AttrsJSON: attrsJSON,
Dependencies: dependencies, Dependencies: dependencies,
DependsOn: dependsOn,
} }
} }

View File

@ -2,7 +2,6 @@ package statefile
import ( import (
"bytes" "bytes"
"encoding/json"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -11,8 +10,6 @@ import (
"testing" "testing"
"github.com/go-test/deep" "github.com/go-test/deep"
tfversion "github.com/hashicorp/terraform/version"
) )
func TestRoundtrip(t *testing.T) { func TestRoundtrip(t *testing.T) {
@ -22,8 +19,6 @@ func TestRoundtrip(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
currentVersion := tfversion.Version
for _, info := range entries { for _, info := range entries {
const inSuffix = ".in.tfstate" const inSuffix = ".in.tfstate"
const outSuffix = ".out.tfstate" const outSuffix = ".out.tfstate"
@ -39,14 +34,20 @@ func TestRoundtrip(t *testing.T) {
outName := name + outSuffix outName := name + outSuffix
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
ir, err := os.Open(filepath.Join(dir, inName))
if err != nil {
t.Fatal(err)
}
oSrcWant, err := ioutil.ReadFile(filepath.Join(dir, outName)) oSrcWant, err := ioutil.ReadFile(filepath.Join(dir, outName))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
oWant, diags := readStateV4(oSrcWant)
if diags.HasErrors() {
t.Fatal(diags.Err())
}
ir, err := os.Open(filepath.Join(dir, inName))
if err != nil {
t.Fatal(err)
}
defer ir.Close()
f, err := Read(ir) f, err := Read(ir)
if err != nil { if err != nil {
@ -58,20 +59,12 @@ func TestRoundtrip(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
oSrcGot := buf.Bytes() oSrcWritten := buf.Bytes()
var oGot, oWant map[string]interface{} oGot, diags := readStateV4(oSrcWritten)
err = json.Unmarshal(oSrcGot, &oGot) if diags.HasErrors() {
if err != nil { t.Fatal(diags.Err())
t.Fatalf("result isn't JSON: %s", err)
} }
err = json.Unmarshal(oSrcWant, &oWant)
if err != nil {
t.Fatalf("wanted result isn't JSON: %s", err)
}
// A newly written state should always reflect the current terraform version.
oWant["terraform_version"] = currentVersion
problems := deep.Equal(oGot, oWant) problems := deep.Equal(oGot, oWant)
sort.Strings(problems) sort.Strings(problems)

View File

@ -0,0 +1,36 @@
{
"version": 4,
"serial": 0,
"lineage": "f2968801-fa14-41ab-a044-224f3a4adf04",
"terraform_version": "0.12.0",
"outputs": {
"numbers": {
"type": "string",
"value": "0,1"
}
},
"resources": [
{
"module": "module.modA",
"mode": "managed",
"type": "null_resource",
"name": "resource",
"provider": "provider.null",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "4639265839606265182",
"triggers": {
"input": "test"
}
},
"private": "bnVsbA==",
"depends_on": [
"var.input"
]
}
]
}
]
}

View File

@ -0,0 +1 @@
v4-foreach.in.tfstate

View File

@ -0,0 +1,88 @@
{
"version": 4,
"terraform_version": "0.12.0",
"serial": 0,
"lineage": "f2968801-fa14-41ab-a044-224f3a4adf04",
"outputs": {
"numbers": {
"value": "0,1",
"type": "string"
}
},
"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.foo"
]
}
]
},
{
"module": "module.modB",
"mode": "managed",
"type": "null_resource",
"name": "bar",
"each": "map",
"provider": "provider.null",
"instances": [
{
"index_key": "a",
"schema_version": 0,
"attributes_flat": {
"id": "8212585058302700791"
},
"dependencies": [
"module.modA.null_resource.resource"
]
},
{
"index_key": "b",
"schema_version": 0,
"attributes_flat": {
"id": "1523897709610803586"
},
"dependencies": [
"module.modA.null_resource.resource"
]
}
]
},
{
"module": "module.modA",
"mode": "managed",
"type": "null_resource",
"name": "resource",
"provider": "provider.null",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "4639265839606265182",
"triggers": {
"input": "test"
}
},
"private": "bnVsbA==",
"dependencies": [
"null_resource.bar"
],
"depends_on": [
"var.input"
]
}
]
}
]
}

View File

@ -0,0 +1 @@
v4-modules.in.tfstate

View File

@ -313,7 +313,7 @@ func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2,
Status: status, Status: status,
Deposed: string(deposedKey), Deposed: string(deposedKey),
AttributesFlat: attributes, AttributesFlat: attributes,
Dependencies: dependencies, DependsOn: dependencies,
SchemaVersion: schemaVersion, SchemaVersion: schemaVersion,
PrivateRaw: privateJSON, PrivateRaw: privateJSON,
}, nil }, nil

View File

@ -181,7 +181,10 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
} }
{ {
depsRaw := isV4.Dependencies // Allow both the deprecated `depends_on` and new
// `dependencies` to coexist for now so resources can be
// upgraded as they are refreshed.
depsRaw := isV4.DependsOn
deps := make([]addrs.Referenceable, 0, len(depsRaw)) deps := make([]addrs.Referenceable, 0, len(depsRaw))
for _, depRaw := range depsRaw { for _, depRaw := range depsRaw {
ref, refDiags := addrs.ParseRefStr(depRaw) ref, refDiags := addrs.ParseRefStr(depRaw)
@ -202,6 +205,20 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
} }
deps = append(deps, ref.Subject) deps = append(deps, ref.Subject)
} }
obj.DependsOn = deps
}
{
depsRaw := isV4.Dependencies
deps := make([]addrs.AbsResource, 0, len(depsRaw))
for _, depRaw := range depsRaw {
addr, addrDiags := addrs.ParseAbsResourceStr(depRaw)
diags = diags.Append(addrDiags)
if addrDiags.HasErrors() {
continue
}
deps = append(deps, addr)
}
obj.Dependencies = deps obj.Dependencies = deps
} }
@ -466,6 +483,11 @@ func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstanc
deps[i] = depAddr.String() deps[i] = depAddr.String()
} }
depOn := make([]string, len(obj.DependsOn))
for i, depAddr := range obj.DependsOn {
depOn[i] = depAddr.String()
}
var rawKey interface{} var rawKey interface{}
switch tk := key.(type) { switch tk := key.(type) {
case addrs.IntKey: case addrs.IntKey:
@ -491,6 +513,7 @@ func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstanc
AttributesRaw: obj.AttrsJSON, AttributesRaw: obj.AttrsJSON,
PrivateRaw: privateRaw, PrivateRaw: privateRaw,
Dependencies: deps, Dependencies: deps,
DependsOn: depOn,
}), diags }), diags
} }
@ -540,7 +563,8 @@ type instanceObjectStateV4 struct {
PrivateRaw []byte `json:"private,omitempty"` PrivateRaw []byte `json:"private,omitempty"`
Dependencies []string `json:"depends_on,omitempty"` Dependencies []string `json:"dependencies,omitempty"`
DependsOn []string `json:"depends_on,omitempty"`
} }
// stateVersionV4 is a weird special type we use to produce our hard-coded // stateVersionV4 is a weird special type we use to produce our hard-coded