github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/eval_count_boundary.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
     9  )
    10  
    11  // EvalCountFixZeroOneBoundaryGlobal is an EvalNode that fixes up the state
    12  // when there is a resource count with zero/one boundary, i.e. fixing
    13  // a resource named "aws_instance.foo" to "aws_instance.foo.0" and vice-versa.
    14  //
    15  // This works on the global state.
    16  type EvalCountFixZeroOneBoundaryGlobal struct {
    17  	Config *configs.Config
    18  }
    19  
    20  // TODO: test
    21  func (n *EvalCountFixZeroOneBoundaryGlobal) Eval(ctx EvalContext) (interface{}, error) {
    22  	// We'll temporarily lock the state to grab the modules, then work on each
    23  	// one separately while taking a lock again for each separate resource.
    24  	// This means that if another caller concurrently adds a module here while
    25  	// we're working then we won't update it, but that's no worse than the
    26  	// concurrent writer blocking for our entire fixup process and _then_
    27  	// adding a new module, and in practice the graph node associated with
    28  	// this eval depends on everything else in the graph anyway, so there
    29  	// should not be concurrent writers.
    30  	state := ctx.State().Lock()
    31  	moduleAddrs := make([]addrs.ModuleInstance, 0, len(state.Modules))
    32  	for _, m := range state.Modules {
    33  		moduleAddrs = append(moduleAddrs, m.Addr)
    34  	}
    35  	ctx.State().Unlock()
    36  
    37  	for _, addr := range moduleAddrs {
    38  		cfg := n.Config.DescendentForInstance(addr)
    39  		if cfg == nil {
    40  			log.Printf("[WARN] Not fixing up EachModes for %s because it has no config", addr)
    41  			continue
    42  		}
    43  		if err := n.fixModule(ctx, addr); err != nil {
    44  			return nil, err
    45  		}
    46  	}
    47  
    48  	return nil, nil
    49  }
    50  
    51  func (n *EvalCountFixZeroOneBoundaryGlobal) fixModule(ctx EvalContext, moduleAddr addrs.ModuleInstance) error {
    52  	ms := ctx.State().Module(moduleAddr)
    53  	cfg := n.Config.DescendentForInstance(moduleAddr)
    54  	if ms == nil {
    55  		// Theoretically possible for a concurrent writer to delete a module
    56  		// while we're running, but in practice the graph node that called us
    57  		// depends on everything else in the graph and so there can never
    58  		// be a concurrent writer.
    59  		return fmt.Errorf("[WARN] no state found for %s while trying to fix up EachModes", moduleAddr)
    60  	}
    61  	if cfg == nil {
    62  		return fmt.Errorf("[WARN] no config found for %s while trying to fix up EachModes", moduleAddr)
    63  	}
    64  
    65  	for _, r := range ms.Resources {
    66  		addr := r.Addr.Absolute(moduleAddr)
    67  		rCfg := cfg.Module.ResourceByAddr(r.Addr)
    68  		if rCfg == nil {
    69  			log.Printf("[WARN] Not fixing up EachModes for %s because it has no config", addr)
    70  			continue
    71  		}
    72  		hasCount := rCfg.Count != nil
    73  		fixResourceCountSetTransition(ctx, addr, hasCount)
    74  	}
    75  
    76  	return nil
    77  }