* Make `async:true` the default for `invoke` calls (#3750) * Switch away from native grpc impl. (#3728) * Remove usage of the 'deasync' library from @pulumi/pulumi. (#3752) * Only retry as long as we get unavailable back. Anything else continues. (#3769) * Handle all errors for now. (#3781) * Do not assume --yes was present when using pulumi in non-interactive mode (#3793) * Upgrade all paths for sdk and pkg to v2 * Backport C# invoke classes and other recent gen changes (#4288) Adjust C# generation * Replace IDeployment with a sealed class (#4318) Replace IDeployment with a sealed class * .NET: default to args subtype rather than Args.Empty (#4320) * Adding system namespace for Dotnet code gen This is required for using Obsolute attributes for deprecations ``` Iam/InstanceProfile.cs(142,10): error CS0246: The type or namespace name 'ObsoleteAttribute' could not be found (are you missing a using directive or an assembly reference?) [/Users/stack72/code/go/src/github.com/pulumi/pulumi-aws/sdk/dotnet/Pulumi.Aws.csproj] Iam/InstanceProfile.cs(142,10): error CS0246: The type or namespace name 'Obsolete' could not be found (are you missing a using directive or an assembly reference?) [/Users/stack72/code/go/src/github.com/pulumi/pulumi-aws/sdk/dotnet/Pulumi.Aws.csproj] ``` * Fix the nullability of config type properties in C# codegen (#4379)
112 lines
3.8 KiB
Go
112 lines
3.8 KiB
Go
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.
|
|
|
|
package graph
|
|
|
|
import (
|
|
"github.com/pulumi/pulumi/pkg/v2/resource/deploy/providers"
|
|
"github.com/pulumi/pulumi/sdk/v2/go/common/resource"
|
|
"github.com/pulumi/pulumi/sdk/v2/go/common/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, ignore map[resource.URN]bool) []*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
|
|
|
|
isDependent := func(candidate *resource.State) bool {
|
|
if ignore[candidate.URN] {
|
|
return false
|
|
}
|
|
if candidate.Provider != "" {
|
|
ref, err := providers.ParseReference(candidate.Provider)
|
|
contract.Assert(err == nil)
|
|
if dependentSet[ref.URN()] {
|
|
return true
|
|
}
|
|
}
|
|
for _, dependency := range candidate.Dependencies {
|
|
if dependentSet[dependency] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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++ {
|
|
candidate := dg.resources[i]
|
|
if isDependent(candidate) {
|
|
dependents = append(dependents, candidate)
|
|
dependentSet[candidate.URN] = true
|
|
}
|
|
}
|
|
|
|
return dependents
|
|
}
|
|
|
|
// DependenciesOf returns a ResourceSet of resources upon which the given resource depends. The resource's parent is
|
|
// included in the returned set.
|
|
func (dg *DependencyGraph) DependenciesOf(res *resource.State) ResourceSet {
|
|
set := make(ResourceSet)
|
|
|
|
dependentUrns := make(map[resource.URN]bool)
|
|
for _, dep := range res.Dependencies {
|
|
dependentUrns[dep] = true
|
|
}
|
|
|
|
if res.Provider != "" {
|
|
ref, err := providers.ParseReference(res.Provider)
|
|
contract.Assert(err == nil)
|
|
dependentUrns[ref.URN()] = true
|
|
}
|
|
|
|
cursorIndex, ok := dg.index[res]
|
|
contract.Assert(ok)
|
|
for i := cursorIndex - 1; i >= 0; i-- {
|
|
candidate := dg.resources[i]
|
|
if dependentUrns[candidate.URN] || candidate.URN == res.Parent {
|
|
set[candidate] = true
|
|
}
|
|
}
|
|
|
|
return set
|
|
}
|
|
|
|
// 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}
|
|
}
|