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 `