Пример #1
0
func (c *walkContext) resourceVariableInfo(
	v *config.ResourceVariable) (*ModuleState, *config.Resource, error) {
	// Get the module tree that contains our current path. This is
	// either the current module (path is empty) or a child.
	var modTree *module.Tree
	childPath := c.Path[1:len(c.Path)]
	if len(childPath) == 0 {
		modTree = c.Context.module
	} else {
		modTree = c.Context.module.Child(childPath)
	}

	// Get the resource from the configuration so we can verify
	// that the resource is in the configuration and so we can access
	// the configuration if we need to.
	var cr *config.Resource
	for _, r := range modTree.Config().Resources {
		if r.Id() == v.ResourceId() {
			cr = r
			break
		}
	}
	if cr == nil {
		return nil, nil, fmt.Errorf(
			"Resource '%s' not found for variable '%s'",
			v.ResourceId(),
			v.FullKey())
	}

	// Get the relevant module
	module := c.Context.state.ModuleByPath(c.Path)
	return module, cr, nil
}
Пример #2
0
func (t *ConfigTransformer) transform(g *Graph, m *module.Tree) error {
	// If no config, do nothing
	if m == nil {
		return nil
	}

	// Add our resources
	if err := t.transformSingle(g, m); err != nil {
		return err
	}

	// Transform all the children.
	for _, c := range m.Children() {
		if err := t.transform(g, c); err != nil {
			return err
		}
	}

	return nil
}
Пример #3
0
func (t *ConfigTransformer) transformSingle(g *Graph, m *module.Tree) error {
	log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", m.Path())

	// Get the configuration for this module
	config := m.Config()

	// Build the path we're at
	path := m.Path()

	// Write all the resources out
	for _, r := range config.Resources {
		// Build the resource address
		addr, err := parseResourceAddressConfig(r)
		if err != nil {
			panic(fmt.Sprintf(
				"Error parsing config address, this is a bug: %#v", r))
		}
		addr.Path = path

		// Build the abstract node and the concrete one
		abstract := &NodeAbstractResource{Addr: addr}
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}

		// Add it to the graph
		g.Add(node)
	}

	return nil
}
func (t *ModuleVariableTransformer) transform(g *Graph, parent, m *module.Tree) error {
	// If no config, no variables
	if m == nil {
		return nil
	}

	// Transform all the children. This must be done BEFORE the transform
	// above since child module variables can reference parent module variables.
	for _, c := range m.Children() {
		if err := t.transform(g, m, c); err != nil {
			return err
		}
	}

	// If we have a parent, we can determine if a module variable is being
	// used, so we transform this.
	if parent != nil {
		if err := t.transformSingle(g, parent, m); err != nil {
			return err
		}
	}

	return nil
}
Пример #5
0
// checkRequiredVersion verifies that any version requirements specified by
// the configuration are met.
//
// This checks the root module as well as any additional version requirements
// from child modules.
//
// This is tested in context_test.go.
func checkRequiredVersion(m *module.Tree) error {
	// Check any children
	for _, c := range m.Children() {
		if err := checkRequiredVersion(c); err != nil {
			return err
		}
	}

	var tf *config.Terraform
	if c := m.Config(); c != nil {
		tf = c.Terraform
	}

	// If there is no Terraform config or the required version isn't set,
	// we move on.
	if tf == nil || tf.RequiredVersion == "" {
		return nil
	}

	// Path for errors
	module := "root"
	if path := normalizeModulePath(m.Path()); len(path) > 1 {
		module = modulePrefixStr(path)
	}

	// Check this version requirement of this module
	cs, err := version.NewConstraint(tf.RequiredVersion)
	if err != nil {
		return fmt.Errorf(
			"%s: terraform.required_version %q syntax error: %s",
			module,
			tf.RequiredVersion, err)
	}

	if !cs.Check(SemVersion) {
		return fmt.Errorf(
			"The currently running version of Terraform doesn't meet the\n"+
				"version requirements explicitly specified by the configuration.\n"+
				"Please use the required version or update the configuration.\n"+
				"Note that version requirements are usually set for a reason, so\n"+
				"we recommend verifying with whoever set the version requirements\n"+
				"prior to making any manual changes.\n\n"+
				"  Module: %s\n"+
				"  Required version: %s\n"+
				"  Current version: %s",
			module,
			tf.RequiredVersion,
			SemVersion)
	}

	return nil
}
Пример #6
0
func (t *FlatConfigTransformer) transform(g *Graph, m *module.Tree) error {
	// If no module, no problem
	if m == nil {
		return nil
	}

	// Transform all the children.
	for _, c := range m.Children() {
		if err := t.transform(g, c); err != nil {
			return err
		}
	}

	// Get the configuration for this module
	config := m.Config()

	// Write all the resources out
	for _, r := range config.Resources {
		// Grab the address for this resource
		addr, err := parseResourceAddressConfig(r)
		if err != nil {
			return err
		}
		addr.Path = m.Path()

		// Build the abstract resource. We have the config already so
		// we'll just pre-populate that.
		abstract := &NodeAbstractResource{
			Addr:   addr,
			Config: r,
		}
		var node dag.Vertex = abstract
		if f := t.Concrete; f != nil {
			node = f(abstract)
		}

		g.Add(node)
	}

	return nil
}
Пример #7
0
func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error {
	// If no config, no outputs
	if m == nil {
		return nil
	}

	// Transform all the children. We must do this first because
	// we can reference module outputs and they must show up in the
	// reference map.
	for _, c := range m.Children() {
		if err := t.transform(g, c); err != nil {
			return err
		}
	}

	// If we have no outputs, we're done!
	os := m.Config().Outputs
	if len(os) == 0 {
		return nil
	}

	// Add all outputs here
	for _, o := range os {
		// Build the node.
		//
		// NOTE: For now this is just an "applyable" output. As we build
		// new graph builders for the other operations I suspect we'll
		// find a way to parameterize this, require new transforms, etc.
		node := &NodeApplyableOutput{
			PathValue: normalizeModulePath(m.Path()),
			Config:    o,
		}

		// Add it!
		g.Add(node)
	}

	return nil
}
Пример #8
0
func (t *OrphanOutputTransformer) transform(g *Graph, m *module.Tree) error {
	// Get our configuration, and recurse into children
	var c *config.Config
	if m != nil {
		c = m.Config()
		for _, child := range m.Children() {
			if err := t.transform(g, child); err != nil {
				return err
			}
		}
	}

	// Get the state. If there is no state, then we have no orphans!
	path := normalizeModulePath(m.Path())
	state := t.State.ModuleByPath(path)
	if state == nil {
		return nil
	}

	// Make a map of the valid outputs
	valid := make(map[string]struct{})
	for _, o := range c.Outputs {
		valid[o.Name] = struct{}{}
	}

	// Go through the outputs and find the ones that aren't in our config.
	for n, _ := range state.Outputs {
		// If it is in the valid map, then ignore
		if _, ok := valid[n]; ok {
			continue
		}

		// Orphan!
		g.Add(&NodeOutputOrphan{OutputName: n, PathValue: path})
	}

	return nil
}
Пример #9
0
// Context returns a Terraform Context taking into account the context
// options used to initialize this meta configuration.
func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
	opts := m.contextOpts()

	// First try to just read the plan directly from the path given.
	f, err := os.Open(copts.Path)
	if err == nil {
		plan, err := terraform.ReadPlan(f)
		f.Close()
		if err == nil {
			// Setup our state
			state, statePath, err := StateFromPlan(m.statePath, m.stateOutPath, plan)
			if err != nil {
				return nil, false, fmt.Errorf("Error loading plan: %s", err)
			}

			// Set our state
			m.state = state

			// this is used for printing the saved location later
			if m.stateOutPath == "" {
				m.stateOutPath = statePath
			}

			if len(m.variables) > 0 {
				return nil, false, fmt.Errorf(
					"You can't set variables with the '-var' or '-var-file' flag\n" +
						"when you're applying a plan file. The variables used when\n" +
						"the plan was created will be used. If you wish to use different\n" +
						"variable values, create a new plan file.")
			}

			ctx, err := plan.Context(opts)
			return ctx, true, err
		}
	}

	// Load the statePath if not given
	if copts.StatePath != "" {
		m.statePath = copts.StatePath
	}

	// Tell the context if we're in a destroy plan / apply
	opts.Destroy = copts.Destroy

	// Store the loaded state
	state, err := m.State()
	if err != nil {
		return nil, false, err
	}

	// Load the root module
	var mod *module.Tree
	if copts.Path != "" {
		mod, err = module.NewTreeModule("", copts.Path)
		if err != nil {
			return nil, false, fmt.Errorf("Error loading config: %s", err)
		}
	} else {
		mod = module.NewEmptyTree()
	}

	err = mod.Load(m.moduleStorage(m.DataDir()), copts.GetMode)
	if err != nil {
		return nil, false, fmt.Errorf("Error downloading modules: %s", err)
	}

	opts.Module = mod
	opts.Parallelism = copts.Parallelism
	opts.State = state.State()
	ctx, err := terraform.NewContext(opts)
	return ctx, false, err
}
Пример #10
0
// Variables returns the fully loaded set of variables to use with
// ContextOpts and NewContext, loading any additional variables from
// the environment or any other sources.
//
// The given module tree doesn't need to be loaded.
func Variables(
	m *module.Tree,
	override map[string]interface{}) (map[string]interface{}, error) {
	result := make(map[string]interface{})

	// Variables are loaded in the following sequence. Each additional step
	// will override conflicting variable keys from prior steps:
	//
	//   * Take default values from config
	//   * Take values from TF_VAR_x env vars
	//   * Take values specified in the "override" param which is usually
	//     from -var, -var-file, etc.
	//

	// First load from the config
	for _, v := range m.Config().Variables {
		// If the var has no default, ignore
		if v.Default == nil {
			continue
		}

		// If the type isn't a string, we use it as-is since it is a rich type
		if v.Type() != config.VariableTypeString {
			result[v.Name] = v.Default
			continue
		}

		// v.Default has already been parsed as HCL but it may be an int type
		switch typedDefault := v.Default.(type) {
		case string:
			if typedDefault == "" {
				continue
			}
			result[v.Name] = typedDefault
		case int, int64:
			result[v.Name] = fmt.Sprintf("%d", typedDefault)
		case float32, float64:
			result[v.Name] = fmt.Sprintf("%f", typedDefault)
		case bool:
			result[v.Name] = fmt.Sprintf("%t", typedDefault)
		default:
			panic(fmt.Sprintf(
				"Unknown default var type: %T\n\n"+
					"THIS IS A BUG. Please report it.",
				v.Default))
		}
	}

	// Load from env vars
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, VarEnvPrefix) {
			continue
		}

		// Strip off the prefix and get the value after the first "="
		idx := strings.Index(v, "=")
		k := v[len(VarEnvPrefix):idx]
		v = v[idx+1:]

		// Override the configuration-default values. Note that *not* finding the variable
		// in configuration is OK, as we don't want to preclude people from having multiple
		// sets of TF_VAR_whatever in their environment even if it is a little weird.
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			varType := schema.Type()
			varVal, err := parseVariableAsHCL(k, v, varType)
			if err != nil {
				return nil, err
			}

			switch varType {
			case config.VariableTypeMap:
				varSetMap(result, k, varVal)
			default:
				result[k] = varVal
			}
		}
	}

	// Load from overrides
	for k, v := range override {
		for _, schema := range m.Config().Variables {
			if schema.Name != k {
				continue
			}

			switch schema.Type() {
			case config.VariableTypeList:
				result[k] = v
			case config.VariableTypeMap:
				varSetMap(result, k, v)
			case config.VariableTypeString:
				// Convert to a string and set. We don't catch any errors
				// here because the validation step later should catch
				// any type errors.
				var strVal string
				if err := hilmapstructure.WeakDecode(v, &strVal); err == nil {
					result[k] = strVal
				} else {
					result[k] = v
				}
			default:
				panic(fmt.Sprintf(
					"Unhandled var type: %T\n\n"+
						"THIS IS A BUG. Please report it.",
					schema.Type()))
			}
		}
	}

	return result, nil
}
Пример #11
0
// Context returns a Terraform Context taking into account the context
// options used to initialize this meta configuration.
func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
	opts := m.contextOpts()

	// First try to just read the plan directly from the path given.
	f, err := os.Open(copts.Path)
	if err == nil {
		plan, err := terraform.ReadPlan(f)
		f.Close()
		if err == nil {
			// Setup our state, force it to use our plan's state
			stateOpts := m.StateOpts()
			if plan != nil {
				stateOpts.ForceState = plan.State
			}

			// Get the state
			result, err := State(stateOpts)
			if err != nil {
				return nil, false, fmt.Errorf("Error loading plan: %s", err)
			}

			// Set our state
			m.state = result.State

			// this is used for printing the saved location later
			if m.stateOutPath == "" {
				m.stateOutPath = result.StatePath
			}

			if len(m.variables) > 0 {
				return nil, false, fmt.Errorf(
					"You can't set variables with the '-var' or '-var-file' flag\n" +
						"when you're applying a plan file. The variables used when\n" +
						"the plan was created will be used. If you wish to use different\n" +
						"variable values, create a new plan file.")
			}

			ctx, err := plan.Context(opts)
			return ctx, true, err
		}
	}

	// Load the statePath if not given
	if copts.StatePath != "" {
		m.statePath = copts.StatePath
	}

	// Tell the context if we're in a destroy plan / apply
	opts.Destroy = copts.Destroy

	// Store the loaded state
	state, err := m.State()
	if err != nil {
		return nil, false, err
	}

	// Load the root module
	var mod *module.Tree
	if copts.Path != "" {
		mod, err = module.NewTreeModule("", copts.Path)

		// Check for the error where we have no config files but
		// allow that. If that happens, clear the error.
		if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) &&
			copts.PathEmptyOk {
			log.Printf(
				"[WARN] Empty configuration dir, ignoring: %s", copts.Path)
			err = nil
			mod = module.NewEmptyTree()
		}

		if err != nil {
			return nil, false, fmt.Errorf("Error loading config: %s", err)
		}
	} else {
		mod = module.NewEmptyTree()
	}

	err = mod.Load(m.moduleStorage(m.DataDir()), copts.GetMode)
	if err != nil {
		return nil, false, fmt.Errorf("Error downloading modules: %s", err)
	}

	// Validate the module right away
	if err := mod.Validate(); err != nil {
		return nil, false, err
	}

	opts.Module = mod
	opts.Parallelism = copts.Parallelism
	opts.State = state.State()
	ctx, err := terraform.NewContext(opts)
	return ctx, false, err
}
func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, m *module.Tree) error {
	// If we have no vars, we're done!
	vars := m.Config().Variables
	if len(vars) == 0 {
		log.Printf("[TRACE] Module %#v has no variables, skipping.", m.Path())
		return nil
	}

	// Look for usage of this module
	var mod *config.Module
	for _, modUse := range parent.Config().Modules {
		if modUse.Name == m.Name() {
			mod = modUse
			break
		}
	}
	if mod == nil {
		log.Printf("[INFO] Module %#v not used, not adding variables", m.Path())
		return nil
	}

	// Build the reference map so we can determine if we're referencing things.
	refMap := NewReferenceMap(g.Vertices())

	// Add all variables here
	for _, v := range vars {
		// Determine the value of the variable. If it isn't in the
		// configuration then it was never set and that's not a problem.
		var value *config.RawConfig
		if raw, ok := mod.RawConfig.Raw[v.Name]; ok {
			var err error
			value, err = config.NewRawConfig(map[string]interface{}{
				v.Name: raw,
			})
			if err != nil {
				// This shouldn't happen because it is already in
				// a RawConfig above meaning it worked once before.
				panic(err)
			}
		}

		// Build the node.
		//
		// NOTE: For now this is just an "applyable" variable. As we build
		// new graph builders for the other operations I suspect we'll
		// find a way to parameterize this, require new transforms, etc.
		node := &NodeApplyableModuleVariable{
			PathValue: normalizeModulePath(m.Path()),
			Config:    v,
			Value:     value,
			Module:    t.Module,
		}

		if !t.DisablePrune {
			// If the node is not referenced by anything, then we don't need
			// to include it since it won't be used.
			if matches := refMap.ReferencedBy(node); len(matches) == 0 {
				log.Printf(
					"[INFO] Not including %q in graph, nothing depends on it",
					dag.VertexName(node))
				continue
			}
		}

		// Add it!
		g.Add(node)
	}

	return nil
}