core: Tolerate missing resource variables during input walk
Provider nodes interpolate their config during the input walk, but this is very early and so it's pretty likely that any resources referenced are entirely absent from the state. As a special case then, we tolerate the normally-fatal case of having an entirely missing resource variable so that the input walk can complete, albeit skipping the providers that have such interpolations. If these interpolations end up still being unresolved during refresh (e.g. because the config references a resource that hasn't been created yet) then we will catch that error on the refresh pass, or indeed on the plan pass if -refresh=false is used.
This commit is contained in:
parent
f95dccf1b3
commit
453fc505f4
|
@ -230,26 +230,38 @@ func (i *Interpolater) valueResourceVar(
|
|||
return nil
|
||||
}
|
||||
|
||||
var variable *ast.Variable
|
||||
var err error
|
||||
|
||||
if v.Multi && v.Index == -1 {
|
||||
variable, err := i.computeResourceMultiVariable(scope, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if variable == nil {
|
||||
return fmt.Errorf("no error reported by variable %q is nil", v.Name)
|
||||
}
|
||||
result[n] = *variable
|
||||
variable, err = i.computeResourceMultiVariable(scope, v)
|
||||
} else {
|
||||
variable, err := i.computeResourceVariable(scope, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if variable == nil {
|
||||
return fmt.Errorf("no error reported by variable %q is nil", v.Name)
|
||||
}
|
||||
result[n] = *variable
|
||||
variable, err = i.computeResourceVariable(scope, v)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if variable == nil {
|
||||
// During the input walk we tolerate missing variables because
|
||||
// we haven't yet had a chance to refresh state, so dynamic data may
|
||||
// not yet be complete.
|
||||
// If it truly is missing, we'll catch it on a later walk.
|
||||
// This applies only to graph nodes that interpolate during the
|
||||
// config walk, e.g. providers.
|
||||
if i.Operation == walkInput {
|
||||
result[n] = ast.Variable{
|
||||
Value: config.UnknownVariableValue,
|
||||
Type: ast.TypeString,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("variable %q is nil, but no error was reported", v.Name)
|
||||
}
|
||||
|
||||
result[n] = *variable
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,60 @@ func TestInterpolater_resourceVariable(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestInterpolater_resourceVariableMissingDuringInput(t *testing.T) {
|
||||
// During the input walk, computed resource attributes may be entirely
|
||||
// absent since we've not yet produced diffs that tell us what computed
|
||||
// attributes to expect. In that case, interpolator tolerates it and
|
||||
// indicates the value is computed.
|
||||
|
||||
lock := new(sync.RWMutex)
|
||||
state := &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
// No resources at all yet, because we're still dealing
|
||||
// with input and so the resources haven't been created.
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
{
|
||||
i := &Interpolater{
|
||||
Operation: walkInput,
|
||||
Module: testModule(t, "interpolate-resource-variable"),
|
||||
State: state,
|
||||
StateLock: lock,
|
||||
}
|
||||
|
||||
scope := &InterpolationScope{
|
||||
Path: rootModulePath,
|
||||
}
|
||||
|
||||
testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
|
||||
Value: config.UnknownVariableValue,
|
||||
Type: ast.TypeString,
|
||||
})
|
||||
}
|
||||
|
||||
// This doesn't apply during other walks, like plan
|
||||
{
|
||||
i := &Interpolater{
|
||||
Operation: walkPlan,
|
||||
Module: testModule(t, "interpolate-resource-variable"),
|
||||
State: state,
|
||||
StateLock: lock,
|
||||
}
|
||||
|
||||
scope := &InterpolationScope{
|
||||
Path: rootModulePath,
|
||||
}
|
||||
|
||||
testInterpolateErr(t, i, scope, "aws_instance.web.foo")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolater_resourceVariableMulti(t *testing.T) {
|
||||
lock := new(sync.RWMutex)
|
||||
state := &State{
|
||||
|
@ -473,3 +527,20 @@ func testInterpolate(
|
|||
t.Fatalf("%q: actual: %#v\nexpected: %#v", n, actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func testInterpolateErr(
|
||||
t *testing.T, i *Interpolater,
|
||||
scope *InterpolationScope,
|
||||
n string) {
|
||||
v, err := config.NewInterpolatedVariable(n)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
_, err = i.Values(scope, map[string]config.InterpolatedVariable{
|
||||
"foo": v,
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("%q: succeeded, but wanted error", n)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue