Exemplo n.º 1
0
// nounAddVariableDeps updates the dependencies of a noun given
// a set of associated variable values
func nounAddVariableDeps(
	g *depgraph.Graph,
	n *depgraph.Noun,
	vars map[string]config.InterpolatedVariable,
	removeSelf bool) {
	for _, v := range vars {
		// Only resource variables impose dependencies
		rv, ok := v.(*config.ResourceVariable)
		if !ok {
			continue
		}

		// Find the target
		target := g.Noun(rv.ResourceId())
		if target == nil {
			continue
		}

		// If we're ignoring self-references, then don't add that
		// dependency.
		if removeSelf && n == target {
			continue
		}

		// Build the dependency
		dep := &depgraph.Dependency{
			Name:   rv.ResourceId(),
			Source: n,
			Target: target,
		}

		n.Deps = append(n.Deps, dep)
	}
}
Exemplo n.º 2
0
// graphAddMissingResourceProviders adds GraphNodeResourceProvider nodes for
// the resources that do not have an explicit resource provider specified
// because no provider configuration was given.
func graphAddMissingResourceProviders(
	g *depgraph.Graph,
	ps map[string]ResourceProviderFactory) error {
	var errs []error

	for _, n := range g.Nouns {
		rn, ok := n.Meta.(*GraphNodeResource)
		if !ok {
			continue
		}
		if rn.ResourceProviderID != "" {
			continue
		}

		prefixes := matchingPrefixes(rn.Type, ps)
		if len(prefixes) == 0 {
			errs = append(errs, fmt.Errorf(
				"No matching provider for type: %s",
				rn.Type))
			continue
		}

		// The resource provider ID is simply the shortest matching
		// prefix, since that'll give us the most resource providers
		// to choose from.
		rn.ResourceProviderID = prefixes[len(prefixes)-1]

		// If we don't have a matching noun for this yet, insert it.
		pn := g.Noun(fmt.Sprintf("provider.%s", rn.ResourceProviderID))
		if pn == nil {
			pn = &depgraph.Noun{
				Name: fmt.Sprintf("provider.%s", rn.ResourceProviderID),
				Meta: &GraphNodeResourceProvider{
					ID:     rn.ResourceProviderID,
					Config: nil,
				},
			}
			g.Nouns = append(g.Nouns, pn)
		}

		// Add the provider configuration noun as a dependency
		dep := &depgraph.Dependency{
			Name:   pn.Name,
			Source: n,
			Target: pn,
		}
		n.Deps = append(n.Deps, dep)
	}

	if len(errs) > 0 {
		return &multierror.Error{Errors: errs}
	}

	return nil
}
Exemplo n.º 3
0
// graphAddRoot adds a root element to the graph so that there is a single
// root to point to all the dependencies.
func graphAddRoot(g *depgraph.Graph) {
	root := &depgraph.Noun{Name: GraphRootNode}
	for _, n := range g.Nouns {
		switch m := n.Meta.(type) {
		case *GraphNodeResource:
			// If the resource is part of a group, we don't need to make a dep
			if m.Index != -1 {
				continue
			}
		case *GraphNodeResourceMeta:
			// Always in the graph
		case *GraphNodeResourceProvider:
			// ResourceProviders don't need to be in the root deps because
			// they're always pointed to by some resource.
			continue
		}

		root.Deps = append(root.Deps, &depgraph.Dependency{
			Name:   n.Name,
			Source: root,
			Target: n,
		})
	}
	g.Nouns = append(g.Nouns, root)
}
Exemplo n.º 4
0
// graphAddProviderConfigs cycles through all the resource-like nodes
// and adds the provider configuration nouns into the tree.
func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) {
	nounsList := make([]*depgraph.Noun, 0, 2)
	pcNouns := make(map[string]*depgraph.Noun)
	for _, noun := range g.Nouns {
		resourceNode, ok := noun.Meta.(*GraphNodeResource)
		if !ok {
			continue
		}

		// Look up the provider config for this resource
		pcName := config.ProviderConfigName(
			resourceNode.Type, c.ProviderConfigs)
		if pcName == "" {
			continue
		}

		// We have one, so build the noun if it hasn't already been made
		pcNoun, ok := pcNouns[pcName]
		if !ok {
			var pc *config.ProviderConfig
			for _, v := range c.ProviderConfigs {
				if v.Name == pcName {
					pc = v
					break
				}
			}
			if pc == nil {
				panic("pc not found")
			}

			pcNoun = &depgraph.Noun{
				Name: fmt.Sprintf("provider.%s", pcName),
				Meta: &GraphNodeResourceProvider{
					ID:     pcName,
					Config: pc,
				},
			}
			pcNouns[pcName] = pcNoun
			nounsList = append(nounsList, pcNoun)
		}

		// Set the resource provider ID for this noun so we can look it
		// up later easily.
		resourceNode.ResourceProviderID = pcName

		// Add the provider configuration noun as a dependency
		dep := &depgraph.Dependency{
			Name:   pcName,
			Source: noun,
			Target: pcNoun,
		}
		noun.Deps = append(noun.Deps, dep)
	}

	// Add all the provider config nouns to the graph
	g.Nouns = append(g.Nouns, nounsList...)
}
Exemplo n.º 5
0
// graphAddOrphans adds the orphans to the graph.
func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
	for _, k := range s.Orphans(c) {
		rs := s.Resources[k]
		noun := &depgraph.Noun{
			Name: k,
			Meta: &GraphNodeResource{
				Index:  -1,
				Type:   rs.Type,
				Orphan: true,
				Resource: &Resource{
					Id:     k,
					State:  rs,
					Config: NewResourceConfig(nil),
				},
			},
		}
		g.Nouns = append(g.Nouns, noun)
	}
}
Exemplo n.º 6
0
// Graph builds a dependency graph of all the resources for infrastructure
// change.
//
// This dependency graph shows the correct order that any resources need
// to be operated on.
//
// The Meta field of a graph Noun can contain one of the follow types. A
// description is next to each type to explain what it is.
//
//   *GraphNodeResource - A resource. See the documentation of this
//     struct for more details.
//   *GraphNodeResourceProvider - A resource provider that needs to be
//     configured at this point.
//
func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
	if opts.Config == nil {
		return nil, errors.New("Config is required for Graph")
	}

	log.Printf("[DEBUG] Creating graph...")

	g := new(depgraph.Graph)

	// First, build the initial resource graph. This only has the resources
	// and no dependencies.
	graphAddConfigResources(g, opts.Config, opts.State)

	// Add explicit dependsOn dependencies to the graph
	graphAddExplicitDeps(g)

	// Next, add the state orphans if we have any
	if opts.State != nil {
		graphAddOrphans(g, opts.Config, opts.State)
	}

	// Map the provider configurations to all of the resources
	graphAddProviderConfigs(g, opts.Config)

	// Setup the provisioners. These may have variable dependencies,
	// and must be done before dependency setup
	if err := graphMapResourceProvisioners(g, opts.Provisioners); err != nil {
		return nil, err
	}

	// Add all the variable dependencies
	graphAddVariableDeps(g)

	// Build the root so that we have a single valid root
	graphAddRoot(g)

	// If providers were given, lets associate the proper providers and
	// instantiate them.
	if len(opts.Providers) > 0 {
		// Add missing providers from the mapping
		if err := graphAddMissingResourceProviders(g, opts.Providers); err != nil {
			return nil, err
		}

		// Initialize all the providers
		if err := graphInitResourceProviders(g, opts.Providers); err != nil {
			return nil, err
		}

		// Map the providers to resources
		if err := graphMapResourceProviders(g); err != nil {
			return nil, err
		}
	}

	// If we have a diff, then make sure to add that in
	if opts.Diff != nil {
		if err := graphAddDiff(g, opts.Diff); err != nil {
			return nil, err
		}
	}

	// Validate
	if err := g.Validate(); err != nil {
		return nil, err
	}

	log.Printf(
		"[DEBUG] Graph created and valid. %d nouns.",
		len(g.Nouns))

	return g, nil
}
Exemplo n.º 7
0
// graphAddDiff takes an already-built graph of resources and adds the
// diffs to the resource nodes themselves.
//
// This may also introduces new graph elements. If there are diffs that
// require a destroy, new elements may be introduced since destroy order
// is different than create order. For example, destroying a VPC requires
// destroying the VPC's subnets first, whereas creating a VPC requires
// doing it before the subnets are created. This function handles inserting
// these nodes for you.
func graphAddDiff(g *depgraph.Graph, d *Diff) error {
	var nlist []*depgraph.Noun
	for _, n := range g.Nouns {
		rn, ok := n.Meta.(*GraphNodeResource)
		if !ok {
			continue
		}

		rd, ok := d.Resources[rn.Resource.Id]
		if !ok {
			continue
		}
		if rd.Empty() {
			continue
		}

		if rd.Destroy {
			// If we're destroying, we create a new destroy node with
			// the proper dependencies. Perform a dirty copy operation.
			newNode := new(GraphNodeResource)
			*newNode = *rn
			newNode.Resource = new(Resource)
			*newNode.Resource = *rn.Resource

			// Make the diff _just_ the destroy.
			newNode.Resource.Diff = &ResourceDiff{Destroy: true}

			// Create the new node
			newN := &depgraph.Noun{
				Name: fmt.Sprintf("%s (destroy)", newNode.Resource.Id),
				Meta: newNode,
			}
			newN.Deps = make([]*depgraph.Dependency, 0, len(n.Deps))
			for _, d := range n.Deps {
				// We don't want to copy any resource dependencies
				if _, ok := d.Target.Meta.(*GraphNodeResource); ok {
					continue
				}

				newN.Deps = append(newN.Deps, &depgraph.Dependency{
					Name:   d.Name,
					Source: newN,
					Target: d.Target,
				})
			}

			// Append it to the list so we handle it later
			nlist = append(nlist, newN)

			// Mark the old diff to not destroy since we handle that in
			// the dedicated node.
			newDiff := new(ResourceDiff)
			*newDiff = *rd
			newDiff.Destroy = false
			rd = newDiff

			// Add to the new noun to our dependencies so that the destroy
			// happens before the apply.
			n.Deps = append(n.Deps, &depgraph.Dependency{
				Name:   newN.Name,
				Source: n,
				Target: newN,
			})

			// If the resource is tainted, mark the state as nil so
			// that a fresh create is done.
			if rn.Resource.Tainted {
				rn.Resource.State = &ResourceState{
					Type: rn.Resource.State.Type,
				}
				rn.Resource.Tainted = false
			}
		}

		rn.Resource.Diff = rd
	}

	// Go through each noun and make sure we calculate all the dependencies
	// properly.
	for _, n := range nlist {
		rn := n.Meta.(*GraphNodeResource)

		// If we have no dependencies, then just continue
		deps := rn.Resource.State.Dependencies
		if len(deps) == 0 {
			continue
		}

		// We have dependencies. We must be destroyed BEFORE those
		// dependencies. Look to see if they're managed.
		for _, dep := range deps {
			for _, n2 := range nlist {
				// Don't ever depend on ourselves
				if n2.Name == n.Name {
					continue
				}

				rn2 := n2.Meta.(*GraphNodeResource)
				if rn2.Resource.State.ID == dep.ID {
					n2.Deps = append(n2.Deps, &depgraph.Dependency{
						Name:   n.Name,
						Source: n2,
						Target: n,
					})

					break
				}
			}
		}
	}

	// Add the nouns to the graph
	g.Nouns = append(g.Nouns, nlist...)

	return nil
}
Exemplo n.º 8
0
// configGraph turns a configuration structure into a dependency graph.
func graphAddConfigResources(
	g *depgraph.Graph, c *config.Config, s *State) {
	// This tracks all the resource nouns
	nouns := make(map[string]*depgraph.Noun)
	for _, r := range c.Resources {
		resourceNouns := make([]*depgraph.Noun, r.Count)
		for i := 0; i < r.Count; i++ {
			name := r.Id()
			index := -1

			// If we have a count that is more than one, then make sure
			// we suffix with the number of the resource that this is.
			if r.Count > 1 {
				name = fmt.Sprintf("%s.%d", name, i)
				index = i
			}

			// Determine if this resource is tainted
			tainted := false
			if s != nil && s.Tainted != nil {
				_, tainted = s.Tainted[r.Id()]
			}

			var state *ResourceState
			if s != nil {
				state = s.Resources[name]

				if state == nil {
					if r.Count == 1 {
						// If the count is one, check the state for ".0"
						// appended, which might exist if we go from
						// count > 1 to count == 1.
						state = s.Resources[r.Id()+".0"]
					} else if i == 0 {
						// If count is greater than one, check for state
						// with just the ID, which might exist if we go
						// from count == 1 to count > 1
						state = s.Resources[r.Id()]
					}
				}
			}
			if state == nil {
				state = &ResourceState{
					Type: r.Type,
				}
			}

			resourceNouns[i] = &depgraph.Noun{
				Name: name,
				Meta: &GraphNodeResource{
					Index:  index,
					Type:   r.Type,
					Config: r,
					Resource: &Resource{
						Id:      name,
						State:   state,
						Config:  NewResourceConfig(r.RawConfig),
						Tainted: tainted,
					},
				},
			}
		}

		// If we have more than one, then create a meta node to track
		// the resources.
		if r.Count > 1 {
			metaNoun := &depgraph.Noun{
				Name: r.Id(),
				Meta: &GraphNodeResourceMeta{
					ID:    r.Id(),
					Name:  r.Name,
					Type:  r.Type,
					Count: r.Count,
				},
			}

			// Create the dependencies on this noun
			for _, n := range resourceNouns {
				metaNoun.Deps = append(metaNoun.Deps, &depgraph.Dependency{
					Name:   n.Name,
					Source: metaNoun,
					Target: n,
				})
			}

			// Assign it to the map so that we have it
			nouns[metaNoun.Name] = metaNoun
		}

		for _, n := range resourceNouns {
			nouns[n.Name] = n
		}
	}

	// Build the list of nouns that we iterate over
	nounsList := make([]*depgraph.Noun, 0, len(nouns))
	for _, n := range nouns {
		nounsList = append(nounsList, n)
	}

	g.Name = "terraform"
	g.Nouns = append(g.Nouns, nounsList...)
}