mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-09 07:05:33 -06:00
terraform: expand resource nodes at walk time
This commit is contained in:
parent
fecb68f117
commit
fb1c224e12
@ -1057,7 +1057,8 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
||||
// This will keep track of whether we're stopped or not
|
||||
var stop uint32 = 0
|
||||
|
||||
return func(n *depgraph.Noun) error {
|
||||
var walkFn depgraph.WalkFunc
|
||||
walkFn = func(n *depgraph.Noun) error {
|
||||
// If it is the root node, ignore
|
||||
if n.Name == GraphRootNode {
|
||||
return nil
|
||||
@ -1132,6 +1133,42 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
||||
|
||||
rn := n.Meta.(*GraphNodeResource)
|
||||
|
||||
// If we're expanding, then expand the nodes, and then rewalk the graph
|
||||
if rn.ExpandMode > ResourceExpandNone {
|
||||
ns, err := rn.Expand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Go through all the nouns and run them in parallel, collecting
|
||||
// any errors.
|
||||
var l sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
errs := make([]error, 0, len(ns))
|
||||
for _, n := range ns {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := walkFn(n); err != nil {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for the subgraph
|
||||
wg.Wait()
|
||||
|
||||
// If there are errors, then we should return them
|
||||
if len(errs) > 0 {
|
||||
return &multierror.Error{Errors: errs}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make sure that at least some resource configuration is set
|
||||
if rn.Config == nil {
|
||||
rn.Resource.Config = new(ResourceConfig)
|
||||
@ -1163,6 +1200,8 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return walkFn
|
||||
}
|
||||
|
||||
// applyProvisioners is used to run any provisioners a resource has
|
||||
|
@ -92,6 +92,9 @@ type GraphNodeResource struct {
|
||||
Resource *Resource
|
||||
ResourceProviderNode string
|
||||
|
||||
Diff *ModuleDiff
|
||||
State *ModuleState
|
||||
|
||||
// Expand, if true, indicates that this resource needs to be expanded
|
||||
// at walk-time to multiple resources.
|
||||
ExpandMode ResourceExpandMode
|
||||
@ -372,6 +375,8 @@ func graphAddConfigResources(
|
||||
nounsList := make([]*depgraph.Noun, len(c.Resources))
|
||||
for i, r := range c.Resources {
|
||||
name := r.Id()
|
||||
|
||||
// Build the noun
|
||||
nounsList[i] = &depgraph.Noun{
|
||||
Name: name,
|
||||
Meta: &GraphNodeResource{
|
||||
@ -385,6 +390,7 @@ func graphAddConfigResources(
|
||||
Type: r.Type,
|
||||
},
|
||||
},
|
||||
State: mod.View(name),
|
||||
ExpandMode: ResourceExpandApply,
|
||||
},
|
||||
}
|
||||
@ -529,6 +535,11 @@ func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error {
|
||||
}
|
||||
}
|
||||
|
||||
// If we're expanding, save the diff so we can add it on later
|
||||
if rn.ExpandMode > ResourceExpandNone {
|
||||
rn.Diff = d
|
||||
}
|
||||
|
||||
var rd *InstanceDiff
|
||||
if rn.ExpandMode == ResourceExpandNone {
|
||||
rd = diffs[0]
|
||||
@ -1577,6 +1588,160 @@ func (p *graphSharedProvider) MergeConfig(
|
||||
return NewResourceConfig(rc)
|
||||
}
|
||||
|
||||
// Expand will expand this node into a subgraph if Expand is set.
|
||||
func (n *GraphNodeResource) Expand() ([]*depgraph.Noun, error) {
|
||||
count := 1
|
||||
|
||||
g := new(depgraph.Graph)
|
||||
|
||||
// Determine the nodes to create. If we're just looking for the
|
||||
// nodes to create, return that.
|
||||
n.expandCreate(g, count)
|
||||
|
||||
// Add in the diff if we have it
|
||||
if n.Diff != nil {
|
||||
if err := graphAddDiff(g, n.Diff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If we're just expanding the apply, then filter those out and
|
||||
// return them now.
|
||||
if n.ExpandMode == ResourceExpandApply {
|
||||
return n.filterNouns(g, false), nil
|
||||
}
|
||||
|
||||
if n.State != nil {
|
||||
// TODO: orphans
|
||||
|
||||
// Add the tainted resources
|
||||
graphAddTainted(g, n.State)
|
||||
}
|
||||
|
||||
return n.filterNouns(g, true), nil
|
||||
}
|
||||
|
||||
// expandCreate expands this resource and adds the resources to the graph.
|
||||
func (n *GraphNodeResource) expandCreate(g *depgraph.Graph, count int) {
|
||||
// Create the list of nouns that we'd have to create
|
||||
create := make([]*depgraph.Noun, 0, count)
|
||||
|
||||
// First thing, expand the counts that we have defined for our
|
||||
// current config into the full set of resources.
|
||||
r := n.Config
|
||||
for i := 0; i < count; i++ {
|
||||
name := r.Id()
|
||||
index := -1
|
||||
|
||||
// If we have a count that is more than one, then make sure
|
||||
// we suffix with the number of the resource that this is.
|
||||
if count > 1 {
|
||||
name = fmt.Sprintf("%s.%d", name, i)
|
||||
index = i
|
||||
}
|
||||
|
||||
var state *ResourceState
|
||||
if n.State != nil {
|
||||
// Lookup the resource state
|
||||
if s, ok := n.State.Resources[name]; ok {
|
||||
state = s
|
||||
}
|
||||
|
||||
if state == nil {
|
||||
if count == 1 {
|
||||
// If the count is one, check the state for ".0"
|
||||
// appended, which might exist if we go from
|
||||
// count > 1 to count == 1.
|
||||
k := r.Id() + ".0"
|
||||
state = n.State.Resources[k]
|
||||
} else if i == 0 {
|
||||
// If count is greater than one, check for state
|
||||
// with just the ID, which might exist if we go
|
||||
// from count == 1 to count > 1
|
||||
state = n.State.Resources[r.Id()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state == nil {
|
||||
state = &ResourceState{
|
||||
Type: r.Type,
|
||||
}
|
||||
}
|
||||
|
||||
flags := FlagPrimary
|
||||
if len(state.Tainted) > 0 {
|
||||
flags |= FlagHasTainted
|
||||
}
|
||||
|
||||
// Copy the base resource so we can fill it in
|
||||
resource := n.copyResource(name)
|
||||
resource.State = state.Primary
|
||||
resource.Flags = flags
|
||||
// TODO: we need the diff here...
|
||||
|
||||
// Add the result
|
||||
create = append(create, &depgraph.Noun{
|
||||
Name: name,
|
||||
Meta: &GraphNodeResource{
|
||||
Index: index,
|
||||
Config: r,
|
||||
Resource: resource,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
g.Nouns = append(g.Nouns, create...)
|
||||
}
|
||||
|
||||
// copyResource copies the Resource structure to assign to a subgraph.
|
||||
func (n *GraphNodeResource) copyResource(id string) *Resource {
|
||||
info := *n.Resource.Info
|
||||
info.Id = id
|
||||
resource := *n.Resource
|
||||
resource.Id = id
|
||||
resource.Info = &info
|
||||
resource.Config = NewResourceConfig(n.Config.RawConfig)
|
||||
return &resource
|
||||
}
|
||||
|
||||
func (n *GraphNodeResource) filterNouns(
|
||||
g *depgraph.Graph, destroy bool) []*depgraph.Noun {
|
||||
result := make([]*depgraph.Noun, 0, len(g.Nouns))
|
||||
for _, n := range g.Nouns {
|
||||
rn, ok := n.Meta.(*GraphNodeResource)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// If the diff is nil, then we're not destroying, so append only
|
||||
// in that case.
|
||||
if rn.Resource.Diff == nil {
|
||||
if !destroy {
|
||||
result = append(result, n)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// If we are destroying, append it only if we care about destroys
|
||||
if rn.Resource.Diff.Destroy {
|
||||
if destroy {
|
||||
result = append(result, n)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// If we're not destroying, then add it only if we don't
|
||||
// care about deploys.
|
||||
if !destroy {
|
||||
result = append(result, n)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// matchingPrefixes takes a resource type and a set of resource
|
||||
// providers we know about by prefix and returns a list of prefixes
|
||||
// that might be valid for that resource.
|
||||
|
@ -212,6 +212,24 @@ func (m *ModuleState) Orphans(c *config.Config) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
// View returns a view with the given resource prefix.
|
||||
func (m *ModuleState) View(id string) *ModuleState {
|
||||
if m == nil {
|
||||
return m
|
||||
}
|
||||
|
||||
r := m.deepcopy()
|
||||
for k, _ := range r.Resources {
|
||||
if id == k || strings.HasPrefix(k, id+".") {
|
||||
continue
|
||||
}
|
||||
|
||||
delete(r.Resources, k)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (m *ModuleState) init() {
|
||||
if m.Outputs == nil {
|
||||
m.Outputs = make(map[string]string)
|
||||
|
Loading…
Reference in New Issue
Block a user