Exemple #1
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 {
			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.")
			}

			return plan.Context(opts), true, nil
		}
	}

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

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

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

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

	opts.Module = mod
	opts.State = state
	ctx := terraform.NewContext(opts)
	return ctx, false, nil
}
func testModule(t *testing.T, name string) *module.Tree {
	mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	s := &module.FolderStorage{StorageDir: tempDir(t)}
	if err := mod.Load(s, module.GetModeGet); err != nil {
		t.Fatalf("err: %s", err)
	}

	return mod
}
func TestConfigTransformer_unloadedModule(t *testing.T) {
	mod, err := module.NewTreeModule(
		"", filepath.Join(fixtureDir, "graph-basic"))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	g := Graph{Path: RootModulePath}
	tf := &ConfigTransformer{Module: mod}
	if err := tf.Transform(&g); err == nil {
		t.Fatal("should error")
	}
}
Exemple #4
0
func testModule(
	opts terraform.ContextOpts,
	step TestStep) (*module.Tree, error) {
	if step.PreConfig != nil {
		step.PreConfig()
	}

	cfgPath, err := ioutil.TempDir("", "tf-test")
	if err != nil {
		return nil, fmt.Errorf(
			"Error creating temporary directory for config: %s", err)
	}
	defer os.RemoveAll(cfgPath)

	// Write the configuration
	cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
	if err != nil {
		return nil, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	_, err = io.Copy(cfgF, strings.NewReader(step.Config))
	cfgF.Close()
	if err != nil {
		return nil, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	// Parse the configuration
	mod, err := module.NewTreeModule("", cfgPath)
	if err != nil {
		return nil, fmt.Errorf(
			"Error loading configuration: %s", err)
	}

	// Load the modules
	modStorage := &getter.FolderStorage{
		StorageDir: filepath.Join(cfgPath, ".tfmodules"),
	}
	err = mod.Load(modStorage, module.GetModeGet)
	if err != nil {
		return nil, fmt.Errorf("Error downloading modules: %s", err)
	}

	return mod, nil
}
Exemple #5
0
// testModuleInline takes a map of path -> config strings and yields a config
// structure with those files loaded from disk
func testModuleInline(t *testing.T, config map[string]string) *module.Tree {
	cfgPath, err := ioutil.TempDir("", "tf-test")
	if err != nil {
		t.Errorf("Error creating temporary directory for config: %s", err)
	}
	defer os.RemoveAll(cfgPath)

	for path, configStr := range config {
		dir := filepath.Dir(path)
		if dir != "." {
			err := os.MkdirAll(filepath.Join(cfgPath, dir), os.FileMode(0777))
			if err != nil {
				t.Fatalf("Error creating subdir: %s", err)
			}
		}
		// Write the configuration
		cfgF, err := os.Create(filepath.Join(cfgPath, path))
		if err != nil {
			t.Fatalf("Error creating temporary file for config: %s", err)
		}

		_, err = io.Copy(cfgF, strings.NewReader(configStr))
		cfgF.Close()
		if err != nil {
			t.Fatalf("Error creating temporary file for config: %s", err)
		}
	}

	// Parse the configuration
	mod, err := module.NewTreeModule("", cfgPath)
	if err != nil {
		t.Fatalf("Error loading configuration: %s", err)
	}

	// Load the modules
	modStorage := &getter.FolderStorage{
		StorageDir: filepath.Join(cfgPath, ".tfmodules"),
	}
	err = mod.Load(modStorage, module.GetModeGet)
	if err != nil {
		t.Errorf("Error downloading modules: %s", err)
	}

	return mod
}
Exemple #6
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
}
Exemple #7
0
func testStep(
	opts terraform.ContextOpts,
	state *terraform.State,
	step TestStep) (*terraform.State, error) {
	cfgPath, err := ioutil.TempDir("", "tf-test")
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary directory for config: %s", err)
	}
	defer os.RemoveAll(cfgPath)

	// Write the configuration
	cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	_, err = io.Copy(cfgF, strings.NewReader(step.Config))
	cfgF.Close()
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	// Parse the configuration
	mod, err := module.NewTreeModule("", cfgPath)
	if err != nil {
		return state, fmt.Errorf(
			"Error loading configuration: %s", err)
	}

	// Load the modules
	modStorage := &module.FolderStorage{
		StorageDir: filepath.Join(cfgPath, ".tfmodules"),
	}
	err = mod.Load(modStorage, module.GetModeGet)
	if err != nil {
		return state, fmt.Errorf("Error downloading modules: %s", err)
	}

	// Build the context
	opts.Module = mod
	opts.State = state
	opts.Destroy = step.Destroy
	ctx := terraform.NewContext(&opts)
	if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
		estrs := make([]string, len(es))
		for i, e := range es {
			estrs[i] = e.Error()
		}
		return state, fmt.Errorf(
			"Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
			ws, estrs)
	}

	// Refresh!
	state, err = ctx.Refresh()
	if err != nil {
		return state, fmt.Errorf(
			"Error refreshing: %s", err)
	}

	// Plan!
	if p, err := ctx.Plan(); err != nil {
		return state, fmt.Errorf(
			"Error planning: %s", err)
	} else {
		log.Printf("[WARN] Test: Step plan: %s", p)
	}

	// Apply!
	state, err = ctx.Apply()
	if err != nil {
		return state, fmt.Errorf("Error applying: %s", err)
	}

	// Check! Excitement!
	if step.Check != nil {
		if err = step.Check(state); err != nil {
			err = fmt.Errorf("Check failed: %s", err)
		}
	}

	return state, err
}
Exemple #8
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
}
Exemple #9
0
func testStep(
	opts terraform.ContextOpts,
	state *terraform.State,
	step TestStep) (*terraform.State, error) {
	if step.PreConfig != nil {
		step.PreConfig()
	}

	cfgPath, err := ioutil.TempDir("", "tf-test")
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary directory for config: %s", err)
	}
	defer os.RemoveAll(cfgPath)

	// Write the configuration
	cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	_, err = io.Copy(cfgF, strings.NewReader(step.Config))
	cfgF.Close()
	if err != nil {
		return state, fmt.Errorf(
			"Error creating temporary file for config: %s", err)
	}

	// Parse the configuration
	mod, err := module.NewTreeModule("", cfgPath)
	if err != nil {
		return state, fmt.Errorf(
			"Error loading configuration: %s", err)
	}

	// Load the modules
	modStorage := &getter.FolderStorage{
		StorageDir: filepath.Join(cfgPath, ".tfmodules"),
	}
	err = mod.Load(modStorage, module.GetModeGet)
	if err != nil {
		return state, fmt.Errorf("Error downloading modules: %s", err)
	}

	// Build the context
	opts.Module = mod
	opts.State = state
	opts.Destroy = step.Destroy
	ctx := terraform.NewContext(&opts)
	if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
		if len(es) > 0 {
			estrs := make([]string, len(es))
			for i, e := range es {
				estrs[i] = e.Error()
			}
			return state, fmt.Errorf(
				"Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
				ws, estrs)
		}
		log.Printf("[WARN] Config warnings: %#v", ws)
	}

	// Refresh!
	state, err = ctx.Refresh()
	if err != nil {
		return state, fmt.Errorf(
			"Error refreshing: %s", err)
	}

	// Plan!
	if p, err := ctx.Plan(); err != nil {
		return state, fmt.Errorf(
			"Error planning: %s", err)
	} else {
		log.Printf("[WARN] Test: Step plan: %s", p)
	}

	// We need to keep a copy of the state prior to destroying
	// such that destroy steps can verify their behaviour in the check
	// function
	stateBeforeApplication := state.DeepCopy()

	// Apply!
	state, err = ctx.Apply()
	if err != nil {
		return state, fmt.Errorf("Error applying: %s", err)
	}

	// Check! Excitement!
	if step.Check != nil {
		if step.Destroy {
			if err := step.Check(stateBeforeApplication); err != nil {
				return state, fmt.Errorf("Check failed: %s", err)
			}
		} else {
			if err := step.Check(state); err != nil {
				return state, fmt.Errorf("Check failed: %s", err)
			}
		}
	}

	// Now, verify that Plan is now empty and we don't have a perpetual diff issue
	// We do this with TWO plans. One without a refresh.
	var p *terraform.Plan
	if p, err = ctx.Plan(); err != nil {
		return state, fmt.Errorf("Error on follow-up plan: %s", err)
	}
	if p.Diff != nil && !p.Diff.Empty() {
		if step.ExpectNonEmptyPlan {
			log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
		} else {
			return state, fmt.Errorf(
				"After applying this step, the plan was not empty:\n\n%s", p)
		}
	}

	// And another after a Refresh.
	state, err = ctx.Refresh()
	if err != nil {
		return state, fmt.Errorf(
			"Error on follow-up refresh: %s", err)
	}
	if p, err = ctx.Plan(); err != nil {
		return state, fmt.Errorf("Error on second follow-up plan: %s", err)
	}
	if p.Diff != nil && !p.Diff.Empty() {
		if step.ExpectNonEmptyPlan {
			log.Printf("[INFO] Got non-empty plan, as expected:\n\n%s", p)
		} else {
			return state, fmt.Errorf(
				"After applying this step and refreshing, "+
					"the plan was not empty:\n\n%s", p)
		}
	}

	// Made it here, but expected a non-empty plan, fail!
	if step.ExpectNonEmptyPlan && (p.Diff == nil || p.Diff.Empty()) {
		return state, fmt.Errorf("Expected a non-empty plan, but got an empty plan!")
	}

	// Made it here? Good job test step!
	return state, nil
}