github.com/opsidian/terraform@v0.7.8-0.20161104123224-27c39cdfba5b/terraform/transform_orphan.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/config"
     7  	"github.com/hashicorp/terraform/config/module"
     8  	"github.com/hashicorp/terraform/dag"
     9  )
    10  
    11  // GraphNodeStateRepresentative is an interface that can be implemented by
    12  // a node to say that it is representing a resource in the state.
    13  type GraphNodeStateRepresentative interface {
    14  	StateId() []string
    15  }
    16  
    17  // OrphanTransformer is a GraphTransformer that adds orphans to the
    18  // graph. This transformer adds both resource and module orphans.
    19  type OrphanTransformer struct {
    20  	// Resource is resource configuration. This is only non-nil when
    21  	// expanding a resource that is in the configuration. It can't be
    22  	// dependend on.
    23  	Resource *config.Resource
    24  
    25  	// State is the global state. We require the global state to
    26  	// properly find module orphans at our path.
    27  	State *State
    28  
    29  	// Module is the root module. We'll look up the proper configuration
    30  	// using the graph path.
    31  	Module *module.Tree
    32  
    33  	// View, if non-nil will set a view on the module state.
    34  	View string
    35  }
    36  
    37  func (t *OrphanTransformer) Transform(g *Graph) error {
    38  	if t.State == nil {
    39  		// If the entire state is nil, there can't be any orphans
    40  		return nil
    41  	}
    42  
    43  	// Build up all our state representatives
    44  	resourceRep := make(map[string]struct{})
    45  	for _, v := range g.Vertices() {
    46  		if sr, ok := v.(GraphNodeStateRepresentative); ok {
    47  			for _, k := range sr.StateId() {
    48  				resourceRep[k] = struct{}{}
    49  			}
    50  		}
    51  	}
    52  
    53  	var config *config.Config
    54  	if t.Module != nil {
    55  		if module := t.Module.Child(g.Path[1:]); module != nil {
    56  			config = module.Config()
    57  		}
    58  	}
    59  
    60  	var resourceVertexes []dag.Vertex
    61  	if state := t.State.ModuleByPath(g.Path); state != nil {
    62  		// If we have state, then we can have orphan resources
    63  
    64  		// If we have a view, get the view
    65  		if t.View != "" {
    66  			state = state.View(t.View)
    67  		}
    68  
    69  		resourceOrphans := state.Orphans(config)
    70  
    71  		resourceVertexes = make([]dag.Vertex, len(resourceOrphans))
    72  		for i, k := range resourceOrphans {
    73  			// If this orphan is represented by some other node somehow,
    74  			// then ignore it.
    75  			if _, ok := resourceRep[k]; ok {
    76  				continue
    77  			}
    78  
    79  			rs := state.Resources[k]
    80  
    81  			rsk, err := ParseResourceStateKey(k)
    82  			if err != nil {
    83  				return err
    84  			}
    85  			resourceVertexes[i] = g.Add(&graphNodeOrphanResource{
    86  				Path:        g.Path,
    87  				ResourceKey: rsk,
    88  				Resource:    t.Resource,
    89  				Provider:    rs.Provider,
    90  				dependentOn: rs.Dependencies,
    91  			})
    92  		}
    93  	}
    94  
    95  	// Go over each module orphan and add it to the graph. We store the
    96  	// vertexes and states outside so that we can connect dependencies later.
    97  	moduleOrphans := t.State.ModuleOrphans(g.Path, config)
    98  	moduleVertexes := make([]dag.Vertex, len(moduleOrphans))
    99  	for i, path := range moduleOrphans {
   100  		var deps []string
   101  		if s := t.State.ModuleByPath(path); s != nil {
   102  			deps = s.Dependencies
   103  		}
   104  
   105  		moduleVertexes[i] = g.Add(&graphNodeOrphanModule{
   106  			Path:        path,
   107  			dependentOn: deps,
   108  		})
   109  	}
   110  
   111  	// Now do the dependencies. We do this _after_ adding all the orphan
   112  	// nodes above because there are cases in which the orphans themselves
   113  	// depend on other orphans.
   114  
   115  	// Resource dependencies
   116  	for _, v := range resourceVertexes {
   117  		g.ConnectDependent(v)
   118  	}
   119  
   120  	// Module dependencies
   121  	for _, v := range moduleVertexes {
   122  		g.ConnectDependent(v)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // graphNodeOrphanModule is the graph vertex representing an orphan resource..
   129  type graphNodeOrphanModule struct {
   130  	Path []string
   131  
   132  	dependentOn []string
   133  }
   134  
   135  func (n *graphNodeOrphanModule) DependableName() []string {
   136  	return []string{n.dependableName()}
   137  }
   138  
   139  func (n *graphNodeOrphanModule) DependentOn() []string {
   140  	return n.dependentOn
   141  }
   142  
   143  func (n *graphNodeOrphanModule) Name() string {
   144  	return fmt.Sprintf("%s (orphan)", n.dependableName())
   145  }
   146  
   147  func (n *graphNodeOrphanModule) dependableName() string {
   148  	return fmt.Sprintf("module.%s", n.Path[len(n.Path)-1])
   149  }
   150  
   151  // GraphNodeExpandable
   152  func (n *graphNodeOrphanModule) Expand(b GraphBuilder) (GraphNodeSubgraph, error) {
   153  	g, err := b.Build(n.Path)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	return &GraphNodeBasicSubgraph{
   159  		NameValue: n.Name(),
   160  		Graph:     g,
   161  	}, nil
   162  }
   163  
   164  // graphNodeOrphanResource is the graph vertex representing an orphan resource..
   165  type graphNodeOrphanResource struct {
   166  	Path        []string
   167  	ResourceKey *ResourceStateKey
   168  	Resource    *config.Resource
   169  	Provider    string
   170  
   171  	dependentOn []string
   172  }
   173  
   174  func (n *graphNodeOrphanResource) ConfigType() GraphNodeConfigType {
   175  	return GraphNodeConfigTypeResource
   176  }
   177  
   178  func (n *graphNodeOrphanResource) ResourceAddress() *ResourceAddress {
   179  	return &ResourceAddress{
   180  		Index:        n.ResourceKey.Index,
   181  		InstanceType: TypePrimary,
   182  		Name:         n.ResourceKey.Name,
   183  		Path:         n.Path[1:],
   184  		Type:         n.ResourceKey.Type,
   185  		Mode:         n.ResourceKey.Mode,
   186  	}
   187  }
   188  
   189  func (n *graphNodeOrphanResource) DependableName() []string {
   190  	return []string{n.dependableName()}
   191  }
   192  
   193  func (n *graphNodeOrphanResource) DependentOn() []string {
   194  	return n.dependentOn
   195  }
   196  
   197  func (n *graphNodeOrphanResource) Flatten(p []string) (dag.Vertex, error) {
   198  	return &graphNodeOrphanResourceFlat{
   199  		graphNodeOrphanResource: n,
   200  		PathValue:               p,
   201  	}, nil
   202  }
   203  
   204  func (n *graphNodeOrphanResource) Name() string {
   205  	return fmt.Sprintf("%s (orphan)", n.ResourceKey)
   206  }
   207  
   208  func (n *graphNodeOrphanResource) ProvidedBy() []string {
   209  	return []string{resourceProvider(n.ResourceKey.Type, n.Provider)}
   210  }
   211  
   212  // GraphNodeEvalable impl.
   213  func (n *graphNodeOrphanResource) EvalTree() EvalNode {
   214  
   215  	seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
   216  
   217  	// Build instance info
   218  	info := &InstanceInfo{Id: n.ResourceKey.String(), Type: n.ResourceKey.Type}
   219  	info.uniqueExtra = "destroy"
   220  
   221  	seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
   222  
   223  	// Each resource mode has its own lifecycle
   224  	switch n.ResourceKey.Mode {
   225  	case config.ManagedResourceMode:
   226  		seq.Nodes = append(
   227  			seq.Nodes,
   228  			n.managedResourceEvalNodes(info)...,
   229  		)
   230  	case config.DataResourceMode:
   231  		seq.Nodes = append(
   232  			seq.Nodes,
   233  			n.dataResourceEvalNodes(info)...,
   234  		)
   235  	default:
   236  		panic(fmt.Errorf("unsupported resource mode %s", n.ResourceKey.Mode))
   237  	}
   238  
   239  	return seq
   240  }
   241  
   242  func (n *graphNodeOrphanResource) managedResourceEvalNodes(info *InstanceInfo) []EvalNode {
   243  	var provider ResourceProvider
   244  	var state *InstanceState
   245  
   246  	nodes := make([]EvalNode, 0, 3)
   247  
   248  	// Refresh the resource
   249  	nodes = append(nodes, &EvalOpFilter{
   250  		Ops: []walkOperation{walkRefresh},
   251  		Node: &EvalSequence{
   252  			Nodes: []EvalNode{
   253  				&EvalGetProvider{
   254  					Name:   n.ProvidedBy()[0],
   255  					Output: &provider,
   256  				},
   257  				&EvalReadState{
   258  					Name:   n.ResourceKey.String(),
   259  					Output: &state,
   260  				},
   261  				&EvalRefresh{
   262  					Info:     info,
   263  					Provider: &provider,
   264  					State:    &state,
   265  					Output:   &state,
   266  				},
   267  				&EvalWriteState{
   268  					Name:         n.ResourceKey.String(),
   269  					ResourceType: n.ResourceKey.Type,
   270  					Provider:     n.Provider,
   271  					Dependencies: n.DependentOn(),
   272  					State:        &state,
   273  				},
   274  			},
   275  		},
   276  	})
   277  
   278  	// Diff the resource
   279  	var diff *InstanceDiff
   280  	nodes = append(nodes, &EvalOpFilter{
   281  		Ops: []walkOperation{walkPlan, walkPlanDestroy},
   282  		Node: &EvalSequence{
   283  			Nodes: []EvalNode{
   284  				&EvalReadState{
   285  					Name:   n.ResourceKey.String(),
   286  					Output: &state,
   287  				},
   288  				&EvalDiffDestroy{
   289  					Info:   info,
   290  					State:  &state,
   291  					Output: &diff,
   292  				},
   293  				&EvalCheckPreventDestroy{
   294  					Resource:   n.Resource,
   295  					ResourceId: n.ResourceKey.String(),
   296  					Diff:       &diff,
   297  				},
   298  				&EvalWriteDiff{
   299  					Name: n.ResourceKey.String(),
   300  					Diff: &diff,
   301  				},
   302  			},
   303  		},
   304  	})
   305  
   306  	// Apply
   307  	var err error
   308  	nodes = append(nodes, &EvalOpFilter{
   309  		Ops: []walkOperation{walkApply, walkDestroy},
   310  		Node: &EvalSequence{
   311  			Nodes: []EvalNode{
   312  				&EvalReadDiff{
   313  					Name: n.ResourceKey.String(),
   314  					Diff: &diff,
   315  				},
   316  				&EvalGetProvider{
   317  					Name:   n.ProvidedBy()[0],
   318  					Output: &provider,
   319  				},
   320  				&EvalReadState{
   321  					Name:   n.ResourceKey.String(),
   322  					Output: &state,
   323  				},
   324  				&EvalApply{
   325  					Info:     info,
   326  					State:    &state,
   327  					Diff:     &diff,
   328  					Provider: &provider,
   329  					Output:   &state,
   330  					Error:    &err,
   331  				},
   332  				&EvalWriteState{
   333  					Name:         n.ResourceKey.String(),
   334  					ResourceType: n.ResourceKey.Type,
   335  					Provider:     n.Provider,
   336  					Dependencies: n.DependentOn(),
   337  					State:        &state,
   338  				},
   339  				&EvalApplyPost{
   340  					Info:  info,
   341  					State: &state,
   342  					Error: &err,
   343  				},
   344  				&EvalUpdateStateHook{},
   345  			},
   346  		},
   347  	})
   348  
   349  	return nodes
   350  }
   351  
   352  func (n *graphNodeOrphanResource) dataResourceEvalNodes(info *InstanceInfo) []EvalNode {
   353  	nodes := make([]EvalNode, 0, 3)
   354  
   355  	// This will remain nil, since we don't retain states for orphaned
   356  	// data resources.
   357  	var state *InstanceState
   358  
   359  	// On both refresh and apply we just drop our state altogether,
   360  	// since the config resource validation pass will have proven that the
   361  	// resources remaining in the configuration don't need it.
   362  	nodes = append(nodes, &EvalOpFilter{
   363  		Ops: []walkOperation{walkRefresh, walkApply},
   364  		Node: &EvalSequence{
   365  			Nodes: []EvalNode{
   366  				&EvalWriteState{
   367  					Name:         n.ResourceKey.String(),
   368  					ResourceType: n.ResourceKey.Type,
   369  					Provider:     n.Provider,
   370  					Dependencies: n.DependentOn(),
   371  					State:        &state, // state is nil
   372  				},
   373  			},
   374  		},
   375  	})
   376  
   377  	return nodes
   378  }
   379  
   380  func (n *graphNodeOrphanResource) dependableName() string {
   381  	return n.ResourceKey.String()
   382  }
   383  
   384  // GraphNodeDestroyable impl.
   385  func (n *graphNodeOrphanResource) DestroyNode() GraphNodeDestroy {
   386  	return n
   387  }
   388  
   389  // GraphNodeDestroy impl.
   390  func (n *graphNodeOrphanResource) CreateBeforeDestroy() bool {
   391  	return false
   392  }
   393  
   394  func (n *graphNodeOrphanResource) CreateNode() dag.Vertex {
   395  	return n
   396  }
   397  
   398  // Same as graphNodeOrphanResource, but for flattening
   399  type graphNodeOrphanResourceFlat struct {
   400  	*graphNodeOrphanResource
   401  
   402  	PathValue []string
   403  }
   404  
   405  func (n *graphNodeOrphanResourceFlat) Name() string {
   406  	return fmt.Sprintf(
   407  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeOrphanResource.Name())
   408  }
   409  
   410  func (n *graphNodeOrphanResourceFlat) Path() []string {
   411  	return n.PathValue
   412  }
   413  
   414  // GraphNodeDestroyable impl.
   415  func (n *graphNodeOrphanResourceFlat) DestroyNode() GraphNodeDestroy {
   416  	return n
   417  }
   418  
   419  // GraphNodeDestroy impl.
   420  func (n *graphNodeOrphanResourceFlat) CreateBeforeDestroy() bool {
   421  	return false
   422  }
   423  
   424  func (n *graphNodeOrphanResourceFlat) CreateNode() dag.Vertex {
   425  	return n
   426  }
   427  
   428  func (n *graphNodeOrphanResourceFlat) ProvidedBy() []string {
   429  	return modulePrefixList(
   430  		n.graphNodeOrphanResource.ProvidedBy(),
   431  		modulePrefixStr(n.PathValue))
   432  }