github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/tflint/terraform_test.go (about) 1 package tflint 2 3 import ( 4 "os" 5 "path/filepath" 6 "reflect" 7 "testing" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/google/go-cmp/cmp/cmpopts" 11 hcl "github.com/hashicorp/hcl/v2" 12 "github.com/hashicorp/hcl/v2/hclsyntax" 13 "github.com/hashicorp/hcl/v2/json" 14 "github.com/hashicorp/terraform/configs" 15 "github.com/hashicorp/terraform/terraform" 16 "github.com/zclconf/go-cty/cty" 17 ) 18 19 func Test_ParseTFVariables(t *testing.T) { 20 cases := []struct { 21 Name string 22 DeclVars map[string]*configs.Variable 23 Vars []string 24 Expected terraform.InputValues 25 }{ 26 { 27 Name: "undeclared", 28 DeclVars: map[string]*configs.Variable{}, 29 Vars: []string{ 30 "foo=bar", 31 "bar=[\"foo\"]", 32 "baz={ foo=\"bar\" }", 33 }, 34 Expected: terraform.InputValues{ 35 "foo": &terraform.InputValue{ 36 Value: cty.StringVal("bar"), 37 SourceType: terraform.ValueFromCLIArg, 38 }, 39 "bar": &terraform.InputValue{ 40 Value: cty.StringVal("[\"foo\"]"), 41 SourceType: terraform.ValueFromCLIArg, 42 }, 43 "baz": &terraform.InputValue{ 44 Value: cty.StringVal("{ foo=\"bar\" }"), 45 SourceType: terraform.ValueFromCLIArg, 46 }, 47 }, 48 }, 49 { 50 Name: "declared", 51 DeclVars: map[string]*configs.Variable{ 52 "foo": &configs.Variable{ParsingMode: configs.VariableParseLiteral}, 53 "bar": &configs.Variable{ParsingMode: configs.VariableParseHCL}, 54 "baz": &configs.Variable{ParsingMode: configs.VariableParseHCL}, 55 }, 56 Vars: []string{ 57 "foo=bar", 58 "bar=[\"foo\"]", 59 "baz={ foo=\"bar\" }", 60 }, 61 Expected: terraform.InputValues{ 62 "foo": &terraform.InputValue{ 63 Value: cty.StringVal("bar"), 64 SourceType: terraform.ValueFromCLIArg, 65 }, 66 "bar": &terraform.InputValue{ 67 Value: cty.TupleVal([]cty.Value{cty.StringVal("foo")}), 68 SourceType: terraform.ValueFromCLIArg, 69 }, 70 "baz": &terraform.InputValue{ 71 Value: cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), 72 SourceType: terraform.ValueFromCLIArg, 73 }, 74 }, 75 }, 76 } 77 78 for _, tc := range cases { 79 ret, err := ParseTFVariables(tc.Vars, tc.DeclVars) 80 if err != nil { 81 t.Fatalf("Failed `%s` test: Unexpected error occurred: %s", tc.Name, err) 82 } 83 84 if !reflect.DeepEqual(tc.Expected, ret) { 85 t.Fatalf("Failed `%s` test:\n Expected: %#v\n Actual: %#v", tc.Name, tc.Expected, ret) 86 } 87 } 88 } 89 90 func Test_ParseTFVariables_errors(t *testing.T) { 91 cases := []struct { 92 Name string 93 DeclVars map[string]*configs.Variable 94 Vars []string 95 Expected string 96 }{ 97 { 98 Name: "invalid format", 99 DeclVars: map[string]*configs.Variable{}, 100 Vars: []string{"foo"}, 101 Expected: "`foo` is invalid. Variables must be `key=value` format", 102 }, 103 { 104 Name: "invalid parsing mode", 105 DeclVars: map[string]*configs.Variable{ 106 "foo": &configs.Variable{ParsingMode: configs.VariableParseHCL}, 107 }, 108 Vars: []string{"foo=bar"}, 109 Expected: "<value for var.foo>:1,1-4: Variables not allowed; Variables may not be used here.", 110 }, 111 { 112 Name: "invalid expression", 113 DeclVars: map[string]*configs.Variable{ 114 "foo": &configs.Variable{ParsingMode: configs.VariableParseHCL}, 115 }, 116 Vars: []string{"foo="}, 117 Expected: "<value for var.foo>:1,1-1: Invalid expression; Expected the start of an expression, but found an invalid expression token.", 118 }, 119 } 120 121 for _, tc := range cases { 122 _, err := ParseTFVariables(tc.Vars, tc.DeclVars) 123 if err == nil { 124 t.Fatalf("Failed `%s` test: Expected an error, but nothing occurred", tc.Name) 125 } 126 127 if err.Error() != tc.Expected { 128 t.Fatalf("Failed `%s` test: Expected `%s`, but got `%s`", tc.Name, tc.Expected, err.Error()) 129 } 130 } 131 } 132 133 func Test_HCLBodyRange_HCL(t *testing.T) { 134 src := ` 135 ebs_block_device { 136 device_name = "/dev/sdf" 137 volume_size = 10 138 foo { 139 bar = "baz" 140 } 141 } 142 ` 143 144 file, diags := hclsyntax.ParseConfig([]byte(src), "example.tf", hcl.InitialPos) 145 if diags.HasErrors() { 146 t.Fatal(diags) 147 } 148 body, diags := file.Body.Content(&hcl.BodySchema{ 149 Blocks: []hcl.BlockHeaderSchema{ 150 { 151 Type: "ebs_block_device", 152 }, 153 }, 154 }) 155 if diags.HasErrors() { 156 t.Fatal(diags) 157 } 158 block := body.Blocks[0] 159 160 got := HCLBodyRange(block.Body, block.DefRange) 161 expected := hcl.Range{ 162 Filename: "example.tf", 163 Start: hcl.Pos{Line: 3, Column: 3}, 164 End: hcl.Pos{Line: 7, Column: 4}, 165 } 166 167 opt := cmpopts.IgnoreFields(hcl.Pos{}, "Byte") 168 if !cmp.Equal(got, expected, opt) { 169 t.Fatalf("Diff=%s", cmp.Diff(got, expected, opt)) 170 } 171 } 172 173 func Test_HCLBodyRange_JSON(t *testing.T) { 174 src := ` 175 { 176 "ebs_block_device": { 177 "device_name": "/dev/sdf", 178 "volume_size": 10, 179 "foo": { 180 "bar": "baz" 181 } 182 } 183 } 184 ` 185 186 file, diags := json.Parse([]byte(src), "example.tf.json") 187 if diags.HasErrors() { 188 t.Fatal(diags) 189 } 190 body, diags := file.Body.Content(&hcl.BodySchema{ 191 Blocks: []hcl.BlockHeaderSchema{ 192 { 193 Type: "ebs_block_device", 194 }, 195 }, 196 }) 197 if diags.HasErrors() { 198 t.Fatal(diags) 199 } 200 block := body.Blocks[0] 201 202 got := HCLBodyRange(block.Body, block.DefRange) 203 expected := hcl.Range{ 204 Filename: "example.tf.json", 205 Start: hcl.Pos{Line: 3, Column: 23}, 206 End: hcl.Pos{Line: 9, Column: 4}, 207 } 208 209 opt := cmpopts.IgnoreFields(hcl.Pos{}, "Byte") 210 if !cmp.Equal(got, expected, opt) { 211 t.Fatalf("Diff=%s", cmp.Diff(got, expected, opt)) 212 } 213 } 214 215 func Test_getTFDataDir(t *testing.T) { 216 cases := []struct { 217 Name string 218 EnvVar map[string]string 219 Expected string 220 }{ 221 { 222 Name: "default", 223 Expected: ".terraform", 224 }, 225 { 226 Name: "environment variable", 227 EnvVar: map[string]string{"TF_DATA_DIR": ".tfdata"}, 228 Expected: ".tfdata", 229 }, 230 } 231 232 for _, tc := range cases { 233 for key, value := range tc.EnvVar { 234 err := os.Setenv(key, value) 235 if err != nil { 236 t.Fatal(err) 237 } 238 } 239 240 ret := getTFDataDir() 241 if ret != tc.Expected { 242 t.Fatalf("Failed `%s` test: expected value is %s, but get %s", tc.Name, tc.Expected, ret) 243 } 244 245 for key := range tc.EnvVar { 246 err := os.Unsetenv(key) 247 if err != nil { 248 t.Fatal(err) 249 } 250 } 251 } 252 } 253 254 func Test_getTFModuleDir(t *testing.T) { 255 cases := []struct { 256 Name string 257 EnvVar map[string]string 258 Expected string 259 }{ 260 { 261 Name: "default", 262 Expected: filepath.Join(".terraform", "modules"), 263 }, 264 { 265 Name: "environment variable", 266 EnvVar: map[string]string{"TF_DATA_DIR": ".tfdata"}, 267 Expected: filepath.Join(".tfdata", "modules"), 268 }, 269 } 270 271 for _, tc := range cases { 272 for key, value := range tc.EnvVar { 273 err := os.Setenv(key, value) 274 if err != nil { 275 t.Fatal(err) 276 } 277 } 278 279 ret := getTFModuleDir() 280 if ret != tc.Expected { 281 t.Fatalf("Failed `%s` test: expected value is %s, but get %s", tc.Name, tc.Expected, ret) 282 } 283 284 for key := range tc.EnvVar { 285 err := os.Unsetenv(key) 286 if err != nil { 287 t.Fatal(err) 288 } 289 } 290 } 291 } 292 293 func Test_getTFModuleManifestPath(t *testing.T) { 294 cases := []struct { 295 Name string 296 EnvVar map[string]string 297 Expected string 298 }{ 299 { 300 Name: "default", 301 Expected: filepath.Join(".terraform", "modules", "modules.json"), 302 }, 303 { 304 Name: "environment variable", 305 EnvVar: map[string]string{"TF_DATA_DIR": ".tfdata"}, 306 Expected: filepath.Join(".tfdata", "modules", "modules.json"), 307 }, 308 } 309 310 for _, tc := range cases { 311 for key, value := range tc.EnvVar { 312 err := os.Setenv(key, value) 313 if err != nil { 314 t.Fatal(err) 315 } 316 } 317 318 ret := getTFModuleManifestPath() 319 if ret != tc.Expected { 320 t.Fatalf("Failed `%s` test: expected value is %s, but get %s", tc.Name, tc.Expected, ret) 321 } 322 323 for key := range tc.EnvVar { 324 err := os.Unsetenv(key) 325 if err != nil { 326 t.Fatal(err) 327 } 328 } 329 } 330 } 331 332 func Test_getTFWorkspace(t *testing.T) { 333 currentDir, err := os.Getwd() 334 if err != nil { 335 t.Fatal(err) 336 } 337 338 cases := []struct { 339 Name string 340 Dir string 341 EnvVar map[string]string 342 Expected string 343 }{ 344 { 345 Name: "default", 346 Expected: "default", 347 }, 348 { 349 Name: "TF_WORKSPACE", 350 EnvVar: map[string]string{"TF_WORKSPACE": "dev"}, 351 Expected: "dev", 352 }, 353 { 354 Name: "environment file", 355 Dir: filepath.Join(currentDir, "test-fixtures", "with_environment_file"), 356 Expected: "staging", 357 }, 358 { 359 Name: "TF_DATA_DIR", 360 Dir: filepath.Join(currentDir, "test-fixtures", "with_environment_file"), 361 EnvVar: map[string]string{"TF_DATA_DIR": ".terraform_production"}, 362 Expected: "production", 363 }, 364 } 365 366 for _, tc := range cases { 367 if tc.Dir != "" { 368 err := os.Chdir(tc.Dir) 369 if err != nil { 370 t.Fatal(err) 371 } 372 } 373 374 for key, value := range tc.EnvVar { 375 err := os.Setenv(key, value) 376 if err != nil { 377 t.Fatal(err) 378 } 379 } 380 381 ret := getTFWorkspace() 382 if ret != tc.Expected { 383 t.Fatalf("Failed `%s` test: expected value is %s, but get %s", tc.Name, tc.Expected, ret) 384 } 385 386 for key := range tc.EnvVar { 387 err := os.Unsetenv(key) 388 if err != nil { 389 t.Fatal(err) 390 } 391 } 392 393 if tc.Dir != "" { 394 err := os.Chdir(currentDir) 395 if err != nil { 396 t.Fatal(err) 397 } 398 } 399 } 400 } 401 402 func Test_getTFEnvVariables(t *testing.T) { 403 cases := []struct { 404 Name string 405 EnvVar map[string]string 406 Expected terraform.InputValues 407 }{ 408 { 409 Name: "environment variable", 410 EnvVar: map[string]string{ 411 "TF_VAR_instance_type": "t2.micro", 412 "TF_VAR_count": "5", 413 }, 414 Expected: terraform.InputValues{ 415 "instance_type": &terraform.InputValue{ 416 Value: cty.StringVal("t2.micro"), 417 SourceType: terraform.ValueFromEnvVar, 418 }, 419 "count": &terraform.InputValue{ 420 Value: cty.StringVal("5"), 421 SourceType: terraform.ValueFromEnvVar, 422 }, 423 }, 424 }, 425 } 426 427 for _, tc := range cases { 428 for key, value := range tc.EnvVar { 429 err := os.Setenv(key, value) 430 if err != nil { 431 t.Fatal(err) 432 } 433 } 434 435 ret := getTFEnvVariables() 436 if !reflect.DeepEqual(tc.Expected, ret) { 437 t.Fatalf("Failed `%s` test:\n Expected: %#v\n Actual: %#v", tc.Name, tc.Expected, ret) 438 } 439 440 for key := range tc.EnvVar { 441 err := os.Unsetenv(key) 442 if err != nil { 443 t.Fatal(err) 444 } 445 } 446 } 447 }