diff --git a/terraform/eval_state.go b/terraform/eval_state.go index 46ba5befde..36051e1e58 100644 --- a/terraform/eval_state.go +++ b/terraform/eval_state.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "log" "github.com/hashicorp/terraform/addrs" ) @@ -208,6 +209,7 @@ func writeInstanceToState( rs.Type = resourceType rs.Dependencies = dependencies rs.Provider = provider + log.Printf("[TRACE] Saving state for %s, managed by %s", resourceName, provider) if err := writerFn(rs); err != nil { return nil, err diff --git a/terraform/evaluate.go b/terraform/evaluate.go index f7bcaf5d84..ffc5b70c20 100644 --- a/terraform/evaluate.go +++ b/terraform/evaluate.go @@ -2,6 +2,7 @@ package terraform import ( "fmt" + "log" "os" "strconv" "strings" @@ -462,10 +463,10 @@ func (d *evaluationStateData) GetResourceInstance(addr addrs.ResourceInstance, r // we revise the state structs to natively support the HCL type system. rs := ms.Resources[addrKey] - // If we have an exact match for the requested instance and it has non-nil - // primary data then we'll use it directly. This is the easy path. - if rs != nil && rs.Primary != nil { - providerAddr, err := rs.ProviderAddr() + var providerAddr addrs.AbsProviderConfig + if rs != nil { + var err error + providerAddr, err = rs.ProviderAddr() if err != nil { // This indicates corruption of or tampering with the state file diags = diags.Append(&hcl.Diagnostic{ @@ -476,6 +477,20 @@ func (d *evaluationStateData) GetResourceInstance(addr addrs.ResourceInstance, r }) return cty.DynamicVal, diags } + } else { + // Must assume a provider address from the config, then. + // This result is usually ignored since we'll probably end up in + // the getResourceInstancesAll path after this (if our instance + // actually has a key). However, we can also end up here in strange + // cases like "terraform console", which might be used before a + // particular resource has been created in state at all. + providerAddr = config.ProviderConfigAddr().Absolute(d.ModulePath) + } + + // If we have an exact match for the requested instance and it has non-nil + // primary data then we'll use it directly. This is the easy path. + if rs != nil && rs.Primary != nil { + log.Printf("[TRACE] GetResourceInstance: %s is a single instance", addr) return d.getResourceInstanceSingle(addr, rng, rs.Primary, providerAddr) } @@ -484,10 +499,11 @@ func (d *evaluationStateData) GetResourceInstance(addr addrs.ResourceInstance, r // If we have a _keyed_ address then instead it's a single instance that // isn't evaluated yet. if addr.Key != addrs.NoKey { - return d.getResourceInstancePending(addr, rng) + log.Printf("[TRACE] GetResourceInstance: %s is pending", addr) + return d.getResourceInstancePending(addr, rng, providerAddr) } - return d.getResourceInstancesAll(addr.ContainingResource(), config, ms) + return d.getResourceInstancesAll(addr.ContainingResource(), config, ms, providerAddr) } func (d *evaluationStateData) getResourceInstanceSingle(addr addrs.ResourceInstance, rng tfdiags.SourceRange, is *InstanceState, providerAddr addrs.AbsProviderConfig) (cty.Value, tfdiags.Diagnostics) { @@ -531,7 +547,7 @@ func (d *evaluationStateData) getResourceInstanceSingle(addr addrs.ResourceInsta return val, diags } -func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, config *configs.Resource, ms *ModuleState) (cty.Value, tfdiags.Diagnostics) { +func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, config *configs.Resource, ms *ModuleState, providerAddr addrs.AbsProviderConfig) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics rng := tfdiags.SourceRangeFromHCL(config.DeclRange) hasCount := config.Count != nil @@ -574,6 +590,8 @@ func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, confi key = addrs.StringKey(keyStr) } + // In this case we'll ignore our given providerAddr, since it was + // for a single unkeyed ResourceState, not the keyed one we have now. providerAddr, err := rs.ProviderAddr() if err != nil { // This indicates corruption of or tampering with the state file @@ -597,10 +615,14 @@ func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, confi // argument then we'll assume that we're dealing with a resource that // is pending creation (e.g. during the validate walk) and that it // will eventually have only one unkeyed instance. - providerAddr := config.ProviderConfigAddr().Absolute(d.ModulePath) + // In this case we _do_ use the given providerAddr, since that + // is for the unkeyed instance we found in GetResourceInstance. + log.Printf("[TRACE] GetResourceInstance: %s has no instances yet", addr) return d.getResourceInstanceSingle(addr.Instance(addrs.NoKey), rng, nil, providerAddr) } + log.Printf("[TRACE] GetResourceInstance: %s has multiple keyed instances (%d)", addr, length) + // TODO: In future, when for_each is implemented, we'll need to decide here // whether to return a tuple value or an object value. However, by that // time we should've revised the state structs so we can see unambigously @@ -628,7 +650,7 @@ func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, confi return cty.TupleVal(valsSeq), diags } -func (d *evaluationStateData) getResourceInstancePending(addr addrs.ResourceInstance, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { +func (d *evaluationStateData) getResourceInstancePending(addr addrs.ResourceInstance, rng tfdiags.SourceRange, providerAddr addrs.AbsProviderConfig) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics // We'd ideally like to return a properly-typed unknown value here, in @@ -655,7 +677,6 @@ func (d *evaluationStateData) getResourceInstancePending(addr addrs.ResourceInst if rc == nil { return cty.DynamicVal, diags } - providerAddr := rc.ProviderConfigAddr().Absolute(d.ModulePath) schema := d.getResourceSchema(addr.ContainingResource(), providerAddr) if schema == nil { return cty.DynamicVal, diags @@ -668,6 +689,7 @@ func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAdd d.Evaluator.ProvidersLock.Lock() defer d.Evaluator.ProvidersLock.Unlock() + log.Printf("[TRACE] Need provider schema for %s", providerAddr) providerSchema := d.Evaluator.ProviderSchemas[providerAddr.String()] if providerSchema == nil { return nil