terraform: InstanceState.Meta is value type interface{}

This changes the type of values in Meta for InstanceState to
`interface{}`. They were `string` before.

This will allow richer structures to be persisted to this without
flatmapping them (down with flatmap!). The documentation clearly states
that only primitives/collections are allowed here.

The only thing using this was helper/schema for schema versioning.
Appropriate type checking was added to make this change safe.

The timeout work @catsby is doing will use this for a richer structure.
This commit is contained in:
Mitchell Hashimoto 2017-02-23 10:44:05 -08:00
parent 9379ec9c60
commit 3342aa580c
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
6 changed files with 45 additions and 23 deletions

View File

@ -353,7 +353,7 @@ func (r *Resource) Data(s *terraform.InstanceState) *ResourceData {
}
// Set the schema version to latest by default
result.meta = map[string]string{
result.meta = map[string]interface{}{
"schema_version": strconv.Itoa(r.SchemaVersion),
}
@ -378,7 +378,22 @@ func (r *Resource) isTopLevel() bool {
// Determines if a given InstanceState needs to be migrated by checking the
// stored version number with the current SchemaVersion
func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) {
stateSchemaVersion, _ := strconv.Atoi(is.Meta["schema_version"])
// Get the raw interface{} value for the schema version. If it doesn't
// exist or is nil then set it to zero.
raw := is.Meta["schema_version"]
if raw == nil {
raw = "0"
}
// Try to convert it to a string. If it isn't a string then we pretend
// that it isn't set at all. It should never not be a string unless it
// was manually tampered with.
rawString, ok := raw.(string)
if !ok {
rawString = "0"
}
stateSchemaVersion, _ := strconv.Atoi(rawString)
return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion
}
@ -386,7 +401,7 @@ func (r *Resource) recordCurrentSchemaVersion(
state *terraform.InstanceState) *terraform.InstanceState {
if state != nil && r.SchemaVersion > 0 {
if state.Meta == nil {
state.Meta = make(map[string]string)
state.Meta = make(map[string]interface{})
}
state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion)
}

View File

@ -23,7 +23,7 @@ type ResourceData struct {
config *terraform.ResourceConfig
state *terraform.InstanceState
diff *terraform.InstanceDiff
meta map[string]string
meta map[string]interface{}
// Don't set
multiReader *MultiLevelFieldReader

View File

@ -52,7 +52,7 @@ func TestResourceApply_create(t *testing.T) {
"id": "foo",
"foo": "42",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
}
@ -210,7 +210,7 @@ func TestResourceApply_destroyPartial(t *testing.T) {
"id": "bar",
"foo": "42",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "3",
},
}
@ -558,7 +558,7 @@ func TestResourceRefresh(t *testing.T) {
"id": "bar",
"foo": "13",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
}
@ -749,7 +749,7 @@ func TestResourceRefresh_needsMigration(t *testing.T) {
Attributes: map[string]string{
"oldfoo": "1.2",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "1",
},
}
@ -765,7 +765,7 @@ func TestResourceRefresh_needsMigration(t *testing.T) {
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
}
@ -803,7 +803,7 @@ func TestResourceRefresh_noMigrationNeeded(t *testing.T) {
Attributes: map[string]string{
"newfoo": "12",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
}
@ -819,7 +819,7 @@ func TestResourceRefresh_noMigrationNeeded(t *testing.T) {
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
}
@ -871,7 +871,7 @@ func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) {
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "1",
},
}
@ -945,7 +945,7 @@ func TestResourceData(t *testing.T) {
}
// Set expectations
state.Meta = map[string]string{
state.Meta = map[string]interface{}{
"schema_version": "2",
}

View File

@ -1541,8 +1541,9 @@ type InstanceState struct {
// Meta is a simple K/V map that is persisted to the State but otherwise
// ignored by Terraform core. It's meant to be used for accounting by
// external client code.
Meta map[string]string `json:"meta"`
// external client code. The value here must only contain Go primitives
// and collections.
Meta map[string]interface{} `json:"meta"`
// Tainted is used to mark a resource for recreation.
Tainted bool `json:"tainted"`
@ -1561,7 +1562,7 @@ func (s *InstanceState) init() {
s.Attributes = make(map[string]string)
}
if s.Meta == nil {
s.Meta = make(map[string]string)
s.Meta = make(map[string]interface{})
}
s.Ephemeral.init()
}

View File

@ -278,7 +278,7 @@ func TestStateDeepCopy(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{},
Meta: map[string]interface{}{},
},
},
},
@ -298,7 +298,7 @@ func TestStateDeepCopy(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{},
Meta: map[string]interface{}{},
},
Deposed: []*InstanceState{
{ID: "test"},
@ -389,7 +389,7 @@ func TestStateEqual(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "1",
},
},
@ -405,7 +405,7 @@ func TestStateEqual(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "2",
},
},
@ -610,7 +610,7 @@ func TestStateIncrementSerialMaybe(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{},
Meta: map[string]interface{}{},
},
},
},
@ -625,7 +625,7 @@ func TestStateIncrementSerialMaybe(t *testing.T) {
Resources: map[string]*ResourceState{
"test_instance.foo": &ResourceState{
Primary: &InstanceState{
Meta: map[string]string{
Meta: map[string]interface{}{
"schema_version": "1",
},
},

View File

@ -150,16 +150,22 @@ func (old *instanceStateV1) upgradeToV2() (*InstanceState, error) {
if err != nil {
return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
}
meta, err := copystructure.Copy(old.Meta)
if err != nil {
return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
}
newMeta := make(map[string]interface{})
for k, v := range meta.(map[string]string) {
newMeta[k] = v
}
return &InstanceState{
ID: old.ID,
Attributes: attributes.(map[string]string),
Ephemeral: *ephemeral,
Meta: meta.(map[string]string),
Meta: newMeta,
}, nil
}