github.com/tarrant/terraform@v0.3.8-0.20150402012457-f68c9eee638e/terraform/transform_orphan.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/terraform/config"
     8  	"github.com/hashicorp/terraform/config/module"
     9  	"github.com/hashicorp/terraform/dag"
    10  )
    11  
    12  // GraphNodeStateRepresentative is an interface that can be implemented by
    13  // a node to say that it is representing a resource in the state.
    14  type GraphNodeStateRepresentative interface {
    15  	StateId() []string
    16  }
    17  
    18  // OrphanTransformer is a GraphTransformer that adds orphans to the
    19  // graph. This transformer adds both resource and module orphans.
    20  type OrphanTransformer struct {
    21  	// State is the global state. We require the global state to
    22  	// properly find module orphans at our path.
    23  	State *State
    24  
    25  	// Module is the root module. We'll look up the proper configuration
    26  	// using the graph path.
    27  	Module *module.Tree
    28  
    29  	// Targets are user-specified resources to target. We need to be aware of
    30  	// these so we don't improperly identify orphans when they've just been
    31  	// filtered out of the graph via targeting.
    32  	Targeting bool
    33  
    34  	// View, if non-nil will set a view on the module state.
    35  	View string
    36  }
    37  
    38  func (t *OrphanTransformer) Transform(g *Graph) error {
    39  	if t.State == nil {
    40  		// If the entire state is nil, there can't be any orphans
    41  		return nil
    42  	}
    43  
    44  	if t.Targeting {
    45  		log.Printf("Skipping orphan transformer because we have targets.")
    46  		// If we are in a run where we are targeting nodes, we won't process
    47  		// orphans for this run.
    48  		return nil
    49  	}
    50  
    51  	// Build up all our state representatives
    52  	resourceRep := make(map[string]struct{})
    53  	for _, v := range g.Vertices() {
    54  		if sr, ok := v.(GraphNodeStateRepresentative); ok {
    55  			for _, k := range sr.StateId() {
    56  				resourceRep[k] = struct{}{}
    57  			}
    58  		}
    59  	}
    60  
    61  	var config *config.Config
    62  	if t.Module != nil {
    63  		if module := t.Module.Child(g.Path[1:]); module != nil {
    64  			config = module.Config()
    65  		}
    66  	}
    67  
    68  	var resourceVertexes []dag.Vertex
    69  	if state := t.State.ModuleByPath(g.Path); state != nil {
    70  		// If we have state, then we can have orphan resources
    71  
    72  		// If we have a view, get the view
    73  		if t.View != "" {
    74  			state = state.View(t.View)
    75  		}
    76  
    77  		// Go over each resource orphan and add it to the graph.
    78  		resourceOrphans := state.Orphans(config)
    79  		resourceVertexes = make([]dag.Vertex, len(resourceOrphans))
    80  		for i, k := range resourceOrphans {
    81  			// If this orphan is represented by some other node somehow,
    82  			// then ignore it.
    83  			if _, ok := resourceRep[k]; ok {
    84  				continue
    85  			}
    86  
    87  			rs := state.Resources[k]
    88  
    89  			resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
    90  				ResourceName: k,
    91  				ResourceType: rs.Type,
    92  				dependentOn:  rs.Dependencies,
    93  			})
    94  		}
    95  	}
    96  
    97  	// Go over each module orphan and add it to the graph. We store the
    98  	// vertexes and states outside so that we can connect dependencies later.
    99  	moduleOrphans := t.State.ModuleOrphans(g.Path, config)
   100  	moduleVertexes := make([]dag.Vertex, len(moduleOrphans))
   101  	for i, path := range moduleOrphans {
   102  		moduleVertexes[i] = g.Add(&graphNodeOrphanModule{
   103  			Path:        path,
   104  			dependentOn: t.State.ModuleByPath(path).Dependencies,
   105  		})
   106  	}
   107  
   108  	// Now do the dependencies. We do this _after_ adding all the orphan
   109  	// nodes above because there are cases in which the orphans themselves
   110  	// depend on other orphans.
   111  
   112  	// Resource dependencies
   113  	for _, v := range resourceVertexes {
   114  		g.ConnectDependent(v)
   115  	}
   116  
   117  	// Module dependencies
   118  	for _, v := range moduleVertexes {
   119  		g.ConnectDependent(v)
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  // graphNodeOrphanModule is the graph vertex representing an orphan resource..
   126  type graphNodeOrphanModule struct {
   127  	Path []string
   128  
   129  	dependentOn []string
   130  }
   131  
   132  func (n *graphNodeOrphanModule) DependableName() []string {
   133  	return []string{n.dependableName()}
   134  }
   135  
   136  func (n *graphNodeOrphanModule) DependentOn() []string {
   137  	return n.dependentOn
   138  }
   139  
   140  func (n *graphNodeOrphanModule) Name() string {
   141  	return fmt.Sprintf("%s (orphan)", n.dependableName())
   142  }
   143  
   144  func (n *graphNodeOrphanModule) dependableName() string {
   145  	return fmt.Sprintf("module.%s", n.Path[len(n.Path)-1])
   146  }
   147  
   148  // GraphNodeExpandable
   149  func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) {
   150  	g, err := b.Build(n.Path)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	return &GraphNodeBasicSubgraph{
   156  		NameValue: n.Name(),
   157  		Graph:     g,
   158  	}, nil
   159  }
   160  
   161  // graphNodeOrphanResource is the graph vertex representing an orphan resource..
   162  type graphNodeOrphanResource struct {
   163  	ResourceName string
   164  	ResourceType string
   165  
   166  	dependentOn []string
   167  }
   168  
   169  func (n *graphNodeOrphanResource) DependableName() []string {
   170  	return []string{n.dependableName()}
   171  }
   172  
   173  func (n *graphNodeOrphanResource) DependentOn() []string {
   174  	return n.dependentOn
   175  }
   176  
   177  func (n *graphNodeOrphanResource) Name() string {
   178  	return fmt.Sprintf("%s (orphan)", n.ResourceName)
   179  }
   180  
   181  func (n *graphNodeOrphanResource) ProvidedBy() []string {
   182  	return []string{resourceProvider(n.ResourceName)}
   183  }
   184  
   185  // GraphNodeEvalable impl.
   186  func (n *graphNodeOrphanResource) EvalTree() EvalNode {
   187  	var provider ResourceProvider
   188  	var state *InstanceState
   189  
   190  	seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
   191  
   192  	// Build instance info
   193  	info := &InstanceInfo{Id: n.ResourceName, Type: n.ResourceType}
   194  	seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
   195  
   196  	// Refresh the resource
   197  	seq.Nodes = append(seq.Nodes, &EvalOpFilter{
   198  		Ops: []walkOperation{walkRefresh},
   199  		Node: &EvalSequence{
   200  			Nodes: []EvalNode{
   201  				&EvalGetProvider{
   202  					Name:   n.ProvidedBy()[0],
   203  					Output: &provider,
   204  				},
   205  				&EvalReadState{
   206  					Name:   n.ResourceName,
   207  					Output: &state,
   208  				},
   209  				&EvalRefresh{
   210  					Info:     info,
   211  					Provider: &provider,
   212  					State:    &state,
   213  					Output:   &state,
   214  				},
   215  				&EvalWriteState{
   216  					Name:         n.ResourceName,
   217  					ResourceType: n.ResourceType,
   218  					Dependencies: n.DependentOn(),
   219  					State:        &state,
   220  				},
   221  			},
   222  		},
   223  	})
   224  
   225  	// Diff the resource
   226  	var diff *InstanceDiff
   227  	seq.Nodes = append(seq.Nodes, &EvalOpFilter{
   228  		Ops: []walkOperation{walkPlan, walkPlanDestroy},
   229  		Node: &EvalSequence{
   230  			Nodes: []EvalNode{
   231  				&EvalReadState{
   232  					Name:   n.ResourceName,
   233  					Output: &state,
   234  				},
   235  				&EvalDiffDestroy{
   236  					Info:   info,
   237  					State:  &state,
   238  					Output: &diff,
   239  				},
   240  				&EvalWriteDiff{
   241  					Name: n.ResourceName,
   242  					Diff: &diff,
   243  				},
   244  			},
   245  		},
   246  	})
   247  
   248  	// Apply
   249  	seq.Nodes = append(seq.Nodes, &EvalOpFilter{
   250  		Ops: []walkOperation{walkApply},
   251  		Node: &EvalSequence{
   252  			Nodes: []EvalNode{
   253  				&EvalReadDiff{
   254  					Name: n.ResourceName,
   255  					Diff: &diff,
   256  				},
   257  				&EvalGetProvider{
   258  					Name:   n.ProvidedBy()[0],
   259  					Output: &provider,
   260  				},
   261  				&EvalReadState{
   262  					Name:   n.ResourceName,
   263  					Output: &state,
   264  				},
   265  				&EvalApply{
   266  					Info:     info,
   267  					State:    &state,
   268  					Diff:     &diff,
   269  					Provider: &provider,
   270  					Output:   &state,
   271  				},
   272  				&EvalWriteState{
   273  					Name:         n.ResourceName,
   274  					ResourceType: n.ResourceType,
   275  					Dependencies: n.DependentOn(),
   276  					State:        &state,
   277  				},
   278  				&EvalUpdateStateHook{},
   279  			},
   280  		},
   281  	})
   282  
   283  	return seq
   284  }
   285  
   286  func (n *graphNodeOrphanResource) dependableName() string {
   287  	return n.ResourceName
   288  }