command: use new API

This commit is contained in:
Mitchell Hashimoto 2014-07-03 11:46:40 -07:00
parent adcd6486a2
commit a6ae7230d1
11 changed files with 69 additions and 89 deletions

View File

@ -13,9 +13,9 @@ import (
// ApplyCommand is a Command implementation that applies a Terraform // ApplyCommand is a Command implementation that applies a Terraform
// configuration and actually builds or changes infrastructure. // configuration and actually builds or changes infrastructure.
type ApplyCommand struct { type ApplyCommand struct {
ShutdownCh <-chan struct{} ShutdownCh <-chan struct{}
TFConfig *terraform.Config ContextOpts *terraform.ContextOpts
Ui cli.Ui Ui cli.Ui
} }
func (c *ApplyCommand) Run(args []string) int { func (c *ApplyCommand) Run(args []string) int {
@ -44,21 +44,14 @@ func (c *ApplyCommand) Run(args []string) int {
stateOutPath = statePath stateOutPath = statePath
} }
// Initialize Terraform right away
c.TFConfig.Hooks = append(c.TFConfig.Hooks, &UiHook{Ui: c.Ui})
tf, err := terraform.New(c.TFConfig)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err))
return 1
}
// Attempt to read a plan from the path given. This is how we test that
// it is a plan or not (kind of jank, but if it quacks like a duck...)
planStatePath := statePath planStatePath := statePath
if init { if init {
planStatePath = "" planStatePath = ""
} }
plan, err := PlanArg(configPath, planStatePath, tf)
// Initialize Terraform right away
c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui})
ctx, err := ContextArg(configPath, planStatePath, c.ContextOpts)
if err != nil { if err != nil {
c.Ui.Error(err.Error()) c.Ui.Error(err.Error())
return 1 return 1
@ -67,7 +60,7 @@ func (c *ApplyCommand) Run(args []string) int {
errCh := make(chan error) errCh := make(chan error)
stateCh := make(chan *terraform.State) stateCh := make(chan *terraform.State)
go func() { go func() {
state, err := tf.Apply(plan) state, err := ctx.Apply()
if err != nil { if err != nil {
errCh <- err errCh <- err
return return
@ -83,7 +76,7 @@ func (c *ApplyCommand) Run(args []string) int {
c.Ui.Output("Interrupt received. Gracefully shutting down...") c.Ui.Output("Interrupt received. Gracefully shutting down...")
// Stop execution // Stop execution
tf.Stop() ctx.Stop()
// Still get the result, since there is still one // Still get the result, since there is still one
select { select {

View File

@ -6,6 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
) )
@ -16,8 +17,8 @@ func TestApply(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -52,8 +53,8 @@ func TestApply_configInvalid(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -67,14 +68,16 @@ func TestApply_configInvalid(t *testing.T) {
} }
func TestApply_plan(t *testing.T) { func TestApply_plan(t *testing.T) {
planPath := testPlanFile(t, new(terraform.Plan)) planPath := testPlanFile(t, &terraform.Plan{
Config: new(config.Config),
})
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -115,9 +118,9 @@ func TestApply_shutdown(t *testing.T) {
shutdownCh := make(chan struct{}) shutdownCh := make(chan struct{})
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
ShutdownCh: shutdownCh, ContextOpts: testCtxConfig(p),
TFConfig: testTFConfig(p), ShutdownCh: shutdownCh,
Ui: ui, Ui: ui,
} }
p.DiffFn = func( p.DiffFn = func(
@ -215,8 +218,8 @@ func TestApply_state(t *testing.T) {
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
// Run the apply command pointing to our existing state // Run the apply command pointing to our existing state
@ -262,8 +265,8 @@ func TestApply_stateNoExist(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{

View File

@ -8,17 +8,17 @@ import (
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func PlanArg( func ContextArg(
path string, path string,
statePath string, statePath string,
tf *terraform.Terraform) (*terraform.Plan, error) { opts *terraform.ContextOpts) (*terraform.Context, error) {
// First try to just read the plan directly from the path given. // First try to just read the plan directly from the path given.
f, err := os.Open(path) f, err := os.Open(path)
if err == nil { if err == nil {
plan, err := terraform.ReadPlan(f) plan, err := terraform.ReadPlan(f)
f.Close() f.Close()
if err == nil { if err == nil {
return plan, nil return plan.Context(opts), nil
} }
} }
@ -59,13 +59,13 @@ func PlanArg(
return nil, fmt.Errorf("Error validating config: %s", err) return nil, fmt.Errorf("Error validating config: %s", err)
} }
plan, err := tf.Plan(&terraform.PlanOpts{ opts.Config = config
Config: config, opts.State = state
State: state, ctx := terraform.NewContext(opts)
})
if err != nil { if _, err := ctx.Plan(nil); err != nil {
return nil, fmt.Errorf("Error running plan: %s", err) return nil, fmt.Errorf("Error running plan: %s", err)
} }
return plan, nil return ctx, nil
} }

View File

@ -16,8 +16,8 @@ func testFixturePath(name string) string {
return filepath.Join(fixtureDir, name, "main.tf") return filepath.Join(fixtureDir, name, "main.tf")
} }
func testTFConfig(p terraform.ResourceProvider) *terraform.Config { func testCtxConfig(p terraform.ResourceProvider) *terraform.ContextOpts {
return &terraform.Config{ return &terraform.ContextOpts{
Providers: map[string]terraform.ResourceProviderFactory{ Providers: map[string]terraform.ResourceProviderFactory{
"test": func() (terraform.ResourceProvider, error) { "test": func() (terraform.ResourceProvider, error) {
return p, nil return p, nil

View File

@ -15,8 +15,8 @@ import (
// GraphCommand is a Command implementation that takes a Terraform // GraphCommand is a Command implementation that takes a Terraform
// configuration and outputs the dependency tree in graphical form. // configuration and outputs the dependency tree in graphical form.
type GraphCommand struct { type GraphCommand struct {
TFConfig *terraform.Config ContextOpts *terraform.ContextOpts
Ui cli.Ui Ui cli.Ui
} }
func (c *GraphCommand) Run(args []string) int { func (c *GraphCommand) Run(args []string) int {
@ -41,7 +41,7 @@ func (c *GraphCommand) Run(args []string) int {
g, err := terraform.Graph(&terraform.GraphOpts{ g, err := terraform.Graph(&terraform.GraphOpts{
Config: conf, Config: conf,
Providers: c.TFConfig.Providers, Providers: c.ContextOpts.Providers,
}) })
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error creating graph: %s", err)) c.Ui.Error(fmt.Sprintf("Error creating graph: %s", err))

View File

@ -15,8 +15,8 @@ import (
// PlanCommand is a Command implementation that compares a Terraform // PlanCommand is a Command implementation that compares a Terraform
// configuration to an actual infrastructure and shows the differences. // configuration to an actual infrastructure and shows the differences.
type PlanCommand struct { type PlanCommand struct {
TFConfig *terraform.Config ContextOpts *terraform.ContextOpts
Ui cli.Ui Ui cli.Ui
} }
func (c *PlanCommand) Run(args []string) int { func (c *PlanCommand) Run(args []string) int {
@ -65,26 +65,19 @@ func (c *PlanCommand) Run(args []string) int {
return 1 return 1
} }
c.TFConfig.Hooks = append(c.TFConfig.Hooks, &UiHook{Ui: c.Ui}) c.ContextOpts.Config = b
tf, err := terraform.New(c.TFConfig) c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui})
if err != nil { c.ContextOpts.State = state
c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err)) ctx := terraform.NewContext(c.ContextOpts)
return 1
}
if refresh { if refresh {
state, err = tf.Refresh(b, state) if _, err := ctx.Refresh(); err != nil {
if err != nil {
c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
return 1 return 1
} }
} }
plan, err := tf.Plan(&terraform.PlanOpts{ plan, err := ctx.Plan(&terraform.PlanOpts{Destroy: destroy})
Config: b,
Destroy: destroy,
State: state,
})
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error running plan: %s", err)) c.Ui.Error(fmt.Sprintf("Error running plan: %s", err))
return 1 return 1

View File

@ -26,8 +26,8 @@ func TestPlan_destroy(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -51,8 +51,8 @@ func TestPlan_noState(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -87,8 +87,8 @@ func TestPlan_outPath(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
p.DiffReturn = &terraform.ResourceDiff{ p.DiffReturn = &terraform.ResourceDiff{
@ -118,8 +118,8 @@ func TestPlan_refresh(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{
@ -162,8 +162,8 @@ func TestPlan_state(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &PlanCommand{ c := &PlanCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
args := []string{ args := []string{

View File

@ -15,8 +15,8 @@ import (
// RefreshCommand is a cli.Command implementation that refreshes the state // RefreshCommand is a cli.Command implementation that refreshes the state
// file. // file.
type RefreshCommand struct { type RefreshCommand struct {
TFConfig *terraform.Config ContextOpts *terraform.ContextOpts
Ui cli.Ui Ui cli.Ui
} }
func (c *RefreshCommand) Run(args []string) int { func (c *RefreshCommand) Run(args []string) int {
@ -66,14 +66,11 @@ func (c *RefreshCommand) Run(args []string) int {
return 1 return 1
} }
c.TFConfig.Hooks = append(c.TFConfig.Hooks, &UiHook{Ui: c.Ui}) c.ContextOpts.Config = b
tf, err := terraform.New(c.TFConfig) c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui})
if err != nil { ctx := terraform.NewContext(c.ContextOpts)
c.Ui.Error(fmt.Sprintf("Error initializing Terraform: %s", err))
return 1
}
state, err = tf.Refresh(b, state) state, err = ctx.Refresh()
if err != nil { if err != nil {
c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
return 1 return 1

View File

@ -30,8 +30,8 @@ func TestRefresh(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &RefreshCommand{ c := &RefreshCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
p.RefreshFn = nil p.RefreshFn = nil
@ -96,8 +96,8 @@ func TestRefresh_outPath(t *testing.T) {
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &RefreshCommand{ c := &RefreshCommand{
TFConfig: testTFConfig(p), ContextOpts: testCtxConfig(p),
Ui: ui, Ui: ui,
} }
p.RefreshFn = nil p.RefreshFn = nil

View File

@ -11,12 +11,6 @@ import (
"github.com/mitchellh/osext" "github.com/mitchellh/osext"
) )
// TFConfig is the global base configuration that has the
// basic providers registered. Users of this configuration
// should copy it (call the Copy method) before using it so
// that it isn't corrupted.
var TFConfig terraform.Config
// Config is the structure of the configuration for the Terraform CLI. // Config is the structure of the configuration for the Terraform CLI.
// //
// This is not the configuration for Terraform itself. That is in the // This is not the configuration for Terraform itself. That is in the

View File

@ -66,7 +66,7 @@ func NewContext(opts *ContextOpts) *Context {
providers: opts.Providers, providers: opts.Providers,
variables: opts.Variables, variables: opts.Variables,
sh: sh, sh: sh,
} }
} }