github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/backend/remote/backend_context_test.go (about) 1 package remote 2 3 import ( 4 "context" 5 "testing" 6 7 tfe "github.com/hashicorp/go-tfe" 8 "github.com/hashicorp/terraform/backend" 9 "github.com/hashicorp/terraform/configs" 10 "github.com/hashicorp/terraform/internal/initwd" 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 func TestRemoteStoredVariableValue(t *testing.T) { 15 tests := map[string]struct { 16 Def *tfe.Variable 17 Want cty.Value 18 WantError string 19 }{ 20 "string literal": { 21 &tfe.Variable{ 22 Key: "test", 23 Value: "foo", 24 HCL: false, 25 Sensitive: false, 26 }, 27 cty.StringVal("foo"), 28 ``, 29 }, 30 "string HCL": { 31 &tfe.Variable{ 32 Key: "test", 33 Value: `"foo"`, 34 HCL: true, 35 Sensitive: false, 36 }, 37 cty.StringVal("foo"), 38 ``, 39 }, 40 "list HCL": { 41 &tfe.Variable{ 42 Key: "test", 43 Value: `[]`, 44 HCL: true, 45 Sensitive: false, 46 }, 47 cty.EmptyTupleVal, 48 ``, 49 }, 50 "null HCL": { 51 &tfe.Variable{ 52 Key: "test", 53 Value: `null`, 54 HCL: true, 55 Sensitive: false, 56 }, 57 cty.NullVal(cty.DynamicPseudoType), 58 ``, 59 }, 60 "literal sensitive": { 61 &tfe.Variable{ 62 Key: "test", 63 HCL: false, 64 Sensitive: true, 65 }, 66 cty.UnknownVal(cty.String), 67 ``, 68 }, 69 "HCL sensitive": { 70 &tfe.Variable{ 71 Key: "test", 72 HCL: true, 73 Sensitive: true, 74 }, 75 cty.DynamicVal, 76 ``, 77 }, 78 "HCL computation": { 79 // This (stored expressions containing computation) is not a case 80 // we intentionally supported, but it became possible for remote 81 // operations in Terraform 0.12 (due to Terraform Cloud/Enterprise 82 // just writing the HCL verbatim into generated `.tfvars` files). 83 // We support it here for consistency, and we continue to support 84 // it in both places for backward-compatibility. In practice, 85 // there's little reason to do computation in a stored variable 86 // value because references are not supported. 87 &tfe.Variable{ 88 Key: "test", 89 Value: `[for v in ["a"] : v]`, 90 HCL: true, 91 Sensitive: false, 92 }, 93 cty.TupleVal([]cty.Value{cty.StringVal("a")}), 94 ``, 95 }, 96 "HCL syntax error": { 97 &tfe.Variable{ 98 Key: "test", 99 Value: `[`, 100 HCL: true, 101 Sensitive: false, 102 }, 103 cty.DynamicVal, 104 `Invalid expression for var.test: The value of variable "test" is marked in the remote workspace as being specified in HCL syntax, but the given value is not valid HCL. Stored variable values must be valid literal expressions and may not contain references to other variables or calls to functions.`, 105 }, 106 "HCL with references": { 107 &tfe.Variable{ 108 Key: "test", 109 Value: `foo.bar`, 110 HCL: true, 111 Sensitive: false, 112 }, 113 cty.DynamicVal, 114 `Invalid expression for var.test: The value of variable "test" is marked in the remote workspace as being specified in HCL syntax, but the given value is not valid HCL. Stored variable values must be valid literal expressions and may not contain references to other variables or calls to functions.`, 115 }, 116 } 117 118 for name, test := range tests { 119 t.Run(name, func(t *testing.T) { 120 v := &remoteStoredVariableValue{ 121 definition: test.Def, 122 } 123 // This ParseVariableValue implementation ignores the parsing mode, 124 // so we'll just always parse literal here. (The parsing mode is 125 // selected by the remote server, not by our local configuration.) 126 gotIV, diags := v.ParseVariableValue(configs.VariableParseLiteral) 127 if test.WantError != "" { 128 if !diags.HasErrors() { 129 t.Fatalf("missing expected error\ngot: <no error>\nwant: %s", test.WantError) 130 } 131 errStr := diags.Err().Error() 132 if errStr != test.WantError { 133 t.Fatalf("wrong error\ngot: %s\nwant: %s", errStr, test.WantError) 134 } 135 } else { 136 if diags.HasErrors() { 137 t.Fatalf("unexpected error\ngot: %s\nwant: <no error>", diags.Err().Error()) 138 } 139 got := gotIV.Value 140 if !test.Want.RawEquals(got) { 141 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) 142 } 143 } 144 }) 145 } 146 } 147 148 func TestRemoteContextWithVars(t *testing.T) { 149 catTerraform := tfe.CategoryTerraform 150 catEnv := tfe.CategoryEnv 151 152 tests := map[string]struct { 153 Opts *tfe.VariableCreateOptions 154 WantError string 155 }{ 156 "Terraform variable": { 157 &tfe.VariableCreateOptions{ 158 Category: &catTerraform, 159 }, 160 `Value for undeclared variable: A variable named "key" was assigned a value, but the root module does not declare a variable of that name. To use this value, add a "variable" block to the configuration.`, 161 }, 162 "environment variable": { 163 &tfe.VariableCreateOptions{ 164 Category: &catEnv, 165 }, 166 ``, 167 }, 168 } 169 170 for name, test := range tests { 171 t.Run(name, func(t *testing.T) { 172 configDir := "./testdata/empty" 173 174 b, bCleanup := testBackendDefault(t) 175 defer bCleanup() 176 177 _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) 178 defer configCleanup() 179 180 workspaceID, err := b.getRemoteWorkspaceID(context.Background(), backend.DefaultStateName) 181 if err != nil { 182 t.Fatal(err) 183 } 184 185 op := &backend.Operation{ 186 ConfigDir: configDir, 187 ConfigLoader: configLoader, 188 Workspace: backend.DefaultStateName, 189 } 190 191 v := test.Opts 192 if v.Key == nil { 193 key := "key" 194 v.Key = &key 195 } 196 b.client.Variables.Create(nil, workspaceID, *v) 197 198 _, _, diags := b.Context(op) 199 200 if test.WantError != "" { 201 if !diags.HasErrors() { 202 t.Fatalf("missing expected error\ngot: <no error>\nwant: %s", test.WantError) 203 } 204 errStr := diags.Err().Error() 205 if errStr != test.WantError { 206 t.Fatalf("wrong error\ngot: %s\nwant: %s", errStr, test.WantError) 207 } 208 } else { 209 if diags.HasErrors() { 210 t.Fatalf("unexpected error\ngot: %s\nwant: <no error>", diags.Err().Error()) 211 } 212 } 213 }) 214 } 215 }