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