terraform: connect providers in the apply graph
This commit is contained in:
parent
77b9177bd5
commit
dc9b9eee44
|
@ -28,6 +28,11 @@ type Graph struct {
|
||||||
// RootModuleName
|
// RootModuleName
|
||||||
Path []string
|
Path []string
|
||||||
|
|
||||||
|
// annotations are the annotations that are added to vertices. Annotations
|
||||||
|
// are arbitrary metadata taht is used for various logic. Annotations
|
||||||
|
// should have unique keys that are referenced via constants.
|
||||||
|
annotations map[dag.Vertex]map[string]interface{}
|
||||||
|
|
||||||
// dependableMap is a lookaside table for fast lookups for connecting
|
// dependableMap is a lookaside table for fast lookups for connecting
|
||||||
// dependencies by their GraphNodeDependable value to avoid O(n^3)-like
|
// dependencies by their GraphNodeDependable value to avoid O(n^3)-like
|
||||||
// situations and turn them into O(1) with respect to the number of new
|
// situations and turn them into O(1) with respect to the number of new
|
||||||
|
@ -37,6 +42,29 @@ type Graph struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Annotations returns the annotations that are configured for the
|
||||||
|
// given vertex. The map is guaranteed to be non-nil but may be empty.
|
||||||
|
//
|
||||||
|
// The returned map may be modified to modify the annotations of the
|
||||||
|
// vertex.
|
||||||
|
func (g *Graph) Annotations(v dag.Vertex) map[string]interface{} {
|
||||||
|
g.once.Do(g.init)
|
||||||
|
|
||||||
|
// If this vertex isn't in the graph, then just return an empty map
|
||||||
|
if !g.HasVertex(v) {
|
||||||
|
return map[string]interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the map, if it doesn't exist yet then initialize it
|
||||||
|
m, ok := g.annotations[v]
|
||||||
|
if !ok {
|
||||||
|
m = make(map[string]interface{})
|
||||||
|
g.annotations[v] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
// Add is the same as dag.Graph.Add.
|
// Add is the same as dag.Graph.Add.
|
||||||
func (g *Graph) Add(v dag.Vertex) dag.Vertex {
|
func (g *Graph) Add(v dag.Vertex) dag.Vertex {
|
||||||
g.once.Do(g.init)
|
g.once.Do(g.init)
|
||||||
|
@ -51,6 +79,14 @@ func (g *Graph) Add(v dag.Vertex) dag.Vertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this initializes annotations, then do that
|
||||||
|
if av, ok := v.(GraphNodeAnnotationInit); ok {
|
||||||
|
as := g.Annotations(v)
|
||||||
|
for k, v := range av.AnnotationInit() {
|
||||||
|
as[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +101,17 @@ func (g *Graph) Remove(v dag.Vertex) dag.Vertex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the annotations
|
||||||
|
delete(g.annotations, v)
|
||||||
|
|
||||||
// Call upwards to remove it from the actual graph
|
// Call upwards to remove it from the actual graph
|
||||||
return g.Graph.Remove(v)
|
return g.Graph.Remove(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace is the same as dag.Graph.Replace
|
// Replace is the same as dag.Graph.Replace
|
||||||
func (g *Graph) Replace(o, n dag.Vertex) bool {
|
func (g *Graph) Replace(o, n dag.Vertex) bool {
|
||||||
|
g.once.Do(g.init)
|
||||||
|
|
||||||
// Go through and update our lookaside to point to the new vertex
|
// Go through and update our lookaside to point to the new vertex
|
||||||
for k, v := range g.dependableMap {
|
for k, v := range g.dependableMap {
|
||||||
if v == o {
|
if v == o {
|
||||||
|
@ -82,6 +123,12 @@ func (g *Graph) Replace(o, n dag.Vertex) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move the annotation if it exists
|
||||||
|
if m, ok := g.annotations[o]; ok {
|
||||||
|
g.annotations[n] = m
|
||||||
|
delete(g.annotations, o)
|
||||||
|
}
|
||||||
|
|
||||||
return g.Graph.Replace(o, n)
|
return g.Graph.Replace(o, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +200,10 @@ func (g *Graph) Walk(walker GraphWalker) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graph) init() {
|
func (g *Graph) init() {
|
||||||
|
if g.annotations == nil {
|
||||||
|
g.annotations = make(map[dag.Vertex]map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
if g.dependableMap == nil {
|
if g.dependableMap == nil {
|
||||||
g.dependableMap = make(map[string]dag.Vertex)
|
g.dependableMap = make(map[string]dag.Vertex)
|
||||||
}
|
}
|
||||||
|
@ -237,6 +288,16 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||||
return g.AcyclicGraph.Walk(walkFn)
|
return g.AcyclicGraph.Walk(walkFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphNodeAnnotationInit is an interface that allows a node to
|
||||||
|
// initialize it's annotations.
|
||||||
|
//
|
||||||
|
// AnnotationInit will be called _once_ when the node is added to a
|
||||||
|
// graph for the first time and is expected to return it's initial
|
||||||
|
// annotations.
|
||||||
|
type GraphNodeAnnotationInit interface {
|
||||||
|
AnnotationInit() map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
// GraphNodeDependable is an interface which says that a node can be
|
// GraphNodeDependable is an interface which says that a node can be
|
||||||
// depended on (an edge can be placed between this node and another) according
|
// depended on (an edge can be placed between this node and another) according
|
||||||
// to the well-known name returned by DependableName.
|
// to the well-known name returned by DependableName.
|
||||||
|
|
|
@ -39,6 +39,10 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||||
// Creates all the nodes represented in the diff.
|
// Creates all the nodes represented in the diff.
|
||||||
&DiffTransformer{Diff: b.Diff},
|
&DiffTransformer{Diff: b.Diff},
|
||||||
|
|
||||||
|
// Create all the providers
|
||||||
|
&MissingProviderTransformer{Providers: b.Providers},
|
||||||
|
&ProviderTransformer{},
|
||||||
|
|
||||||
// Single root
|
// Single root
|
||||||
&RootTransformer{},
|
&RootTransformer{},
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ func TestApplyGraphBuilder(t *testing.T) {
|
||||||
|
|
||||||
b := &ApplyGraphBuilder{
|
b := &ApplyGraphBuilder{
|
||||||
Diff: diff,
|
Diff: diff,
|
||||||
|
Providers: []string{"aws"},
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := b.Build(RootModulePath)
|
g, err := b.Build(RootModulePath)
|
||||||
|
@ -54,4 +55,6 @@ func TestApplyGraphBuilder(t *testing.T) {
|
||||||
|
|
||||||
const testApplyGraphBuilderStr = `
|
const testApplyGraphBuilderStr = `
|
||||||
aws_instance.create
|
aws_instance.create
|
||||||
|
provider.aws
|
||||||
|
provider.aws
|
||||||
`
|
`
|
||||||
|
|
|
@ -1,10 +1,33 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
// NodeResource is a graph node for referencing a resource.
|
import (
|
||||||
type NodeResource struct {
|
"github.com/hashicorp/terraform/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NodeApplyableResource represents a resource that is "applyable":
|
||||||
|
// it is ready to be applied and is represented by a diff.
|
||||||
|
type NodeApplyableResource struct {
|
||||||
Addr *ResourceAddress // Addr is the address for this resource
|
Addr *ResourceAddress // Addr is the address for this resource
|
||||||
|
Config *config.Resource // Config is the resource in the config
|
||||||
|
ResourceState *ResourceState // ResourceState is the ResourceState for this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NodeResource) Name() string {
|
func (n *NodeApplyableResource) Name() string {
|
||||||
return n.Addr.String()
|
return n.Addr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphNodeProviderConsumer
|
||||||
|
func (n *NodeApplyableResource) ProvidedBy() []string {
|
||||||
|
// If we have a config we prefer that above all else
|
||||||
|
if n.Config != nil {
|
||||||
|
return []string{resourceProvider(n.Config.Type, n.Config.Provider)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have state, then we will use the provider from there
|
||||||
|
if n.ResourceState != nil {
|
||||||
|
return []string{n.ResourceState.Provider}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use our type
|
||||||
|
return []string{resourceProvider(n.Addr.Type, "")}
|
||||||
|
}
|
||||||
|
|
|
@ -85,6 +85,17 @@ func (r *ResourceAddress) String() string {
|
||||||
return strings.Join(result, ".")
|
return strings.Join(result, ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseResourceAddressConfig creates a resource address from a config.Resource
|
||||||
|
func parseResourceAddressConfig(r *config.Resource) (*ResourceAddress, error) {
|
||||||
|
return &ResourceAddress{
|
||||||
|
Type: r.Type,
|
||||||
|
Name: r.Name,
|
||||||
|
Index: -1,
|
||||||
|
InstanceType: TypePrimary,
|
||||||
|
Mode: r.Mode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// parseResourceAddressInternal parses the somewhat bespoke resource
|
// parseResourceAddressInternal parses the somewhat bespoke resource
|
||||||
// identifier used in states and diffs, such as "instance.name.0".
|
// identifier used in states and diffs, such as "instance.name.0".
|
||||||
func parseResourceAddressInternal(s string) (*ResourceAddress, error) {
|
func parseResourceAddressInternal(s string) (*ResourceAddress, error) {
|
||||||
|
@ -101,6 +112,7 @@ func parseResourceAddressInternal(s string) (*ResourceAddress, error) {
|
||||||
Name: parts[1],
|
Name: parts[1],
|
||||||
Index: -1,
|
Index: -1,
|
||||||
InstanceType: TypePrimary,
|
InstanceType: TypePrimary,
|
||||||
|
Mode: config.ManagedResourceMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have more parts, then we have an index. Parse that.
|
// If we have more parts, then we have an index. Parse that.
|
||||||
|
|
|
@ -2,6 +2,8 @@ package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/config/module"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DiffTransformer is a GraphTransformer that adds the elements of
|
// DiffTransformer is a GraphTransformer that adds the elements of
|
||||||
|
@ -9,8 +11,16 @@ import (
|
||||||
//
|
//
|
||||||
// This transform is used for example by the ApplyGraphBuilder to ensure
|
// This transform is used for example by the ApplyGraphBuilder to ensure
|
||||||
// that only resources that are being modified are represented in the graph.
|
// that only resources that are being modified are represented in the graph.
|
||||||
|
//
|
||||||
|
// Config and State is still required for the DiffTransformer for annotations
|
||||||
|
// since the Diff doesn't contain all the information required to build the
|
||||||
|
// complete graph (such as create-before-destroy information). The graph
|
||||||
|
// is built based on the diff first, though, ensuring that only resources
|
||||||
|
// that are being modified are present in the graph.
|
||||||
type DiffTransformer struct {
|
type DiffTransformer struct {
|
||||||
Diff *Diff
|
Diff *Diff
|
||||||
|
Config *module.Tree
|
||||||
|
State *State
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *DiffTransformer) Transform(g *Graph) error {
|
func (t *DiffTransformer) Transform(g *Graph) error {
|
||||||
|
@ -20,6 +30,7 @@ func (t *DiffTransformer) Transform(g *Graph) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through all the modules in the diff.
|
// Go through all the modules in the diff.
|
||||||
|
var nodes []*NodeApplyableResource
|
||||||
for _, m := range t.Diff.Modules {
|
for _, m := range t.Diff.Modules {
|
||||||
// TODO: If this is a destroy diff then add a module destroy node
|
// TODO: If this is a destroy diff then add a module destroy node
|
||||||
|
|
||||||
|
@ -38,16 +49,75 @@ func (t *DiffTransformer) Transform(g *Graph) error {
|
||||||
// reference this resource.
|
// reference this resource.
|
||||||
addr, err := parseResourceAddressInternal(name)
|
addr, err := parseResourceAddressInternal(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
panic(fmt.Sprintf(
|
||||||
"Error parsing internal name, this is a bug: %q", name)
|
"Error parsing internal name, this is a bug: %q", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Very important: add the module path for this resource to
|
||||||
|
// the address. Remove "root" from it.
|
||||||
|
addr.Path = m.Path[1:]
|
||||||
|
|
||||||
// Add the resource to the graph
|
// Add the resource to the graph
|
||||||
g.Add(&NodeResource{
|
nodes = append(nodes, &NodeApplyableResource{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Lots of room for performance optimizations below. For
|
||||||
|
// resource-heavy diffs this part alone is probably pretty slow.
|
||||||
|
|
||||||
|
// Annotate all nodes with their config and state
|
||||||
|
for _, n := range nodes {
|
||||||
|
// Grab the configuration at this path.
|
||||||
|
if t := t.Config.Child(n.Addr.Path); t != nil {
|
||||||
|
for _, r := range t.Config().Resources {
|
||||||
|
// Get a resource address so we can compare
|
||||||
|
addr, err := parseResourceAddressConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf(
|
||||||
|
"Error parsing config address, this is a bug: %#v", r))
|
||||||
|
}
|
||||||
|
addr.Path = n.Addr.Path
|
||||||
|
|
||||||
|
// If this is not the same resource, then continue
|
||||||
|
if !addr.Equals(n.Addr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same resource! Mark it and exit
|
||||||
|
n.Config = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the state at this path
|
||||||
|
if ms := t.State.ModuleByPath(normalizeModulePath(n.Addr.Path)); ms != nil {
|
||||||
|
for name, rs := range ms.Resources {
|
||||||
|
// Parse the name for comparison
|
||||||
|
addr, err := parseResourceAddressInternal(name)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf(
|
||||||
|
"Error parsing internal name, this is a bug: %q", name))
|
||||||
|
}
|
||||||
|
addr.Path = n.Addr.Path
|
||||||
|
|
||||||
|
// If this is not the same resource, then continue
|
||||||
|
if !addr.Equals(n.Addr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same resource!
|
||||||
|
n.ResourceState = rs
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all the nodes to the graph
|
||||||
|
for _, n := range nodes {
|
||||||
|
g.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue