github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/terraform/context_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  func TestNewContextState(t *testing.T) {
    12  	cases := map[string]struct {
    13  		Input *ContextOpts
    14  		Err   bool
    15  	}{
    16  		"empty TFVersion": {
    17  			&ContextOpts{
    18  				State: &State{},
    19  			},
    20  			false,
    21  		},
    22  
    23  		"past TFVersion": {
    24  			&ContextOpts{
    25  				State: &State{TFVersion: "0.1.2"},
    26  			},
    27  			false,
    28  		},
    29  
    30  		"equal TFVersion": {
    31  			&ContextOpts{
    32  				State: &State{TFVersion: Version},
    33  			},
    34  			false,
    35  		},
    36  
    37  		"future TFVersion": {
    38  			&ContextOpts{
    39  				State: &State{TFVersion: "99.99.99"},
    40  			},
    41  			true,
    42  		},
    43  
    44  		"future TFVersion, allowed": {
    45  			&ContextOpts{
    46  				State:              &State{TFVersion: "99.99.99"},
    47  				StateFutureAllowed: true,
    48  			},
    49  			false,
    50  		},
    51  	}
    52  
    53  	for k, tc := range cases {
    54  		ctx, err := NewContext(tc.Input)
    55  		if (err != nil) != tc.Err {
    56  			t.Fatalf("%s: err: %s", k, err)
    57  		}
    58  		if err != nil {
    59  			continue
    60  		}
    61  
    62  		// Version should always be set to our current
    63  		if ctx.state.TFVersion != Version {
    64  			t.Fatalf("%s: state not set to current version", k)
    65  		}
    66  	}
    67  }
    68  
    69  func testContext2(t *testing.T, opts *ContextOpts) *Context {
    70  	ctx, err := NewContext(opts)
    71  	if err != nil {
    72  		t.Fatalf("err: %s", err)
    73  	}
    74  
    75  	return ctx
    76  }
    77  
    78  func testApplyFn(
    79  	info *InstanceInfo,
    80  	s *InstanceState,
    81  	d *InstanceDiff) (*InstanceState, error) {
    82  	if d.Destroy {
    83  		return nil, nil
    84  	}
    85  
    86  	id := "foo"
    87  	if idAttr, ok := d.Attributes["id"]; ok && !idAttr.NewComputed {
    88  		id = idAttr.New
    89  	}
    90  
    91  	result := &InstanceState{
    92  		ID:         id,
    93  		Attributes: make(map[string]string),
    94  	}
    95  
    96  	// Copy all the prior attributes
    97  	for k, v := range s.Attributes {
    98  		result.Attributes[k] = v
    99  	}
   100  
   101  	if d != nil {
   102  		result = result.MergeDiff(d)
   103  	}
   104  	return result, nil
   105  }
   106  
   107  func testDiffFn(
   108  	info *InstanceInfo,
   109  	s *InstanceState,
   110  	c *ResourceConfig) (*InstanceDiff, error) {
   111  	diff := new(InstanceDiff)
   112  	diff.Attributes = make(map[string]*ResourceAttrDiff)
   113  
   114  	if s != nil {
   115  		diff.DestroyTainted = s.Tainted
   116  	}
   117  
   118  	for k, v := range c.Raw {
   119  		if _, ok := v.(string); !ok {
   120  			continue
   121  		}
   122  
   123  		// Ignore __-prefixed keys since they're used for magic
   124  		if k[0] == '_' && k[1] == '_' {
   125  			continue
   126  		}
   127  
   128  		if k == "nil" {
   129  			return nil, nil
   130  		}
   131  
   132  		// This key is used for other purposes
   133  		if k == "compute_value" {
   134  			continue
   135  		}
   136  
   137  		if k == "compute" {
   138  			attrDiff := &ResourceAttrDiff{
   139  				Old:         "",
   140  				New:         "",
   141  				NewComputed: true,
   142  			}
   143  
   144  			if cv, ok := c.Config["compute_value"]; ok {
   145  				if cv.(string) == "1" {
   146  					attrDiff.NewComputed = false
   147  					attrDiff.New = fmt.Sprintf("computed_%s", v.(string))
   148  				}
   149  			}
   150  
   151  			diff.Attributes[v.(string)] = attrDiff
   152  			continue
   153  		}
   154  
   155  		// If this key is not computed, then look it up in the
   156  		// cleaned config.
   157  		found := false
   158  		for _, ck := range c.ComputedKeys {
   159  			if ck == k {
   160  				found = true
   161  				break
   162  			}
   163  		}
   164  		if !found {
   165  			v = c.Config[k]
   166  		}
   167  
   168  		attrDiff := &ResourceAttrDiff{
   169  			Old: "",
   170  		}
   171  
   172  		if reflect.DeepEqual(v, []interface{}{}) {
   173  			attrDiff.New = ""
   174  		} else {
   175  			attrDiff.New = v.(string)
   176  		}
   177  
   178  		if k == "require_new" {
   179  			attrDiff.RequiresNew = true
   180  		}
   181  		if _, ok := c.Raw["__"+k+"_requires_new"]; ok {
   182  			attrDiff.RequiresNew = true
   183  		}
   184  		diff.Attributes[k] = attrDiff
   185  	}
   186  
   187  	for _, k := range c.ComputedKeys {
   188  		diff.Attributes[k] = &ResourceAttrDiff{
   189  			Old:         "",
   190  			NewComputed: true,
   191  		}
   192  	}
   193  
   194  	// If we recreate this resource because it's tainted, we keep all attrs
   195  	if !diff.RequiresNew() {
   196  		for k, v := range diff.Attributes {
   197  			if v.NewComputed {
   198  				continue
   199  			}
   200  
   201  			old, ok := s.Attributes[k]
   202  			if !ok {
   203  				continue
   204  			}
   205  
   206  			if old == v.New {
   207  				delete(diff.Attributes, k)
   208  			}
   209  		}
   210  	}
   211  
   212  	if !diff.Empty() {
   213  		diff.Attributes["type"] = &ResourceAttrDiff{
   214  			Old: "",
   215  			New: info.Type,
   216  		}
   217  	}
   218  
   219  	return diff, nil
   220  }
   221  
   222  func testProvider(prefix string) *MockResourceProvider {
   223  	p := new(MockResourceProvider)
   224  	p.RefreshFn = func(info *InstanceInfo, s *InstanceState) (*InstanceState, error) {
   225  		return s, nil
   226  	}
   227  	p.ResourcesReturn = []ResourceType{
   228  		ResourceType{
   229  			Name: fmt.Sprintf("%s_instance", prefix),
   230  		},
   231  	}
   232  
   233  	return p
   234  }
   235  
   236  func testProvisioner() *MockResourceProvisioner {
   237  	p := new(MockResourceProvisioner)
   238  	return p
   239  }
   240  
   241  func checkStateString(t *testing.T, state *State, expected string) {
   242  	actual := strings.TrimSpace(state.String())
   243  	expected = strings.TrimSpace(expected)
   244  
   245  	if actual != expected {
   246  		t.Fatalf("state does not match! actual:\n%s\n\nexpected:\n%s", actual, expected)
   247  	}
   248  }
   249  
   250  func resourceState(resourceType, resourceID string) *ResourceState {
   251  	return &ResourceState{
   252  		Type: resourceType,
   253  		Primary: &InstanceState{
   254  			ID: resourceID,
   255  		},
   256  	}
   257  }
   258  
   259  // Test helper that gives a function 3 seconds to finish, assumes deadlock and
   260  // fails test if it does not.
   261  func testCheckDeadlock(t *testing.T, f func()) {
   262  	timeout := make(chan bool, 1)
   263  	done := make(chan bool, 1)
   264  	go func() {
   265  		time.Sleep(3 * time.Second)
   266  		timeout <- true
   267  	}()
   268  	go func(f func(), done chan bool) {
   269  		defer func() { done <- true }()
   270  		f()
   271  	}(f, done)
   272  	select {
   273  	case <-timeout:
   274  		t.Fatalf("timed out! probably deadlock")
   275  	case <-done:
   276  		// ok
   277  	}
   278  }
   279  
   280  const testContextGraph = `
   281  root: root
   282  aws_instance.bar
   283    aws_instance.bar -> provider.aws
   284  aws_instance.foo
   285    aws_instance.foo -> provider.aws
   286  provider.aws
   287  root
   288    root -> aws_instance.bar
   289    root -> aws_instance.foo
   290  `
   291  
   292  const testContextRefreshModuleStr = `
   293  aws_instance.web: (tainted)
   294    ID = bar
   295  
   296  module.child:
   297    aws_instance.web:
   298      ID = new
   299  `
   300  
   301  const testContextRefreshOutputStr = `
   302  aws_instance.web:
   303    ID = foo
   304    foo = bar
   305  
   306  Outputs:
   307  
   308  foo = bar
   309  `
   310  
   311  const testContextRefreshOutputPartialStr = `
   312  <no state>
   313  `
   314  
   315  const testContextRefreshTaintedStr = `
   316  aws_instance.web: (tainted)
   317    ID = foo
   318  `