@@ -20,100 +20,75 @@ type ApplyCommand struct {
2020
2121func (c * ApplyCommand ) Run (args []string ) int {
2222 var init bool
23- var statePath , stateOutPath string
23+ var stateOutPath string
2424
2525 cmdFlags := flag .NewFlagSet ("apply" , flag .ContinueOnError )
2626 cmdFlags .BoolVar (& init , "init" , false , "init" )
27- cmdFlags .StringVar (& statePath , "state" , "terraform.tfstate" , "path" )
28- cmdFlags .StringVar (& stateOutPath , "state-out" , "" , "path" )
27+ cmdFlags .StringVar (& stateOutPath , "out" , "" , "path" )
2928 cmdFlags .Usage = func () { c .Ui .Error (c .Help ()) }
3029 if err := cmdFlags .Parse (args ); err != nil {
3130 return 1
3231 }
3332
3433 args = cmdFlags .Args ()
35- if len (args ) != 1 {
36- c .Ui .Error (
37- "The apply command expects only one argument with the path\n " +
38- "to a Terraform configuration.\n " )
34+ if len (args ) != 2 {
35+ c .Ui .Error ("The apply command expects two arguments.\n " )
3936 cmdFlags .Usage ()
4037 return 1
4138 }
4239
43- if statePath == "" {
44- c .Ui .Error ("-state cannot be blank" )
45- return 1
46- }
40+ statePath := args [0 ]
41+ configPath := args [1 ]
42+
4743 if stateOutPath == "" {
4844 stateOutPath = statePath
4945 }
50- if ! init {
51- if _ , err := os .Stat (statePath ); err != nil {
52- c .Ui .Error (fmt .Sprintf (
53- "There was an error reading the state file. The path\n " +
54- "and error are shown below. If you're trying to build a\n " +
55- "brand new infrastructure, explicitly pass the '-init'\n " +
56- "flag to Terraform to tell it it is okay to build new\n " +
57- "state.\n \n " +
58- "Path: %s\n " +
59- "Error: %s" ,
60- statePath ,
61- err ))
62- return 1
63- }
64- }
65-
66- // Load up the state
67- var state * terraform.State
68- if ! init {
69- f , err := os .Open (statePath )
70- if err != nil {
71- c .Ui .Error (fmt .Sprintf ("Error loading state: %s" , err ))
72- return 1
73- }
74-
75- state , err = terraform .ReadState (f )
76- f .Close ()
77- if err != nil {
78- c .Ui .Error (fmt .Sprintf ("Error loading state: %s" , err ))
79- return 1
80- }
81- }
82-
83- b , err := config .Load (args [0 ])
84- if err != nil {
85- c .Ui .Error (fmt .Sprintf ("Error loading blueprint: %s" , err ))
86- return 1
87- }
8846
47+ // Initialize Terraform right away
8948 c .TFConfig .Hooks = append (c .TFConfig .Hooks , & UiHook {Ui : c .Ui })
9049 tf , err := terraform .New (c .TFConfig )
9150 if err != nil {
9251 c .Ui .Error (fmt .Sprintf ("Error initializing Terraform: %s" , err ))
9352 return 1
9453 }
9554
96- plan , err := tf .Plan (b , state , nil )
97- if err != nil {
98- c .Ui .Error (fmt .Sprintf ("Error running plan: %s" , err ))
99- return 1
55+ // Attempt to read a plan from the path given. This is how we test that
56+ // it is a plan or not (kind of jank, but if it quacks like a duck...)
57+ var plan * terraform.Plan
58+ f , err := os .Open (configPath )
59+ if err == nil {
60+ plan , err = terraform .ReadPlan (f )
61+ f .Close ()
62+ if err != nil {
63+ // Make sure the plan is nil so that we try to load as
64+ // configuration.
65+ plan = nil
66+ }
67+ }
68+
69+ if plan == nil {
70+ // No plan was given, so we're loading from configuration. Generate
71+ // the plan given the configuration.
72+ plan , err = c .configToPlan (tf , init , statePath , configPath )
73+ if err != nil {
74+ c .Ui .Error (err .Error ())
75+ return 1
76+ }
10077 }
10178
102- state , err = tf .Apply (plan )
79+ state , err : = tf .Apply (plan )
10380 if err != nil {
10481 c .Ui .Error (fmt .Sprintf ("Error applying plan: %s" , err ))
10582 return 1
10683 }
10784
10885 // Write state out to the file
109- f , err : = os .Create (stateOutPath )
110- if err ! = nil {
111- c . Ui . Error ( fmt . Sprintf ( "Failed to save state: %s" , err ) )
112- return 1
86+ f , err = os .Create (stateOutPath )
87+ if err = = nil {
88+ err = terraform . WriteState ( state , f )
89+ f . Close ()
11390 }
114- defer f .Close ()
115-
116- if err := terraform .WriteState (state , f ); err != nil {
91+ if err != nil {
11792 c .Ui .Error (fmt .Sprintf ("Failed to save state: %s" , err ))
11893 return 1
11994 }
@@ -125,7 +100,7 @@ func (c *ApplyCommand) Run(args []string) int {
125100
126101func (c * ApplyCommand ) Help () string {
127102 helpText := `
128- Usage: terraform apply [terraform.tf]
103+ Usage: terraform apply [options] STATE PATH
129104
130105 Builds or changes infrastructure according to the Terraform configuration
131106 file.
@@ -135,17 +110,60 @@ Options:
135110 -init If specified, it is okay to build brand new
136111 infrastructure (with no state file specified).
137112
138- -state=terraform.tfstate Path to the state file to build off of. This file
139- will also be written to with updated state unless
140- -state-out is specified.
141-
142- -state-out=file.tfstate Path to save the new state. If not specified, the
143- -state value will be used.
113+ -out=file.tfstate Path to save the new state. If not specified, the
114+ state path argument will be used.
144115
145116`
146117 return strings .TrimSpace (helpText )
147118}
148119
149120func (c * ApplyCommand ) Synopsis () string {
150- return "Builds or changes infrastructure according to Terrafiles"
121+ return "Builds or changes infrastructure"
122+ }
123+
124+ func (c * ApplyCommand ) configToPlan (
125+ tf * terraform.Terraform ,
126+ init bool ,
127+ statePath string ,
128+ configPath string ) (* terraform.Plan , error ) {
129+ if ! init {
130+ if _ , err := os .Stat (statePath ); err != nil {
131+ return nil , fmt .Errorf (
132+ "There was an error reading the state file. The path\n " +
133+ "and error are shown below. If you're trying to build a\n " +
134+ "brand new infrastructure, explicitly pass the '-init'\n " +
135+ "flag to Terraform to tell it it is okay to build new\n " +
136+ "state.\n \n " +
137+ "Path: %s\n " +
138+ "Error: %s" ,
139+ statePath ,
140+ err )
141+ }
142+ }
143+
144+ // Load up the state
145+ var state * terraform.State
146+ if ! init {
147+ f , err := os .Open (statePath )
148+ if err == nil {
149+ state , err = terraform .ReadState (f )
150+ f .Close ()
151+ }
152+
153+ if err != nil {
154+ return nil , fmt .Errorf ("Error loading state: %s" , err )
155+ }
156+ }
157+
158+ config , err := config .Load (configPath )
159+ if err != nil {
160+ return nil , fmt .Errorf ("Error loading config: %s" , err )
161+ }
162+
163+ plan , err := tf .Plan (config , state , nil )
164+ if err != nil {
165+ return nil , fmt .Errorf ("Error running plan: %s" , err )
166+ }
167+
168+ return plan , nil
151169}
0 commit comments