pulumi/pkg/resource/graph/dependency_graph.go

68 lines
2.6 KiB
Go

// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
package graph
import (
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/util/contract"
)
// DependencyGraph represents a dependency graph encoded within a resource snapshot.
type DependencyGraph struct {
index map[*resource.State]int // A mapping of resource pointers to indexes within the snapshot
resources []*resource.State // The list of resources, obtained from the snapshot
}
// DependingOn returns a slice containing all resources that directly or indirectly
// depend upon the given resource. The returned slice is guaranteed to be in topological
// order with respect to the snapshot dependency graph.
//
// The time complexity of DependingOn is linear with respect to the number of resources.
func (dg *DependencyGraph) DependingOn(res *resource.State) []*resource.State {
// This implementation relies on the detail that snapshots are stored in a valid
// topological order.
var dependents []*resource.State
dependentSet := make(map[resource.URN]bool)
cursorIndex, ok := dg.index[res]
contract.Assert(ok)
dependentSet[res.URN] = true
// The dependency graph encoded directly within the snapshot is the reverse of
// the graph that we actually want to operate upon. Edges in the snapshot graph
// originate in a resource and go to that resource's dependencies.
//
// The `DependingOn` is simpler when operating on the reverse of the snapshot graph,
// where edges originate in a resource and go to resources that depend on that resource.
// In this graph, `DependingOn` for a resource is the set of resources that are reachable from the
// given resource.
//
// To accomplish this without building up an entire graph data structure, we'll do a linear
// scan of the resource list starting at the requested resource and ending at the end of
// the list. All resources that depend directly or indirectly on `res` are prepended
// onto `dependents`.
for i := cursorIndex + 1; i < len(dg.resources); i++ {
cursorResource := dg.resources[i]
for _, dependency := range cursorResource.Dependencies {
if dependentSet[dependency] {
dependents = append(dependents, cursorResource)
dependentSet[cursorResource.URN] = true
break
}
}
}
return dependents
}
// NewDependencyGraph creates a new DependencyGraph from a list of resources.
// The resources should be in topological order with respect to their dependencies.
func NewDependencyGraph(resources []*resource.State) *DependencyGraph {
index := make(map[*resource.State]int)
for idx, res := range resources {
index[res] = idx
}
return &DependencyGraph{index, resources}
}