github.com/chanzuckerberg/terraform@v0.11.12-beta1/config/loader_hcl2_test.go (about) 1 package config 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/zclconf/go-cty/cty" 8 9 gohcl2 "github.com/hashicorp/hcl2/gohcl" 10 hcl2 "github.com/hashicorp/hcl2/hcl" 11 ) 12 13 func TestHCL2ConfigurableConfigurable(t *testing.T) { 14 var _ configurable = new(hcl2Configurable) 15 } 16 17 func TestHCL2Basic(t *testing.T) { 18 loader := globalHCL2Loader 19 cbl, _, err := loader.loadFile("test-fixtures/basic-hcl2.tf") 20 if err != nil { 21 if diags, isDiags := err.(hcl2.Diagnostics); isDiags { 22 for _, diag := range diags { 23 t.Logf("- %s", diag.Error()) 24 } 25 t.Fatalf("unexpected diagnostics in load") 26 } else { 27 t.Fatalf("unexpected error in load: %s", err) 28 } 29 } 30 31 cfg, err := cbl.Config() 32 if err != nil { 33 if diags, isDiags := err.(hcl2.Diagnostics); isDiags { 34 for _, diag := range diags { 35 t.Logf("- %s", diag.Error()) 36 } 37 t.Fatalf("unexpected diagnostics in decode") 38 } else { 39 t.Fatalf("unexpected error in decode: %s", err) 40 } 41 } 42 43 // Unfortunately the config structure isn't DeepEqual-friendly because 44 // of all the nested RawConfig, etc structures, so we'll need to 45 // hand-assert each item. 46 47 // The "terraform" block 48 if cfg.Terraform == nil { 49 t.Fatalf("Terraform field is nil") 50 } 51 if got, want := cfg.Terraform.RequiredVersion, "foo"; got != want { 52 t.Errorf("wrong Terraform.RequiredVersion %q; want %q", got, want) 53 } 54 if cfg.Terraform.Backend == nil { 55 t.Fatalf("Terraform.Backend is nil") 56 } 57 if got, want := cfg.Terraform.Backend.Type, "baz"; got != want { 58 t.Errorf("wrong Terraform.Backend.Type %q; want %q", got, want) 59 } 60 if got, want := cfg.Terraform.Backend.RawConfig.Raw, map[string]interface{}{"something": "nothing"}; !reflect.DeepEqual(got, want) { 61 t.Errorf("wrong Terraform.Backend.RawConfig.Raw %#v; want %#v", got, want) 62 } 63 64 // The "atlas" block 65 if cfg.Atlas == nil { 66 t.Fatalf("Atlas field is nil") 67 } 68 if got, want := cfg.Atlas.Name, "example/foo"; got != want { 69 t.Errorf("wrong Atlas.Name %q; want %q", got, want) 70 } 71 72 // "module" blocks 73 if got, want := len(cfg.Modules), 1; got != want { 74 t.Errorf("Modules slice has wrong length %#v; want %#v", got, want) 75 } else { 76 m := cfg.Modules[0] 77 if got, want := m.Name, "child"; got != want { 78 t.Errorf("wrong Modules[0].Name %#v; want %#v", got, want) 79 } 80 if got, want := m.Source, "./baz"; got != want { 81 t.Errorf("wrong Modules[0].Source %#v; want %#v", got, want) 82 } 83 want := map[string]string{"toasty": "true"} 84 var got map[string]string 85 gohcl2.DecodeBody(m.RawConfig.Body, nil, &got) 86 if !reflect.DeepEqual(got, want) { 87 t.Errorf("wrong Modules[0].RawConfig.Body %#v; want %#v", got, want) 88 } 89 } 90 91 // "resource" blocks 92 if got, want := len(cfg.Resources), 5; got != want { 93 t.Errorf("Resources slice has wrong length %#v; want %#v", got, want) 94 } else { 95 { 96 r := cfg.Resources[0] 97 98 if got, want := r.Id(), "aws_security_group.firewall"; got != want { 99 t.Errorf("wrong Resources[0].Id() %#v; want %#v", got, want) 100 } 101 102 wantConfig := map[string]string{} 103 var gotConfig map[string]string 104 gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig) 105 if !reflect.DeepEqual(gotConfig, wantConfig) { 106 t.Errorf("wrong Resources[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 107 } 108 109 wantCount := map[string]string{"count": "5"} 110 var gotCount map[string]string 111 gohcl2.DecodeBody(r.RawCount.Body, nil, &gotCount) 112 if !reflect.DeepEqual(gotCount, wantCount) { 113 t.Errorf("wrong Resources[0].RawCount.Body %#v; want %#v", gotCount, wantCount) 114 } 115 if got, want := r.RawCount.Key, "count"; got != want { 116 t.Errorf("wrong Resources[0].RawCount.Key %#v; want %#v", got, want) 117 } 118 119 if got, want := len(r.Provisioners), 0; got != want { 120 t.Errorf("wrong Resources[0].Provisioners length %#v; want %#v", got, want) 121 } 122 if got, want := len(r.DependsOn), 0; got != want { 123 t.Errorf("wrong Resources[0].DependsOn length %#v; want %#v", got, want) 124 } 125 if got, want := r.Provider, "another"; got != want { 126 t.Errorf("wrong Resources[0].Provider %#v; want %#v", got, want) 127 } 128 if got, want := r.Lifecycle, (ResourceLifecycle{}); !reflect.DeepEqual(got, want) { 129 t.Errorf("wrong Resources[0].Lifecycle %#v; want %#v", got, want) 130 } 131 } 132 { 133 r := cfg.Resources[1] 134 135 if got, want := r.Id(), "aws_instance.web"; got != want { 136 t.Errorf("wrong Resources[1].Id() %#v; want %#v", got, want) 137 } 138 if got, want := r.Provider, ""; got != want { 139 t.Errorf("wrong Resources[1].Provider %#v; want %#v", got, want) 140 } 141 142 if got, want := len(r.Provisioners), 1; got != want { 143 t.Errorf("wrong Resources[1].Provisioners length %#v; want %#v", got, want) 144 } else { 145 p := r.Provisioners[0] 146 147 if got, want := p.Type, "file"; got != want { 148 t.Errorf("wrong Resources[1].Provisioners[0].Type %#v; want %#v", got, want) 149 } 150 151 wantConfig := map[string]string{ 152 "source": "foo", 153 "destination": "bar", 154 } 155 var gotConfig map[string]string 156 gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig) 157 if !reflect.DeepEqual(gotConfig, wantConfig) { 158 t.Errorf("wrong Resources[1].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 159 } 160 161 wantConn := map[string]string{ 162 "default": "true", 163 } 164 var gotConn map[string]string 165 gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn) 166 if !reflect.DeepEqual(gotConn, wantConn) { 167 t.Errorf("wrong Resources[1].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn) 168 } 169 } 170 171 // We'll use these throwaway structs to more easily decode and 172 // compare the main config body. 173 type instanceNetworkInterface struct { 174 DeviceIndex int `hcl:"device_index"` 175 Description string `hcl:"description"` 176 } 177 type instanceConfig struct { 178 AMI string `hcl:"ami"` 179 SecurityGroups []string `hcl:"security_groups"` 180 NetworkInterface instanceNetworkInterface `hcl:"network_interface,block"` 181 } 182 var gotConfig instanceConfig 183 wantConfig := instanceConfig{ 184 AMI: "ami-abc123", 185 SecurityGroups: []string{"foo", "sg-firewall"}, 186 NetworkInterface: instanceNetworkInterface{ 187 DeviceIndex: 0, 188 Description: "Main network interface", 189 }, 190 } 191 ctx := &hcl2.EvalContext{ 192 Variables: map[string]cty.Value{ 193 "var": cty.ObjectVal(map[string]cty.Value{ 194 "foo": cty.StringVal("ami-abc123"), 195 }), 196 "aws_security_group": cty.ObjectVal(map[string]cty.Value{ 197 "firewall": cty.ObjectVal(map[string]cty.Value{ 198 "foo": cty.StringVal("sg-firewall"), 199 }), 200 }), 201 }, 202 } 203 diags := gohcl2.DecodeBody(r.RawConfig.Body, ctx, &gotConfig) 204 if len(diags) != 0 { 205 t.Errorf("unexpected diagnostics decoding Resources[1].RawConfig.Body") 206 for _, diag := range diags { 207 t.Logf("- %s", diag.Error()) 208 } 209 } 210 if !reflect.DeepEqual(gotConfig, wantConfig) { 211 t.Errorf("wrong Resources[1].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 212 } 213 214 } 215 { 216 r := cfg.Resources[2] 217 218 if got, want := r.Id(), "aws_instance.db"; got != want { 219 t.Errorf("wrong Resources[2].Id() %#v; want %#v", got, want) 220 } 221 if got, want := r.DependsOn, []string{"aws_instance.web"}; !reflect.DeepEqual(got, want) { 222 t.Errorf("wrong Resources[2].DependsOn %#v; want %#v", got, want) 223 } 224 225 if got, want := len(r.Provisioners), 1; got != want { 226 t.Errorf("wrong Resources[2].Provisioners length %#v; want %#v", got, want) 227 } else { 228 p := r.Provisioners[0] 229 230 if got, want := p.Type, "file"; got != want { 231 t.Errorf("wrong Resources[2].Provisioners[0].Type %#v; want %#v", got, want) 232 } 233 234 wantConfig := map[string]string{ 235 "source": "here", 236 "destination": "there", 237 } 238 var gotConfig map[string]string 239 gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig) 240 if !reflect.DeepEqual(gotConfig, wantConfig) { 241 t.Errorf("wrong Resources[2].Provisioners[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 242 } 243 244 wantConn := map[string]string{ 245 "default": "false", 246 } 247 var gotConn map[string]string 248 gohcl2.DecodeBody(p.ConnInfo.Body, nil, &gotConn) 249 if !reflect.DeepEqual(gotConn, wantConn) { 250 t.Errorf("wrong Resources[2].Provisioners[0].ConnInfo.Body %#v; want %#v", gotConn, wantConn) 251 } 252 } 253 } 254 { 255 r := cfg.Resources[3] 256 257 if got, want := r.Id(), "data.do.simple"; got != want { 258 t.Errorf("wrong Resources[3].Id() %#v; want %#v", got, want) 259 } 260 if got, want := r.DependsOn, []string(nil); !reflect.DeepEqual(got, want) { 261 t.Errorf("wrong Resources[3].DependsOn %#v; want %#v", got, want) 262 } 263 if got, want := r.Provider, "do.foo"; got != want { 264 t.Errorf("wrong Resources[3].Provider %#v; want %#v", got, want) 265 } 266 267 wantConfig := map[string]string{ 268 "foo": "baz", 269 } 270 var gotConfig map[string]string 271 gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig) 272 if !reflect.DeepEqual(gotConfig, wantConfig) { 273 t.Errorf("wrong Resources[3].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 274 } 275 } 276 { 277 r := cfg.Resources[4] 278 279 if got, want := r.Id(), "data.do.depends"; got != want { 280 t.Errorf("wrong Resources[4].Id() %#v; want %#v", got, want) 281 } 282 if got, want := r.DependsOn, []string{"data.do.simple"}; !reflect.DeepEqual(got, want) { 283 t.Errorf("wrong Resources[4].DependsOn %#v; want %#v", got, want) 284 } 285 if got, want := r.Provider, ""; got != want { 286 t.Errorf("wrong Resources[4].Provider %#v; want %#v", got, want) 287 } 288 289 wantConfig := map[string]string{} 290 var gotConfig map[string]string 291 gohcl2.DecodeBody(r.RawConfig.Body, nil, &gotConfig) 292 if !reflect.DeepEqual(gotConfig, wantConfig) { 293 t.Errorf("wrong Resources[4].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 294 } 295 } 296 } 297 298 // "variable" blocks 299 if got, want := len(cfg.Variables), 3; got != want { 300 t.Errorf("Variables slice has wrong length %#v; want %#v", got, want) 301 } else { 302 { 303 v := cfg.Variables[0] 304 305 if got, want := v.Name, "foo"; got != want { 306 t.Errorf("wrong Variables[0].Name %#v; want %#v", got, want) 307 } 308 if got, want := v.Default, "bar"; got != want { 309 t.Errorf("wrong Variables[0].Default %#v; want %#v", got, want) 310 } 311 if got, want := v.Description, "barbar"; got != want { 312 t.Errorf("wrong Variables[0].Description %#v; want %#v", got, want) 313 } 314 if got, want := v.DeclaredType, ""; got != want { 315 t.Errorf("wrong Variables[0].DeclaredType %#v; want %#v", got, want) 316 } 317 } 318 { 319 v := cfg.Variables[1] 320 321 if got, want := v.Name, "bar"; got != want { 322 t.Errorf("wrong Variables[1].Name %#v; want %#v", got, want) 323 } 324 if got, want := v.Default, interface{}(nil); got != want { 325 t.Errorf("wrong Variables[1].Default %#v; want %#v", got, want) 326 } 327 if got, want := v.Description, ""; got != want { 328 t.Errorf("wrong Variables[1].Description %#v; want %#v", got, want) 329 } 330 if got, want := v.DeclaredType, "string"; got != want { 331 t.Errorf("wrong Variables[1].DeclaredType %#v; want %#v", got, want) 332 } 333 } 334 { 335 v := cfg.Variables[2] 336 337 if got, want := v.Name, "baz"; got != want { 338 t.Errorf("wrong Variables[2].Name %#v; want %#v", got, want) 339 } 340 if got, want := v.Default, map[string]interface{}{"key": "value"}; !reflect.DeepEqual(got, want) { 341 t.Errorf("wrong Variables[2].Default %#v; want %#v", got, want) 342 } 343 if got, want := v.Description, ""; got != want { 344 t.Errorf("wrong Variables[2].Description %#v; want %#v", got, want) 345 } 346 if got, want := v.DeclaredType, "map"; got != want { 347 t.Errorf("wrong Variables[2].DeclaredType %#v; want %#v", got, want) 348 } 349 } 350 } 351 352 // "output" blocks 353 if got, want := len(cfg.Outputs), 2; got != want { 354 t.Errorf("Outputs slice has wrong length %#v; want %#v", got, want) 355 } else { 356 { 357 o := cfg.Outputs[0] 358 359 if got, want := o.Name, "web_ip"; got != want { 360 t.Errorf("wrong Outputs[0].Name %#v; want %#v", got, want) 361 } 362 if got, want := o.DependsOn, []string(nil); !reflect.DeepEqual(got, want) { 363 t.Errorf("wrong Outputs[0].DependsOn %#v; want %#v", got, want) 364 } 365 if got, want := o.Description, ""; got != want { 366 t.Errorf("wrong Outputs[0].Description %#v; want %#v", got, want) 367 } 368 if got, want := o.Sensitive, true; got != want { 369 t.Errorf("wrong Outputs[0].Sensitive %#v; want %#v", got, want) 370 } 371 372 wantConfig := map[string]string{ 373 "value": "312.213.645.123", 374 } 375 var gotConfig map[string]string 376 ctx := &hcl2.EvalContext{ 377 Variables: map[string]cty.Value{ 378 "aws_instance": cty.ObjectVal(map[string]cty.Value{ 379 "web": cty.ObjectVal(map[string]cty.Value{ 380 "private_ip": cty.StringVal("312.213.645.123"), 381 }), 382 }), 383 }, 384 } 385 gohcl2.DecodeBody(o.RawConfig.Body, ctx, &gotConfig) 386 if !reflect.DeepEqual(gotConfig, wantConfig) { 387 t.Errorf("wrong Outputs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 388 } 389 } 390 { 391 o := cfg.Outputs[1] 392 393 if got, want := o.Name, "web_id"; got != want { 394 t.Errorf("wrong Outputs[1].Name %#v; want %#v", got, want) 395 } 396 if got, want := o.DependsOn, []string{"aws_instance.db"}; !reflect.DeepEqual(got, want) { 397 t.Errorf("wrong Outputs[1].DependsOn %#v; want %#v", got, want) 398 } 399 if got, want := o.Description, "The ID"; got != want { 400 t.Errorf("wrong Outputs[1].Description %#v; want %#v", got, want) 401 } 402 if got, want := o.Sensitive, false; got != want { 403 t.Errorf("wrong Outputs[1].Sensitive %#v; want %#v", got, want) 404 } 405 } 406 } 407 408 // "provider" blocks 409 if got, want := len(cfg.ProviderConfigs), 2; got != want { 410 t.Errorf("ProviderConfigs slice has wrong length %#v; want %#v", got, want) 411 } else { 412 { 413 p := cfg.ProviderConfigs[0] 414 415 if got, want := p.Name, "aws"; got != want { 416 t.Errorf("wrong ProviderConfigs[0].Name %#v; want %#v", got, want) 417 } 418 if got, want := p.Alias, ""; got != want { 419 t.Errorf("wrong ProviderConfigs[0].Alias %#v; want %#v", got, want) 420 } 421 if got, want := p.Version, "1.0.0"; got != want { 422 t.Errorf("wrong ProviderConfigs[0].Version %#v; want %#v", got, want) 423 } 424 425 wantConfig := map[string]string{ 426 "access_key": "foo", 427 "secret_key": "bar", 428 } 429 var gotConfig map[string]string 430 gohcl2.DecodeBody(p.RawConfig.Body, nil, &gotConfig) 431 if !reflect.DeepEqual(gotConfig, wantConfig) { 432 t.Errorf("wrong ProviderConfigs[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 433 } 434 435 } 436 { 437 p := cfg.ProviderConfigs[1] 438 439 if got, want := p.Name, "do"; got != want { 440 t.Errorf("wrong ProviderConfigs[1].Name %#v; want %#v", got, want) 441 } 442 if got, want := p.Alias, "fum"; got != want { 443 t.Errorf("wrong ProviderConfigs[1].Alias %#v; want %#v", got, want) 444 } 445 if got, want := p.Version, ""; got != want { 446 t.Errorf("wrong ProviderConfigs[1].Version %#v; want %#v", got, want) 447 } 448 449 } 450 } 451 452 // "locals" definitions 453 if got, want := len(cfg.Locals), 5; got != want { 454 t.Errorf("Locals slice has wrong length %#v; want %#v", got, want) 455 } else { 456 { 457 l := cfg.Locals[0] 458 459 if got, want := l.Name, "security_group_ids"; got != want { 460 t.Errorf("wrong Locals[0].Name %#v; want %#v", got, want) 461 } 462 463 wantConfig := map[string][]string{ 464 "value": []string{"sg-abc123"}, 465 } 466 var gotConfig map[string][]string 467 ctx := &hcl2.EvalContext{ 468 Variables: map[string]cty.Value{ 469 "aws_security_group": cty.ObjectVal(map[string]cty.Value{ 470 "firewall": cty.ObjectVal(map[string]cty.Value{ 471 "id": cty.StringVal("sg-abc123"), 472 }), 473 }), 474 }, 475 } 476 gohcl2.DecodeBody(l.RawConfig.Body, ctx, &gotConfig) 477 if !reflect.DeepEqual(gotConfig, wantConfig) { 478 t.Errorf("wrong Locals[0].RawConfig.Body %#v; want %#v", gotConfig, wantConfig) 479 } 480 } 481 { 482 l := cfg.Locals[1] 483 484 if got, want := l.Name, "web_ip"; got != want { 485 t.Errorf("wrong Locals[1].Name %#v; want %#v", got, want) 486 } 487 } 488 { 489 l := cfg.Locals[2] 490 491 if got, want := l.Name, "literal"; got != want { 492 t.Errorf("wrong Locals[2].Name %#v; want %#v", got, want) 493 } 494 } 495 { 496 l := cfg.Locals[3] 497 498 if got, want := l.Name, "literal_list"; got != want { 499 t.Errorf("wrong Locals[3].Name %#v; want %#v", got, want) 500 } 501 } 502 { 503 l := cfg.Locals[4] 504 505 if got, want := l.Name, "literal_map"; got != want { 506 t.Errorf("wrong Locals[4].Name %#v; want %#v", got, want) 507 } 508 } 509 } 510 }