terraform/internal/cloud/cloud_variables_test.go
Martin Atkins 88cb4aeea3 backendrun: Separate the types/etc for backends that support operations
We previously had all of the types and helpers for all kinds of backends
together in package backend. That kept things relatively simple, but it
also meant that the majority of backends that only deal with remote state
storage ended up still indirectly depending on the entire Terraform modules
runtime, configuration loader, etc, etc, which brings into scope a bunch
of external dependencies that the remote state backends don't really need.

Since backends that support operations are a rare exception, we'll move the
types and helpers for those into a separate package "backendrun", and
then the main package backend can have a much more modest set of types and,
more importantly, a modest set of dependencies on other packages in this
codebase.

This is part of an ongoing effort to reduce the exposure of Terraform Core
and CLI code to the remote backends and vice-versa, so that in the long
run we can more often treat them as separate for dependency maintenance
purposes.
2024-03-12 08:44:17 -07:00

187 lines
7.2 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package cloud
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/backend/backendrun"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/terraform"
"github.com/hashicorp/terraform/internal/tfdiags"
)
func TestParseCloudRunVariables(t *testing.T) {
t.Run("populates variables from allowed sources", func(t *testing.T) {
vv := map[string]backendrun.UnparsedVariableValue{
"undeclared": testUnparsedVariableValue{source: terraform.ValueFromCLIArg, value: cty.StringVal("0")},
"declaredFromConfig": testUnparsedVariableValue{source: terraform.ValueFromConfig, value: cty.StringVal("1")},
"declaredFromNamedFileMapString": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("bar")})},
"declaredFromNamedFileBool": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.BoolVal(true)},
"declaredFromNamedFileNumber": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.NumberIntVal(2)},
"declaredFromNamedFileListString": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.ListVal([]cty.Value{cty.StringVal("2a"), cty.StringVal("2b")})},
"declaredFromNamedFileNull": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.NullVal(cty.String)},
"declaredFromNamedMapComplex": testUnparsedVariableValue{source: terraform.ValueFromNamedFile, value: cty.MapVal(map[string]cty.Value{"foo": cty.ObjectVal(map[string]cty.Value{"qux": cty.ListVal([]cty.Value{cty.BoolVal(true), cty.BoolVal(false)})})})},
"declaredFromCLIArg": testUnparsedVariableValue{source: terraform.ValueFromCLIArg, value: cty.StringVal("3")},
"declaredFromEnvVar": testUnparsedVariableValue{source: terraform.ValueFromEnvVar, value: cty.StringVal("4")},
}
decls := map[string]*configs.Variable{
"declaredFromConfig": {
Name: "declaredFromConfig",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedFileMapString": {
Name: "declaredFromNamedFileMapString",
Type: cty.Map(cty.String),
ConstraintType: cty.Map(cty.String),
ParsingMode: configs.VariableParseHCL,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedFileBool": {
Name: "declaredFromNamedFileBool",
Type: cty.Bool,
ConstraintType: cty.Bool,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedFileNumber": {
Name: "declaredFromNamedFileNumber",
Type: cty.Number,
ConstraintType: cty.Number,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedFileListString": {
Name: "declaredFromNamedFileListString",
Type: cty.List(cty.String),
ConstraintType: cty.List(cty.String),
ParsingMode: configs.VariableParseHCL,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedFileNull": {
Name: "declaredFromNamedFileNull",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseHCL,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromNamedMapComplex": {
Name: "declaredFromNamedMapComplex",
Type: cty.DynamicPseudoType,
ConstraintType: cty.DynamicPseudoType,
ParsingMode: configs.VariableParseHCL,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromCLIArg": {
Name: "declaredFromCLIArg",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"declaredFromEnvVar": {
Name: "declaredFromEnvVar",
Type: cty.String,
ConstraintType: cty.String,
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
"missing": {
Name: "missing",
Type: cty.String,
ConstraintType: cty.String,
Default: cty.StringVal("2"),
ParsingMode: configs.VariableParseLiteral,
DeclRange: hcl.Range{
Filename: "fake.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 0},
End: hcl.Pos{Line: 2, Column: 1, Byte: 0},
},
},
}
wantVals := make(map[string]string)
wantVals["declaredFromNamedFileBool"] = "true"
wantVals["declaredFromNamedFileNumber"] = "2"
wantVals["declaredFromNamedFileListString"] = `["2a", "2b"]`
wantVals["declaredFromNamedFileNull"] = "null"
wantVals["declaredFromNamedFileMapString"] = "{\n foo = \"bar\"\n}"
wantVals["declaredFromNamedMapComplex"] = "{\n foo = {\n qux = [true, false]\n }\n}"
wantVals["declaredFromCLIArg"] = `"3"`
wantVals["declaredFromEnvVar"] = `"4"`
gotVals, diags := ParseCloudRunVariables(vv, decls)
if diff := cmp.Diff(wantVals, gotVals, cmp.Comparer(cty.Value.RawEquals)); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
if got, want := len(diags), 1; got != want {
t.Fatalf("expected 1 variable error: %v, got %v", diags.Err(), want)
}
if got, want := diags[0].Description().Summary, "Value for undeclared variable"; got != want {
t.Errorf("wrong summary for diagnostic 0\ngot: %s\nwant: %s", got, want)
}
})
}
type testUnparsedVariableValue struct {
source terraform.ValueSourceType
value cty.Value
}
func (v testUnparsedVariableValue) ParseVariableValue(mode configs.VariableParsingMode) (*terraform.InputValue, tfdiags.Diagnostics) {
return &terraform.InputValue{
Value: v.value,
SourceType: v.source,
SourceRange: tfdiags.SourceRange{
Filename: "fake.tfvars",
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
},
}, nil
}