]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
107c1cdb | 4 | "fmt" |
bae9f6d2 | 5 | "log" |
107c1cdb ND |
6 | |
7 | "github.com/hashicorp/terraform/addrs" | |
8 | "github.com/hashicorp/terraform/configs" | |
bae9f6d2 JC |
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. | |
107c1cdb ND |
16 | type EvalCountFixZeroOneBoundaryGlobal struct { |
17 | Config *configs.Config | |
18 | } | |
bae9f6d2 JC |
19 | |
20 | // TODO: test | |
21 | func (n *EvalCountFixZeroOneBoundaryGlobal) Eval(ctx EvalContext) (interface{}, error) { | |
107c1cdb ND |
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)) | |
bae9f6d2 | 32 | for _, m := range state.Modules { |
107c1cdb ND |
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 { | |
bae9f6d2 JC |
44 | return nil, err |
45 | } | |
46 | } | |
47 | ||
48 | return nil, nil | |
49 | } | |
50 | ||
107c1cdb ND |
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) | |
bae9f6d2 JC |
63 | } |
64 | ||
107c1cdb ND |
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) | |
bae9f6d2 JC |
70 | continue |
71 | } | |
107c1cdb ND |
72 | hasCount := rCfg.Count != nil |
73 | fixResourceCountSetTransition(ctx, addr, hasCount) | |
bae9f6d2 JC |
74 | } |
75 | ||
76 | return nil | |
77 | } |