github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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  	seq.Nodes = append(seq.Nodes, &EvalInstanceInfo{Info: info})
   213  
   214  	// Each resource mode has its own lifecycle
   215  	switch n.ResourceKey.Mode {
   216  	case config.ManagedResourceMode:
   217  		seq.Nodes = append(
   218  			seq.Nodes,
   219  			n.managedResourceEvalNodes(info)...,
   220  		)
   221  	case config.DataResourceMode:
   222  		seq.Nodes = append(
   223  			seq.Nodes,
   224  			n.dataResourceEvalNodes(info)...,
   225  		)
   226  	default:
   227  		panic(fmt.Errorf("unsupported resource mode %s", n.ResourceKey.Mode))
   228  	}
   229  
   230  	return seq
   231  }
   232  
   233  func (n *graphNodeOrphanResource) managedResourceEvalNodes(info *InstanceInfo) []EvalNode {
   234  	var provider ResourceProvider
   235  	var state *InstanceState
   236  
   237  	nodes := make([]EvalNode, 0, 3)
   238  
   239  	// Refresh the resource
   240  	nodes = append(nodes, &EvalOpFilter{
   241  		Ops: []walkOperation{walkRefresh},
   242  		Node: &EvalSequence{
   243  			Nodes: []EvalNode{
   244  				&EvalGetProvider{
   245  					Name:   n.ProvidedBy()[0],
   246  					Output: &provider,
   247  				},
   248  				&EvalReadState{
   249  					Name:   n.ResourceKey.String(),
   250  					Output: &state,
   251  				},
   252  				&EvalRefresh{
   253  					Info:     info,
   254  					Provider: &provider,
   255  					State:    &state,
   256  					Output:   &state,
   257  				},
   258  				&EvalWriteState{
   259  					Name:         n.ResourceKey.String(),
   260  					ResourceType: n.ResourceKey.Type,
   261  					Provider:     n.Provider,
   262  					Dependencies: n.DependentOn(),
   263  					State:        &state,
   264  				},
   265  			},
   266  		},
   267  	})
   268  
   269  	// Diff the resource
   270  	var diff *InstanceDiff
   271  	nodes = append(nodes, &EvalOpFilter{
   272  		Ops: []walkOperation{walkPlan, walkPlanDestroy},
   273  		Node: &EvalSequence{
   274  			Nodes: []EvalNode{
   275  				&EvalReadState{
   276  					Name:   n.ResourceKey.String(),
   277  					Output: &state,
   278  				},
   279  				&EvalDiffDestroy{
   280  					Info:   info,
   281  					State:  &state,
   282  					Output: &diff,
   283  				},
   284  				&EvalWriteDiff{
   285  					Name: n.ResourceKey.String(),
   286  					Diff: &diff,
   287  				},
   288  			},
   289  		},
   290  	})
   291  
   292  	// Apply
   293  	var err error
   294  	nodes = append(nodes, &EvalOpFilter{
   295  		Ops: []walkOperation{walkApply, walkDestroy},
   296  		Node: &EvalSequence{
   297  			Nodes: []EvalNode{
   298  				&EvalReadDiff{
   299  					Name: n.ResourceKey.String(),
   300  					Diff: &diff,
   301  				},
   302  				&EvalGetProvider{
   303  					Name:   n.ProvidedBy()[0],
   304  					Output: &provider,
   305  				},
   306  				&EvalReadState{
   307  					Name:   n.ResourceKey.String(),
   308  					Output: &state,
   309  				},
   310  				&EvalApply{
   311  					Info:     info,
   312  					State:    &state,
   313  					Diff:     &diff,
   314  					Provider: &provider,
   315  					Output:   &state,
   316  					Error:    &err,
   317  				},
   318  				&EvalWriteState{
   319  					Name:         n.ResourceKey.String(),
   320  					ResourceType: n.ResourceKey.Type,
   321  					Provider:     n.Provider,
   322  					Dependencies: n.DependentOn(),
   323  					State:        &state,
   324  				},
   325  				&EvalApplyPost{
   326  					Info:  info,
   327  					State: &state,
   328  					Error: &err,
   329  				},
   330  				&EvalUpdateStateHook{},
   331  			},
   332  		},
   333  	})
   334  
   335  	return nodes
   336  }
   337  
   338  func (n *graphNodeOrphanResource) dataResourceEvalNodes(info *InstanceInfo) []EvalNode {
   339  	nodes := make([]EvalNode, 0, 3)
   340  
   341  	// This will remain nil, since we don't retain states for orphaned
   342  	// data resources.
   343  	var state *InstanceState
   344  
   345  	// On both refresh and apply we just drop our state altogether,
   346  	// since the config resource validation pass will have proven that the
   347  	// resources remaining in the configuration don't need it.
   348  	nodes = append(nodes, &EvalOpFilter{
   349  		Ops: []walkOperation{walkRefresh, walkApply},
   350  		Node: &EvalSequence{
   351  			Nodes: []EvalNode{
   352  				&EvalWriteState{
   353  					Name:         n.ResourceKey.String(),
   354  					ResourceType: n.ResourceKey.Type,
   355  					Provider:     n.Provider,
   356  					Dependencies: n.DependentOn(),
   357  					State:        &state, // state is nil
   358  				},
   359  			},
   360  		},
   361  	})
   362  
   363  	return nodes
   364  }
   365  
   366  func (n *graphNodeOrphanResource) dependableName() string {
   367  	return n.ResourceKey.String()
   368  }
   369  
   370  // GraphNodeDestroyable impl.
   371  func (n *graphNodeOrphanResource) DestroyNode() GraphNodeDestroy {
   372  	return n
   373  }
   374  
   375  // GraphNodeDestroy impl.
   376  func (n *graphNodeOrphanResource) CreateBeforeDestroy() bool {
   377  	return false
   378  }
   379  
   380  func (n *graphNodeOrphanResource) CreateNode() dag.Vertex {
   381  	return n
   382  }
   383  
   384  // Same as graphNodeOrphanResource, but for flattening
   385  type graphNodeOrphanResourceFlat struct {
   386  	*graphNodeOrphanResource
   387  
   388  	PathValue []string
   389  }
   390  
   391  func (n *graphNodeOrphanResourceFlat) Name() string {
   392  	return fmt.Sprintf(
   393  		"%s.%s", modulePrefixStr(n.PathValue), n.graphNodeOrphanResource.Name())
   394  }
   395  
   396  func (n *graphNodeOrphanResourceFlat) Path() []string {
   397  	return n.PathValue
   398  }
   399  
   400  // GraphNodeDestroyable impl.
   401  func (n *graphNodeOrphanResourceFlat) DestroyNode() GraphNodeDestroy {
   402  	return n
   403  }
   404  
   405  // GraphNodeDestroy impl.
   406  func (n *graphNodeOrphanResourceFlat) CreateBeforeDestroy() bool {
   407  	return false
   408  }
   409  
   410  func (n *graphNodeOrphanResourceFlat) CreateNode() dag.Vertex {
   411  	return n
   412  }
   413  
   414  func (n *graphNodeOrphanResourceFlat) ProvidedBy() []string {
   415  	return modulePrefixList(
   416  		n.graphNodeOrphanResource.ProvidedBy(),
   417  		modulePrefixStr(n.PathValue))
   418  }