2014-05-28 15:56:43 -05:00
|
|
|
package terraform
|
|
|
|
|
2014-06-05 04:32:10 -05:00
|
|
|
import (
|
2014-09-23 13:43:21 -05:00
|
|
|
"bufio"
|
2014-06-10 13:22:32 -05:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2014-09-23 13:43:21 -05:00
|
|
|
"reflect"
|
2014-06-10 13:22:32 -05:00
|
|
|
"sort"
|
2014-06-10 13:30:54 -05:00
|
|
|
"strings"
|
2014-06-05 04:32:10 -05:00
|
|
|
)
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
// DiffChangeType is an enum with the kind of changes a diff has planned.
|
|
|
|
type DiffChangeType byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
DiffInvalid DiffChangeType = iota
|
|
|
|
DiffNone
|
|
|
|
DiffCreate
|
|
|
|
DiffUpdate
|
|
|
|
DiffDestroy
|
|
|
|
DiffDestroyCreate
|
|
|
|
)
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
// Diff trackes the changes that are necessary to apply a configuration
|
|
|
|
// to an existing infrastructure.
|
2014-05-28 15:56:43 -05:00
|
|
|
type Diff struct {
|
2014-09-23 13:43:21 -05:00
|
|
|
// Modules contains all the modules that have a diff
|
|
|
|
Modules []*ModuleDiff
|
2014-06-05 04:32:10 -05:00
|
|
|
}
|
|
|
|
|
2014-09-23 16:15:40 -05:00
|
|
|
// AddModule adds the module with the given path to the diff.
|
|
|
|
//
|
|
|
|
// This should be the preferred method to add module diffs since it
|
|
|
|
// allows us to optimize lookups later as well as control sorting.
|
|
|
|
func (d *Diff) AddModule(path []string) *ModuleDiff {
|
|
|
|
m := &ModuleDiff{Path: path}
|
|
|
|
m.init()
|
|
|
|
d.Modules = append(d.Modules, m)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
// ModuleByPath is used to lookup the module diff for the given path.
|
|
|
|
// This should be the prefered lookup mechanism as it allows for future
|
|
|
|
// lookup optimizations.
|
|
|
|
func (d *Diff) ModuleByPath(path []string) *ModuleDiff {
|
|
|
|
if d == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, mod := range d.Modules {
|
|
|
|
if mod.Path == nil {
|
|
|
|
panic("missing module path")
|
|
|
|
}
|
|
|
|
if reflect.DeepEqual(mod.Path, path) {
|
|
|
|
return mod
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RootModule returns the ModuleState for the root module
|
|
|
|
func (d *Diff) RootModule() *ModuleDiff {
|
|
|
|
root := d.ModuleByPath(rootModulePath)
|
|
|
|
if root == nil {
|
|
|
|
panic("missing root module")
|
|
|
|
}
|
|
|
|
return root
|
|
|
|
}
|
|
|
|
|
2014-09-24 16:37:24 -05:00
|
|
|
// Empty returns true if the diff has no changes.
|
|
|
|
func (d *Diff) Empty() bool {
|
|
|
|
for _, m := range d.Modules {
|
|
|
|
if !m.Empty() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
func (d *Diff) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
for _, m := range d.Modules {
|
|
|
|
mStr := m.String()
|
|
|
|
|
|
|
|
// If we're the root module, we just write the output directly.
|
|
|
|
if reflect.DeepEqual(m.Path, rootModulePath) {
|
|
|
|
buf.WriteString(mStr + "\n")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], ".")))
|
|
|
|
|
|
|
|
s := bufio.NewScanner(strings.NewReader(mStr))
|
|
|
|
for s.Scan() {
|
|
|
|
buf.WriteString(fmt.Sprintf(" %s\n", s.Text()))
|
2014-06-10 13:22:32 -05:00
|
|
|
}
|
2014-09-23 13:43:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return strings.TrimSpace(buf.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Diff) init() {
|
|
|
|
if d.Modules == nil {
|
|
|
|
rootDiff := &ModuleDiff{Path: rootModulePath}
|
|
|
|
d.Modules = []*ModuleDiff{rootDiff}
|
|
|
|
}
|
|
|
|
for _, m := range d.Modules {
|
|
|
|
m.init()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ModuleDiff tracks the differences between resources to apply within
|
|
|
|
// a single module.
|
|
|
|
type ModuleDiff struct {
|
|
|
|
Path []string
|
|
|
|
Resources map[string]*InstanceDiff
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *ModuleDiff) init() {
|
|
|
|
if d.Resources == nil {
|
|
|
|
d.Resources = make(map[string]*InstanceDiff)
|
|
|
|
}
|
|
|
|
for _, r := range d.Resources {
|
|
|
|
r.init()
|
|
|
|
}
|
2014-05-28 15:56:43 -05:00
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
// ChangeType returns the type of changes that the diff for this
|
|
|
|
// module includes.
|
|
|
|
//
|
|
|
|
// At a module level, this will only be DiffNone, DiffUpdate, DiffDestroy, or
|
|
|
|
// DiffCreate. If an instance within the module has a DiffDestroyCreate
|
|
|
|
// then this will register as a DiffCreate for a module.
|
|
|
|
func (d *ModuleDiff) ChangeType() DiffChangeType {
|
|
|
|
result := DiffNone
|
|
|
|
for _, r := range d.Resources {
|
|
|
|
change := r.ChangeType()
|
|
|
|
switch change {
|
|
|
|
case DiffCreate:
|
|
|
|
fallthrough
|
|
|
|
case DiffDestroy:
|
|
|
|
if result == DiffNone {
|
|
|
|
result = change
|
|
|
|
}
|
|
|
|
case DiffDestroyCreate:
|
|
|
|
fallthrough
|
|
|
|
case DiffUpdate:
|
|
|
|
result = DiffUpdate
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:43:21 -05:00
|
|
|
// Empty returns true if the diff has no changes within this module.
|
|
|
|
func (d *ModuleDiff) Empty() bool {
|
2014-06-19 16:57:36 -05:00
|
|
|
if len(d.Resources) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rd := range d.Resources {
|
2014-06-26 00:10:25 -05:00
|
|
|
if !rd.Empty() {
|
2014-06-19 16:57:36 -05:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-09-25 00:54:51 -05:00
|
|
|
// IsRoot says whether or not this module diff is for the root module.
|
|
|
|
func (d *ModuleDiff) IsRoot() bool {
|
|
|
|
return reflect.DeepEqual(d.Path, rootModulePath)
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:22:32 -05:00
|
|
|
// String outputs the diff in a long but command-line friendly output
|
|
|
|
// format that users can read to quickly inspect a diff.
|
2014-09-23 13:43:21 -05:00
|
|
|
func (d *ModuleDiff) String() string {
|
2014-06-10 13:22:32 -05:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
|
|
|
names := make([]string, 0, len(d.Resources))
|
|
|
|
for name, _ := range d.Resources {
|
|
|
|
names = append(names, name)
|
|
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
|
|
|
|
for _, name := range names {
|
2014-06-10 13:27:17 -05:00
|
|
|
rdiff := d.Resources[name]
|
|
|
|
|
2014-06-10 13:37:04 -05:00
|
|
|
crud := "UPDATE"
|
2014-09-19 23:47:53 -05:00
|
|
|
if rdiff.RequiresNew() && (rdiff.Destroy || rdiff.DestroyTainted) {
|
2014-06-25 23:58:33 -05:00
|
|
|
crud = "DESTROY/CREATE"
|
|
|
|
} else if rdiff.Destroy {
|
|
|
|
crud = "DESTROY"
|
|
|
|
} else if rdiff.RequiresNew() {
|
2014-06-10 13:37:04 -05:00
|
|
|
crud = "CREATE"
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString(fmt.Sprintf(
|
|
|
|
"%s: %s\n",
|
|
|
|
crud,
|
|
|
|
name))
|
2014-06-10 13:22:32 -05:00
|
|
|
|
2014-06-10 13:30:54 -05:00
|
|
|
keyLen := 0
|
2014-06-10 13:27:17 -05:00
|
|
|
keys := make([]string, 0, len(rdiff.Attributes))
|
|
|
|
for key, _ := range rdiff.Attributes {
|
2014-07-08 18:58:31 -05:00
|
|
|
if key == "id" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:27:17 -05:00
|
|
|
keys = append(keys, key)
|
2014-06-10 13:30:54 -05:00
|
|
|
if len(key) > keyLen {
|
|
|
|
keyLen = len(key)
|
|
|
|
}
|
2014-06-10 13:27:17 -05:00
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
for _, attrK := range keys {
|
|
|
|
attrDiff := rdiff.Attributes[attrK]
|
|
|
|
|
2014-06-10 13:22:32 -05:00
|
|
|
v := attrDiff.New
|
|
|
|
if attrDiff.NewComputed {
|
|
|
|
v = "<computed>"
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:33:59 -05:00
|
|
|
newResource := ""
|
|
|
|
if attrDiff.RequiresNew {
|
|
|
|
newResource = " (forces new resource)"
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:22:32 -05:00
|
|
|
buf.WriteString(fmt.Sprintf(
|
2014-06-10 13:33:59 -05:00
|
|
|
" %s:%s %#v => %#v%s\n",
|
2014-06-10 13:22:32 -05:00
|
|
|
attrK,
|
2014-06-10 13:30:54 -05:00
|
|
|
strings.Repeat(" ", keyLen-len(attrK)),
|
2014-06-10 13:22:32 -05:00
|
|
|
attrDiff.Old,
|
2014-06-10 13:33:59 -05:00
|
|
|
v,
|
|
|
|
newResource))
|
2014-06-10 13:22:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|
2014-06-10 13:37:04 -05:00
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
// InstanceDiff is the diff of a resource from some state to another.
|
|
|
|
type InstanceDiff struct {
|
2014-09-19 23:47:53 -05:00
|
|
|
Attributes map[string]*ResourceAttrDiff
|
|
|
|
Destroy bool
|
|
|
|
DestroyTainted bool
|
2014-06-10 13:37:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// ResourceAttrDiff is the diff of a single attribute of a resource.
|
|
|
|
type ResourceAttrDiff struct {
|
2014-07-01 13:23:40 -05:00
|
|
|
Old string // Old Value
|
|
|
|
New string // New Value
|
|
|
|
NewComputed bool // True if new value is computed (unknown currently)
|
2014-07-09 11:51:36 -05:00
|
|
|
NewRemoved bool // True if this attribute is being removed
|
2014-07-01 13:23:40 -05:00
|
|
|
NewExtra interface{} // Extra information for the provider
|
|
|
|
RequiresNew bool // True if change requires new resource
|
2014-06-23 14:49:30 -05:00
|
|
|
Type DiffAttrType
|
2014-06-10 13:37:04 -05:00
|
|
|
}
|
|
|
|
|
2014-07-26 23:02:34 -05:00
|
|
|
func (d *ResourceAttrDiff) GoString() string {
|
|
|
|
return fmt.Sprintf("*%#v", *d)
|
|
|
|
}
|
|
|
|
|
2014-06-23 14:49:30 -05:00
|
|
|
// DiffAttrType is an enum type that says whether a resource attribute
|
|
|
|
// diff is an input attribute (comes from the configuration) or an
|
|
|
|
// output attribute (comes as a result of applying the configuration). An
|
|
|
|
// example input would be "ami" for AWS and an example output would be
|
|
|
|
// "private_ip".
|
|
|
|
type DiffAttrType byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
DiffAttrUnknown DiffAttrType = iota
|
|
|
|
DiffAttrInput
|
|
|
|
DiffAttrOutput
|
|
|
|
)
|
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
func (d *InstanceDiff) init() {
|
2014-09-23 13:43:21 -05:00
|
|
|
if d.Attributes == nil {
|
|
|
|
d.Attributes = make(map[string]*ResourceAttrDiff)
|
|
|
|
}
|
2014-07-08 18:58:31 -05:00
|
|
|
}
|
|
|
|
|
2014-09-24 19:51:45 -05:00
|
|
|
// ChangeType returns the DiffChangeType represented by the diff
|
|
|
|
// for this single instance.
|
|
|
|
func (d *InstanceDiff) ChangeType() DiffChangeType {
|
|
|
|
if d.Empty() {
|
|
|
|
return DiffNone
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.RequiresNew() && (d.Destroy || d.DestroyTainted) {
|
|
|
|
return DiffDestroyCreate
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.Destroy {
|
|
|
|
return DiffDestroy
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.RequiresNew() {
|
|
|
|
return DiffCreate
|
|
|
|
}
|
|
|
|
|
|
|
|
return DiffUpdate
|
|
|
|
}
|
|
|
|
|
2014-06-20 13:03:33 -05:00
|
|
|
// Empty returns true if this diff encapsulates no changes.
|
2014-09-17 18:33:24 -05:00
|
|
|
func (d *InstanceDiff) Empty() bool {
|
2014-06-20 13:03:33 -05:00
|
|
|
if d == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-06-25 23:58:33 -05:00
|
|
|
return !d.Destroy && len(d.Attributes) == 0
|
2014-06-20 13:03:33 -05:00
|
|
|
}
|
|
|
|
|
2014-06-10 13:37:04 -05:00
|
|
|
// RequiresNew returns true if the diff requires the creation of a new
|
|
|
|
// resource (implying the destruction of the old).
|
2014-09-17 18:33:24 -05:00
|
|
|
func (d *InstanceDiff) RequiresNew() bool {
|
2014-06-17 20:10:38 -05:00
|
|
|
if d == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:37:04 -05:00
|
|
|
for _, rd := range d.Attributes {
|
2014-07-22 21:32:46 -05:00
|
|
|
if rd != nil && rd.RequiresNew {
|
2014-06-10 13:37:04 -05:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
2014-07-22 21:32:46 -05:00
|
|
|
|
2014-09-17 18:33:24 -05:00
|
|
|
// Same checks whether or not to InstanceDiff are the "same." When
|
2014-07-22 21:32:46 -05:00
|
|
|
// we say "same", it is not necessarily exactly equal. Instead, it is
|
|
|
|
// just checking that the same attributes are changing, a destroy
|
|
|
|
// isn't suddenly happening, etc.
|
2014-09-17 18:33:24 -05:00
|
|
|
func (d *InstanceDiff) Same(d2 *InstanceDiff) bool {
|
2014-07-22 21:39:48 -05:00
|
|
|
if d == nil && d2 == nil {
|
|
|
|
return true
|
|
|
|
} else if d == nil || d2 == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2014-07-22 21:32:46 -05:00
|
|
|
if d.Destroy != d2.Destroy {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if d.RequiresNew() != d2.RequiresNew() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(d.Attributes) != len(d2.Attributes) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
ks := make(map[string]struct{})
|
|
|
|
for k, _ := range d.Attributes {
|
|
|
|
ks[k] = struct{}{}
|
|
|
|
}
|
|
|
|
for k, _ := range d2.Attributes {
|
|
|
|
delete(ks, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ks) > 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|