mirror of
https://github.com/hashicorp/terraform.git
synced 2026-05-28 04:03:27 -04:00
Merge branch 'main' into provider-defined-function-during-init
This commit is contained in:
commit
b8bdde09ca
18 changed files with 235 additions and 36 deletions
5
.changes/v1.15/BUG FIXES-20260429-183507.yaml
Normal file
5
.changes/v1.15/BUG FIXES-20260429-183507.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: BUG FIXES
|
||||
body: Fix non-const variable checks on `init`
|
||||
time: 2026-04-29T18:35:07.99622+02:00
|
||||
custom:
|
||||
Issue: "38470"
|
||||
5
.changes/v1.15/BUG FIXES-20260430-103820.yaml
Normal file
5
.changes/v1.15/BUG FIXES-20260430-103820.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: BUG FIXES
|
||||
body: Fix panic for types modules with no expanded instances
|
||||
time: 2026-04-30T10:38:20.648469-04:00
|
||||
custom:
|
||||
Issue: "38491"
|
||||
5
.changes/v1.15/BUG FIXES-20260430-121433.yaml
Normal file
5
.changes/v1.15/BUG FIXES-20260430-121433.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: BUG FIXES
|
||||
body: Avoid warnings in 'terraform output -raw'
|
||||
time: 2026-04-30T12:14:33.373975+02:00
|
||||
custom:
|
||||
Issue: "38487"
|
||||
5
.changes/v1.15/BUG FIXES-20260430-154241.yaml
Normal file
5
.changes/v1.15/BUG FIXES-20260430-154241.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
kind: BUG FIXES
|
||||
body: Ignore undeclared variable values from the cloud backend
|
||||
time: 2026-04-30T15:42:41.89732+02:00
|
||||
custom:
|
||||
Issue: "38490"
|
||||
2
.github/workflows/changelog.yml
vendored
2
.github/workflows/changelog.yml
vendored
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
.changie.yaml
|
||||
.changes/
|
||||
sparse-checkout-cone-mode: false
|
||||
ref: ${{ github.ref }} # Ref refers to the target branch of this PR
|
||||
ref: ${{ github.base_ref }} # Base ref refers to the target branch of this PR
|
||||
|
||||
- name: "Check for changelog entry"
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ func ParseUndeclaredVariableValues(vv map[string]arguments.UnparsedVariableValue
|
|||
// variables, because users will often set these globally
|
||||
// when they are used across many (but not necessarily all)
|
||||
// configurations.
|
||||
case terraform.ValueFromCloud:
|
||||
// We allow and ignore undeclared names fetched from the cloud
|
||||
// backend, because users will often set these globally or via
|
||||
// varsets when they are used across many (but not necessarily all)
|
||||
// workspaces.
|
||||
case terraform.ValueFromCLIArg:
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
|
|
|
|||
|
|
@ -303,6 +303,6 @@ func (v *remoteStoredVariableValue) ParseVariableValue(mode configs.VariablePars
|
|||
// roughly speaking, a similar idea to entering variable values at
|
||||
// the interactive CLI prompts. It's not a perfect correspondance,
|
||||
// but it's closer than the other options.
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
}, diags
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,7 +169,8 @@ func TestRemoteContextWithVars(t *testing.T) {
|
|||
&tfe.VariableCreateOptions{
|
||||
Category: &catTerraform,
|
||||
},
|
||||
`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.`,
|
||||
// We don't expect an error for values of undeclared variables
|
||||
``,
|
||||
},
|
||||
"environment variable": {
|
||||
&tfe.VariableCreateOptions{
|
||||
|
|
@ -279,7 +280,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
terraform.InputValues{
|
||||
varName1: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue1),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -288,7 +289,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
},
|
||||
varName2: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue2),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -297,7 +298,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
},
|
||||
varName3: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue3),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -328,7 +329,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
terraform.InputValues{
|
||||
varName1: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue1),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -337,7 +338,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
},
|
||||
varName2: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue2),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -373,7 +374,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
terraform.InputValues{
|
||||
varName1: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue1),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
@ -382,7 +383,7 @@ func TestRemoteVariablesDoNotOverride(t *testing.T) {
|
|||
},
|
||||
varName2: &terraform.InputValue{
|
||||
Value: cty.StringVal(varValue2),
|
||||
SourceType: terraform.ValueFromInput,
|
||||
SourceType: terraform.ValueFromCloud,
|
||||
SourceRange: tfdiags.SourceRange{
|
||||
Filename: "",
|
||||
Start: tfdiags.SourcePos{Line: 0, Column: 0, Byte: 0},
|
||||
|
|
|
|||
|
|
@ -13,9 +13,13 @@ import (
|
|||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/backend"
|
||||
backendInit "github.com/hashicorp/terraform/internal/backend/init"
|
||||
"github.com/hashicorp/terraform/internal/backend/remote-state/inmem"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/states/statefile"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func TestOutput(t *testing.T) {
|
||||
|
|
@ -381,3 +385,81 @@ func TestOutput_stateDefault(t *testing.T) {
|
|||
t.Fatalf("bad: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// deprecatedInmemBackend wraps the inmem backend and injects a deprecation
|
||||
// warning from PrepareConfig, simulating a backend with deprecated attributes
|
||||
// (like the S3 backend's dynamodb_table).
|
||||
type deprecatedInmemBackend struct {
|
||||
backend.Backend
|
||||
}
|
||||
|
||||
func (b *deprecatedInmemBackend) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) {
|
||||
newObj, diags := b.Backend.PrepareConfig(obj)
|
||||
diags = diags.Append(tfdiags.SimpleWarning(`The attribute "deprecated_attr" is deprecated.`))
|
||||
return newObj, diags
|
||||
}
|
||||
|
||||
func TestOutputRaw_warningsSuppressed(t *testing.T) {
|
||||
// Pre-populate the inmem backend with a state containing an output value
|
||||
inmem.Reset()
|
||||
originalState := states.BuildState(func(s *states.SyncState) {
|
||||
s.SetOutputValue(
|
||||
addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
|
||||
cty.StringVal("bar"),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
// Register a backend that wraps inmem with a deprecation warning,
|
||||
// simulating a backend like S3 whose PrepareConfig warns about
|
||||
// deprecated attributes (e.g. dynamodb_table).
|
||||
backendInit.Set("inmem", func() backend.Backend {
|
||||
return &deprecatedInmemBackend{Backend: inmem.New()}
|
||||
})
|
||||
defer backendInit.Set("inmem", inmem.New)
|
||||
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath("output-backend-with-deprecation"), td)
|
||||
t.Chdir(td)
|
||||
|
||||
// Write the state into the inmem backend's default workspace
|
||||
b := inmem.New()
|
||||
b.Configure(cty.ObjectVal(map[string]cty.Value{
|
||||
"lock_id": cty.NullVal(cty.String),
|
||||
}))
|
||||
sMgr, sDiags := b.StateMgr(backend.DefaultStateName)
|
||||
if sDiags.HasErrors() {
|
||||
t.Fatalf("unexpected error: %s", sDiags.Err())
|
||||
}
|
||||
sMgr.WriteState(originalState)
|
||||
if err := sMgr.PersistState(nil); err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
view, done := testView(t)
|
||||
c := &OutputCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{"-raw", "foo"}
|
||||
code := c.Run(args)
|
||||
output := done(t)
|
||||
if code != 0 {
|
||||
t.Fatalf("unexpected exit code %d\nstderr:\n%s", code, output.Stderr())
|
||||
}
|
||||
|
||||
// The key assertion: warnings must not appear in raw output
|
||||
// as they would be indistinguishable from the value.
|
||||
stderr := output.Stderr()
|
||||
if strings.Contains(stderr, "deprecated") {
|
||||
t.Fatalf("warnings should be suppressed, got:\n%s", stderr)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(output.Stdout())
|
||||
if actual != `bar` {
|
||||
t.Fatalf("expected output \"bar\", got: %#v", actual)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
internal/command/testdata/output-backend-with-deprecation/.terraform/terraform.tfstate
vendored
Normal file
12
internal/command/testdata/output-backend-with-deprecation/.terraform/terraform.tfstate
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 3,
|
||||
"serial": 0,
|
||||
"lineage": "test-output-deprecation",
|
||||
"backend": {
|
||||
"type": "inmem",
|
||||
"config": {
|
||||
"lock_id": null
|
||||
},
|
||||
"hash": 3947750061
|
||||
}
|
||||
}
|
||||
7
internal/command/testdata/output-backend-with-deprecation/main.tf
vendored
Normal file
7
internal/command/testdata/output-backend-with-deprecation/main.tf
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
terraform {
|
||||
backend "inmem" {}
|
||||
}
|
||||
|
||||
output "foo" {
|
||||
value = "bar"
|
||||
}
|
||||
|
|
@ -176,7 +176,11 @@ func (v *OutputRaw) Output(name string, outputs map[string]*states.OutputValue)
|
|||
}
|
||||
|
||||
func (v *OutputRaw) Diagnostics(diags tfdiags.Diagnostics) {
|
||||
v.view.Diagnostics(diags)
|
||||
// filter out warnings as these wouldn't be expected in raw mode
|
||||
// as they typically don't influence exit code so user cannot
|
||||
// expect them in stdout
|
||||
errsOnly := diags.ErrorsOnly()
|
||||
v.view.Diagnostics(errsOnly)
|
||||
}
|
||||
|
||||
// The OutputJSON implementation renders outputs as JSON values. When rendering
|
||||
|
|
|
|||
|
|
@ -697,22 +697,35 @@ module "example" {
|
|||
"non-const variable validation does not run during init": {
|
||||
module: map[string]string{
|
||||
"main.tf": `
|
||||
variable "some" {
|
||||
type = string
|
||||
}
|
||||
variable "name" {
|
||||
type = string
|
||||
default = "bad"
|
||||
|
||||
validation {
|
||||
condition = var.name != "bad"
|
||||
condition = var.name != var.some
|
||||
error_message = "must not be bad"
|
||||
}
|
||||
}
|
||||
module "example" {
|
||||
source = "./modules/fixed"
|
||||
source = "./modules/example"
|
||||
|
||||
name = var.name
|
||||
}
|
||||
`,
|
||||
},
|
||||
mockedLoadModuleCalls: map[string]map[string]string{
|
||||
"./modules/example": {
|
||||
"main.tf": `
|
||||
variable "name" {
|
||||
type = string
|
||||
}
|
||||
`},
|
||||
},
|
||||
expectLoadModuleCalls: []*configs.ModuleRequest{{
|
||||
SourceAddr: mustModuleSource(t, "./modules/fixed"),
|
||||
SourceAddr: mustModuleSource(t, "./modules/example"),
|
||||
}},
|
||||
},
|
||||
} {
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd
|
|||
// that are disabled, etc. Terraform's static validation leans towards
|
||||
// being liberal in what it accepts because the subsequent plan walk has
|
||||
// more information available and so can be more conservative.
|
||||
if d.Operation == walkValidate {
|
||||
if d.Operation == walkValidate || (d.Operation == walkInit && !config.Const) {
|
||||
// We should still capture the statically-configured marks during
|
||||
// the validate walk.
|
||||
ret := cty.UnknownVal(config.Type)
|
||||
|
|
@ -438,23 +438,25 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc
|
|||
noDynamicTypes = noDynamicTypes && !out.ConstraintType.HasDynamicTypes()
|
||||
}
|
||||
|
||||
if d.Operation == walkValidate && typeDefined {
|
||||
atys := make(map[string]cty.Type, len(outputConfigs))
|
||||
as := make(map[string]cty.Value, len(outputConfigs))
|
||||
for name, c := range outputConfigs {
|
||||
// atys is used to create the module object type for expanded modules
|
||||
atys[name] = c.ConstraintType
|
||||
// the unknown val can be used when we return a single module
|
||||
// instance with unknown outputs
|
||||
val := cty.UnknownVal(c.ConstraintType)
|
||||
// build up the type of the configured module output object
|
||||
atys := make(map[string]cty.Type, len(outputConfigs))
|
||||
// and create a single unknown instance value for validation
|
||||
as := make(map[string]cty.Value, len(outputConfigs))
|
||||
for name, c := range outputConfigs {
|
||||
// atys is used to create the module object type for expanded modules
|
||||
atys[name] = c.ConstraintType
|
||||
|
||||
if c.DeprecatedSet {
|
||||
val = val.Mark(marks.NewDeprecation(c.Deprecated, absAddr.Output(name).ConfigOutputValue().ForDisplay()))
|
||||
}
|
||||
as[name] = val
|
||||
// the unknown val can be used when we return a single module
|
||||
// instance with unknown outputs
|
||||
val := cty.UnknownVal(c.ConstraintType)
|
||||
if c.DeprecatedSet {
|
||||
val = val.Mark(marks.NewDeprecation(c.Deprecated, absAddr.Output(name).ConfigOutputValue().ForDisplay()))
|
||||
}
|
||||
instTy := cty.Object(atys)
|
||||
as[name] = val
|
||||
}
|
||||
instTy := cty.Object(atys)
|
||||
|
||||
if d.Operation == walkValidate && typeDefined {
|
||||
switch {
|
||||
case callConfig.Count != nil && noDynamicTypes:
|
||||
return cty.UnknownVal(cty.List(instTy)), diags
|
||||
|
|
@ -580,9 +582,12 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc
|
|||
elems = append(elems, instVal)
|
||||
diags = diags.Append(moreDiags)
|
||||
}
|
||||
if noDynamicTypes {
|
||||
switch {
|
||||
case noDynamicTypes && len(elems) == 0:
|
||||
return cty.ListValEmpty(instTy), diags
|
||||
case noDynamicTypes:
|
||||
return cty.ListVal(elems), diags
|
||||
} else {
|
||||
default:
|
||||
return cty.TupleVal(elems), diags
|
||||
}
|
||||
|
||||
|
|
@ -593,9 +598,13 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc
|
|||
attrs[string(instKey.(addrs.StringKey))] = instVal
|
||||
diags = diags.Append(moreDiags)
|
||||
}
|
||||
if noDynamicTypes {
|
||||
|
||||
switch {
|
||||
case noDynamicTypes && len(attrs) == 0:
|
||||
return cty.MapValEmpty(instTy), diags
|
||||
case noDynamicTypes:
|
||||
return cty.MapVal(attrs), diags
|
||||
} else {
|
||||
default:
|
||||
return cty.ObjectVal(attrs), diags
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -612,6 +612,14 @@ func TestEvaluatorGetModule_validateTypedOutputs(t *testing.T) {
|
|||
"out": cty.String,
|
||||
}))),
|
||||
},
|
||||
"empty_count": {
|
||||
configureModuleCall: func(call *configs.ModuleCall) {
|
||||
call.Count = hcltest.MockExprLiteral(cty.NumberIntVal(0))
|
||||
},
|
||||
want: cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{
|
||||
"out": cty.String,
|
||||
}))),
|
||||
},
|
||||
"for_each": {
|
||||
configureModuleCall: func(call *configs.ModuleCall) {
|
||||
call.ForEach = hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{
|
||||
|
|
@ -729,6 +737,15 @@ func TestEvaluatorGetModule_planTypedOutputs(t *testing.T) {
|
|||
cty.ObjectVal(map[string]cty.Value{"out": cty.StringVal("second").Mark(marks.Sensitive)}),
|
||||
}),
|
||||
},
|
||||
"empty_count": {
|
||||
setupInstances: func(expander *instances.Expander) {
|
||||
expander.SetModuleCount(addrs.RootModuleInstance, addrs.ModuleCall{Name: "mod"}, 0)
|
||||
},
|
||||
setupOutputs: func(namedValues *namedvals.State) {
|
||||
// explicitly setting no values
|
||||
},
|
||||
want: cty.ListValEmpty(cty.Object(map[string]cty.Type{"out": cty.String})),
|
||||
},
|
||||
"for_each": {
|
||||
setupInstances: func(expander *instances.Expander) {
|
||||
expander.SetModuleForEach(addrs.RootModuleInstance, addrs.ModuleCall{Name: "mod"}, map[string]cty.Value{
|
||||
|
|
@ -755,6 +772,15 @@ func TestEvaluatorGetModule_planTypedOutputs(t *testing.T) {
|
|||
"b": cty.ObjectVal(map[string]cty.Value{"out": cty.StringVal("second").Mark(marks.Sensitive)}),
|
||||
}),
|
||||
},
|
||||
"empty_for_each": {
|
||||
setupInstances: func(expander *instances.Expander) {
|
||||
expander.SetModuleForEach(addrs.RootModuleInstance, addrs.ModuleCall{Name: "mod"}, map[string]cty.Value{})
|
||||
},
|
||||
setupOutputs: func(namedValues *namedvals.State) {
|
||||
// no values for empty for_each
|
||||
},
|
||||
want: cty.MapValEmpty(cty.Object(map[string]cty.Type{"out": cty.String})),
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func _() {
|
|||
_ = x[ValueFromInput-73]
|
||||
_ = x[ValueFromPlan-80]
|
||||
_ = x[ValueFromCaller-83]
|
||||
_ = x[ValueFromCloud-84]
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -27,11 +28,12 @@ const (
|
|||
_ValueSourceType_name_4 = "ValueFromInput"
|
||||
_ValueSourceType_name_5 = "ValueFromNamedFile"
|
||||
_ValueSourceType_name_6 = "ValueFromPlan"
|
||||
_ValueSourceType_name_7 = "ValueFromCaller"
|
||||
_ValueSourceType_name_7 = "ValueFromCallerValueFromCloud"
|
||||
)
|
||||
|
||||
var (
|
||||
_ValueSourceType_index_3 = [...]uint8{0, 15, 32}
|
||||
_ValueSourceType_index_7 = [...]uint8{0, 15, 29}
|
||||
)
|
||||
|
||||
func (i ValueSourceType) String() string {
|
||||
|
|
@ -51,8 +53,9 @@ func (i ValueSourceType) String() string {
|
|||
return _ValueSourceType_name_5
|
||||
case i == 80:
|
||||
return _ValueSourceType_name_6
|
||||
case i == 83:
|
||||
return _ValueSourceType_name_7
|
||||
case 83 <= i && i <= 84:
|
||||
i -= 83
|
||||
return _ValueSourceType_name_7[_ValueSourceType_index_7[i]:_ValueSourceType_index_7[i+1]]
|
||||
default:
|
||||
return "ValueSourceType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,10 @@ const (
|
|||
// ValueFromCaller indicates that the value was explicitly overridden by
|
||||
// a caller to Context.SetVariable after the context was constructed.
|
||||
ValueFromCaller ValueSourceType = 'S'
|
||||
|
||||
// ValueFromCloud indicates that the value was retrieved from a remote source,
|
||||
// such as HCP Terraform or Terraform Enterprise.
|
||||
ValueFromCloud ValueSourceType = 'T'
|
||||
)
|
||||
|
||||
func (v *InputValue) GoString() string {
|
||||
|
|
@ -153,6 +157,8 @@ func (v ValueSourceType) DiagnosticLabel() string {
|
|||
return "set by an interactive input"
|
||||
case ValueFromPlan:
|
||||
return "set by the plan"
|
||||
case ValueFromCloud:
|
||||
return "set by the cloud backend"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,17 @@ func (diags Diagnostics) Warnings() Diagnostics {
|
|||
return warns
|
||||
}
|
||||
|
||||
// ErrorsOnly returns a Diagnostics list containing only diagnostics with a severity of Error.
|
||||
func (diags Diagnostics) ErrorsOnly() Diagnostics {
|
||||
var errorsOnly = Diagnostics{}
|
||||
for _, diag := range diags {
|
||||
if diag.Severity() == Error {
|
||||
errorsOnly = append(errorsOnly, diag)
|
||||
}
|
||||
}
|
||||
return errorsOnly
|
||||
}
|
||||
|
||||
// HasErrors returns true if any of the diagnostics in the list have
|
||||
// a severity of Error.
|
||||
func (diags Diagnostics) HasErrors() bool {
|
||||
|
|
|
|||
Loading…
Reference in a new issue