terraform: diff/state work better together, merge
This commit is contained in:
parent
bd1f235b9b
commit
7c6920bba1
|
@ -91,19 +91,19 @@ func TestResourceReplaceVariables(t *testing.T) {
|
|||
}
|
||||
|
||||
/*
|
||||
TODO(mitchellh): Eventually, preserve original config...
|
||||
TODO(mitchellh): Eventually, preserve original config...
|
||||
|
||||
expectedOriginal := &Resource{
|
||||
Name: "foo",
|
||||
Type: "bar",
|
||||
Config: map[string]interface{}{
|
||||
"foo": "${var.bar}",
|
||||
},
|
||||
}
|
||||
expectedOriginal := &Resource{
|
||||
Name: "foo",
|
||||
Type: "bar",
|
||||
Config: map[string]interface{}{
|
||||
"foo": "${var.bar}",
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r, expectedOriginal) {
|
||||
t.Fatalf("bad: %#v", r)
|
||||
}
|
||||
if !reflect.DeepEqual(r, expectedOriginal) {
|
||||
t.Fatalf("bad: %#v", r)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,15 @@ func (d *Diff) init() {
|
|||
})
|
||||
}
|
||||
|
||||
// ResourceAttrDiff is the diff of a single attribute of a resource.
|
||||
//
|
||||
// This tracks the old value, the new value, and whether the change of this
|
||||
// value actually requires an entirely new resource.
|
||||
type ResourceAttrDiff struct {
|
||||
Old string
|
||||
New string
|
||||
RequiresNew bool
|
||||
// ResourceDiff is the diff of a resource from some state to another.
|
||||
type ResourceDiff struct {
|
||||
Attributes map[string]*ResourceAttrDiff
|
||||
}
|
||||
|
||||
// ResourceAttrDiff is the diff of a single attribute of a resource.
|
||||
type ResourceAttrDiff struct {
|
||||
Old string // Old Value
|
||||
New string // New Value
|
||||
NewComputed bool // True if new value is computed (unknown currently)
|
||||
RequiresNew bool // True if change requires new resource
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func testDiffStr(d *Diff) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
names := make([]string, len(d.Resources))
|
||||
for n, _ := range d.Resources {
|
||||
names = append(names, n)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
for _, n := range names {
|
||||
r := d.Resources[n]
|
||||
buf.WriteString(fmt.Sprintf("%s\n", n))
|
||||
for attr, attrDiff := range r {
|
||||
buf.WriteString(fmt.Sprintf(
|
||||
" %s: %#v => %#v\n",
|
||||
attr,
|
||||
attrDiff.Old,
|
||||
attrDiff.New,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
|
@ -28,22 +28,6 @@ type ResourceProvider interface {
|
|||
map[string]interface{}) (ResourceDiff, error)
|
||||
}
|
||||
|
||||
// ResourceDiff is the diff of a resource from some state to another.
|
||||
type ResourceDiff struct {
|
||||
Attributes map[string]*ResourceAttrDiff
|
||||
}
|
||||
|
||||
// ResourceState holds the state of a resource that is used so that
|
||||
// a provider can find and manage an existing resource as well as for
|
||||
// storing attributes that are uesd to populate variables of child
|
||||
// resources.
|
||||
type ResourceState struct {
|
||||
Type string
|
||||
ID string
|
||||
Attributes map[string]string
|
||||
Extra map[string]interface{}
|
||||
}
|
||||
|
||||
// ResourceType is a type of resource that a resource provider can manage.
|
||||
type ResourceType struct {
|
||||
Name string
|
||||
|
|
|
@ -13,6 +13,7 @@ type MockResourceProvider struct {
|
|||
DiffCalled bool
|
||||
DiffState ResourceState
|
||||
DiffDesired map[string]interface{}
|
||||
DiffFn func(ResourceState, map[string]interface{}) (ResourceDiff, error)
|
||||
DiffReturn ResourceDiff
|
||||
DiffReturnError error
|
||||
ResourcesCalled bool
|
||||
|
@ -31,6 +32,10 @@ func (p *MockResourceProvider) Diff(
|
|||
p.DiffCalled = true
|
||||
p.DiffState = state
|
||||
p.DiffDesired = desired
|
||||
if p.DiffFn != nil {
|
||||
return p.DiffFn(state, desired)
|
||||
}
|
||||
|
||||
return p.DiffReturn, p.DiffReturnError
|
||||
}
|
||||
|
||||
|
|
|
@ -6,3 +6,37 @@ package terraform
|
|||
type State struct {
|
||||
resources map[string]ResourceState
|
||||
}
|
||||
|
||||
// ResourceState holds the state of a resource that is used so that
|
||||
// a provider can find and manage an existing resource as well as for
|
||||
// storing attributes that are uesd to populate variables of child
|
||||
// resources.
|
||||
//
|
||||
// Attributes has attributes about the created resource that are
|
||||
// queryable in interpolation: "${type.id.attr}"
|
||||
//
|
||||
// Extra is just extra data that a provider can return that we store
|
||||
// for later, but is not exposed in any way to the user.
|
||||
type ResourceState struct {
|
||||
ID string
|
||||
Attributes map[string]string
|
||||
Extra map[string]interface{}
|
||||
}
|
||||
|
||||
// MergeDiff takes a ResourceDiff and merges the attributes into
|
||||
// this resource state in order to generate a new state. This new
|
||||
// state can be used to provide updated attribute lookups for
|
||||
// variable interpolation.
|
||||
func (s *ResourceState) MergeDiff(
|
||||
d map[string]*ResourceAttrDiff) ResourceState {
|
||||
result := *s
|
||||
result.Attributes = make(map[string]string)
|
||||
for k, v := range s.Attributes {
|
||||
result.Attributes[k] = v
|
||||
}
|
||||
for k, diff := range d {
|
||||
result.Attributes[k] = diff.New
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResourceState_MergeDiff(t *testing.T) {
|
||||
rs := ResourceState{
|
||||
ID: "foo",
|
||||
Attributes: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
|
||||
diff := map[string]*ResourceAttrDiff{
|
||||
"foo": &ResourceAttrDiff{
|
||||
Old: "bar",
|
||||
New: "baz",
|
||||
},
|
||||
"bar": &ResourceAttrDiff{
|
||||
Old: "",
|
||||
New: "foo",
|
||||
},
|
||||
}
|
||||
|
||||
rs2 := rs.MergeDiff(diff)
|
||||
|
||||
expected := map[string]string{
|
||||
"foo": "baz",
|
||||
"bar": "foo",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, rs2.Attributes) {
|
||||
t.Fatalf("bad: %#v", rs2.Attributes)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package terraform
|
|||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
|
@ -113,6 +114,12 @@ func TestTerraformDiff(t *testing.T) {
|
|||
if len(diff.Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", diff.Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(testDiffStr(diff))
|
||||
expected := strings.TrimSpace(testTerraformDiffStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func testConfig(t *testing.T, name string) *config.Config {
|
||||
|
@ -133,17 +140,28 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory {
|
|||
}
|
||||
|
||||
return func() (ResourceProvider, error) {
|
||||
var diff ResourceDiff
|
||||
diff.Attributes = map[string]*ResourceAttrDiff{
|
||||
n: &ResourceAttrDiff{
|
||||
Old: "foo",
|
||||
New: "bar",
|
||||
},
|
||||
diffFn := func(
|
||||
_ ResourceState,
|
||||
c map[string]interface{}) (ResourceDiff, error) {
|
||||
var diff ResourceDiff
|
||||
diff.Attributes = make(map[string]*ResourceAttrDiff)
|
||||
for k, v := range c {
|
||||
if _, ok := v.(string); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
diff.Attributes[k] = &ResourceAttrDiff{
|
||||
Old: "",
|
||||
New: v.(string),
|
||||
}
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
result := &MockResourceProvider{
|
||||
Meta: n,
|
||||
DiffReturn: diff,
|
||||
DiffFn: diffFn,
|
||||
ResourcesReturn: resources,
|
||||
}
|
||||
|
||||
|
@ -184,3 +202,10 @@ func testTerraform(t *testing.T, name string) *Terraform {
|
|||
|
||||
return tf
|
||||
}
|
||||
|
||||
const testTerraformDiffStr = `
|
||||
aws_instance.bar
|
||||
foo: "" => "${aws_instance.foo.num}"
|
||||
aws_instance.foo
|
||||
num: "" => "2"
|
||||
`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
resource "aws_instance" "foo" {
|
||||
num = 2
|
||||
num = "2"
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
|
|
Loading…
Reference in New Issue