sfn 3.0.32 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/bin/command-config-generator +34 -14
- data/docs/command-config.md +338 -1
- data/docs/commands.md +56 -10
- data/docs/usage.md +1 -0
- data/lib/sfn.rb +1 -0
- data/lib/sfn/command.rb +1 -0
- data/lib/sfn/command/import.rb +8 -1
- data/lib/sfn/command/plan.rb +8 -51
- data/lib/sfn/command/realize.rb +82 -0
- data/lib/sfn/command/update.rb +1 -1
- data/lib/sfn/command_module/planning.rb +6 -2
- data/lib/sfn/command_module/stack.rb +11 -3
- data/lib/sfn/command_module/template.rb +5 -1
- data/lib/sfn/config.rb +9 -1
- data/lib/sfn/config/plan.rb +18 -0
- data/lib/sfn/config/realize.rb +13 -0
- data/lib/sfn/config/validate.rb +8 -1
- data/lib/sfn/error.rb +61 -0
- data/lib/sfn/version.rb +1 -1
- metadata +5 -3
- data/lib/chef/knife/knife_plugin_seed.rb +0 -117
data/docs/commands.md
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
title: "CLI Commands"
|
3
3
|
weight: 4
|
4
4
|
anchors:
|
5
|
-
- title: "Lifecycle
|
6
|
-
url: "#lifecycle
|
7
|
-
- title: "
|
8
|
-
url: "#
|
9
|
-
- title: "Configuration
|
10
|
-
url: "#configuration
|
5
|
+
- title: "Lifecycle"
|
6
|
+
url: "#lifecycle"
|
7
|
+
- title: "Informational"
|
8
|
+
url: "#informational"
|
9
|
+
- title: "Configuration"
|
10
|
+
url: "#configuration"
|
11
11
|
---
|
12
12
|
|
13
13
|
## SparkleFormation CLI commands
|
@@ -16,7 +16,7 @@ The `sfn` command provides a collection of subcommands to handle stack
|
|
16
16
|
lifecycles as well as subcommands to aid in inspection of existing
|
17
17
|
stacks.
|
18
18
|
|
19
|
-
### Lifecycle
|
19
|
+
### Lifecycle
|
20
20
|
|
21
21
|
#### Stack create
|
22
22
|
|
@@ -172,6 +172,52 @@ Example of stack update:
|
|
172
172
|
|
173
173
|
![stack update](./images/d_update.png)
|
174
174
|
|
175
|
+
#### Stack plan
|
176
|
+
|
177
|
+
The `plan` command allows for a stack to be created or updated via `sfn`. It uses the
|
178
|
+
planning functionality of the provider to include planning information prior to
|
179
|
+
application of the creation/modifications. The `plan` command is currently only
|
180
|
+
supported with the AWS provider. When using the `update` command, the plan generation
|
181
|
+
uses the internal planner with `sfn` and supports showing resource for nested stacks.
|
182
|
+
This is currently not supported with the remote planning API.
|
183
|
+
|
184
|
+
Available options for the `plan` command are similar to the `create` and `update`
|
185
|
+
commands. Additionally, available plans can be listed for a given stack:
|
186
|
+
|
187
|
+
~~~
|
188
|
+
$ sfn plan my-stack --list
|
189
|
+
~~~
|
190
|
+
|
191
|
+
By default the plan will be created with a default name (`miasma-changeset-STACKNAME`). A
|
192
|
+
custom name can be used for the plan:
|
193
|
+
|
194
|
+
~~~
|
195
|
+
$ sfn plan my-stack --file my_template --plan-name custom-plan-name
|
196
|
+
~~~
|
197
|
+
|
198
|
+
It is also possible to apply an existing plan for a stack by providing the plan name:
|
199
|
+
|
200
|
+
~~~
|
201
|
+
$ sfn plan my-stack --plan-name existing-plan-name
|
202
|
+
~~~
|
203
|
+
|
204
|
+
#### Stack realize
|
205
|
+
|
206
|
+
The `realize` command loads an existing plan for a given stack and executes the plan. This
|
207
|
+
is useful to allow generating plans and then executing a plan at a later time. For example
|
208
|
+
a stack plan can be first generated and not applied:
|
209
|
+
|
210
|
+
~~~
|
211
|
+
$ sfn plan my-stack --file my_template --plan-only
|
212
|
+
~~~
|
213
|
+
|
214
|
+
This generates the plan and displays the result but does not apply the changes. The plan
|
215
|
+
can then be loaded and applied using the `realize` command:
|
216
|
+
|
217
|
+
~~~
|
218
|
+
$ sfn realize my-stack
|
219
|
+
~~~
|
220
|
+
|
175
221
|
#### Stack destroy
|
176
222
|
|
177
223
|
Existing stacks can be destroyed via `sfn`:
|
@@ -209,7 +255,7 @@ Example of stack destroy:
|
|
209
255
|
|
210
256
|
![stack destroy](./images/d_destroy.png)
|
211
257
|
|
212
|
-
###
|
258
|
+
### Informational
|
213
259
|
|
214
260
|
#### Stack list
|
215
261
|
|
@@ -386,7 +432,7 @@ is installed on the local system, an image can be generated directly:
|
|
386
432
|
$ sfn graph --file my_template --graph-type png
|
387
433
|
~~~
|
388
434
|
|
389
|
-
### Configuration
|
435
|
+
### Configuration
|
390
436
|
|
391
437
|
To aid setup and configuration, `sfn` provides a configuration helper:
|
392
438
|
|
@@ -399,4 +445,4 @@ one automatically generated for them:
|
|
399
445
|
|
400
446
|
~~~
|
401
447
|
$ sfn conf --generate
|
402
|
-
~~~
|
448
|
+
~~~
|
data/docs/usage.md
CHANGED
data/lib/sfn.rb
CHANGED
data/lib/sfn/command.rb
CHANGED
@@ -21,6 +21,7 @@ module Sfn
|
|
21
21
|
autoload :Plan, "sfn/command/plan"
|
22
22
|
autoload :Print, "sfn/command/print"
|
23
23
|
autoload :Promote, "sfn/command/promote"
|
24
|
+
autoload :Realize, "sfn/command/realize"
|
24
25
|
autoload :Update, "sfn/command/update"
|
25
26
|
autoload :Validate, "sfn/command/validate"
|
26
27
|
|
data/lib/sfn/command/import.rb
CHANGED
@@ -23,7 +23,14 @@ module Sfn
|
|
23
23
|
if entries.size > 1
|
24
24
|
valid = false
|
25
25
|
until valid
|
26
|
-
|
26
|
+
if config[:interactive_parameters]
|
27
|
+
answer = ui.ask_question(
|
28
|
+
"Import via file system (fs) or remote bucket (remote)?",
|
29
|
+
:default => "remote",
|
30
|
+
)
|
31
|
+
else
|
32
|
+
answer = "remote"
|
33
|
+
end
|
27
34
|
valid = true if %w(remote fs).include?(answer)
|
28
35
|
entries = [answer]
|
29
36
|
end
|
data/lib/sfn/command/plan.rb
CHANGED
@@ -36,7 +36,12 @@ module Sfn
|
|
36
36
|
if stack && stack.plan
|
37
37
|
ui.warn "Found existing plan for this stack"
|
38
38
|
begin
|
39
|
-
|
39
|
+
if config[:load_existing]
|
40
|
+
raise Bogo::Ui::ConfirmationDeclined
|
41
|
+
end
|
42
|
+
if config[:load_existing].nil?
|
43
|
+
ui.confirm "Destroy existing plan"
|
44
|
+
end
|
40
45
|
ui.info "Destroying existing plan to generate new plan"
|
41
46
|
stack.plan.destroy
|
42
47
|
rescue Bogo::Ui::ConfirmationDeclined
|
@@ -127,56 +132,8 @@ module Sfn
|
|
127
132
|
end
|
128
133
|
|
129
134
|
plan = stack.plan || stack.plan_generate
|
130
|
-
|
131
|
-
|
132
|
-
display_plan_information(plan)
|
133
|
-
rescue Bogo::Ui::ConfirmationDeclined
|
134
|
-
stack.reload
|
135
|
-
if (stack.template.nil? || stack.template.empty?) && stack.state == :unknown
|
136
|
-
ui.auto_confirm = false
|
137
|
-
ui.warn "Stack appears to be empty and should be destroyed"
|
138
|
-
ui.confirm "Destroy stack?"
|
139
|
-
stack.destroy
|
140
|
-
poll_stack(stack.name)
|
141
|
-
else
|
142
|
-
ui.confirm "Destroy generated plan?"
|
143
|
-
plan.destroy
|
144
|
-
end
|
145
|
-
raise
|
146
|
-
end
|
147
|
-
|
148
|
-
if config[:merge_api_options]
|
149
|
-
config.fetch(:options, Smash.new).each_pair do |key, value|
|
150
|
-
if stack.respond_to?("#{key}=")
|
151
|
-
stack.send("#{key}=", value)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
begin
|
157
|
-
api_action!(:api_stack => stack) do
|
158
|
-
stack.plan_execute
|
159
|
-
if config[:poll]
|
160
|
-
poll_stack(stack.name)
|
161
|
-
if stack.reload.state == :update_complete || stack.reload.state == :create_complete
|
162
|
-
ui.info "Stack plan apply complete: #{ui.color("SUCCESS", :green)}"
|
163
|
-
namespace.const_get(:Describe).new({:outputs => true}, [name]).execute!
|
164
|
-
else
|
165
|
-
ui.fatal "Update of stack #{ui.color(name, :bold)}: #{ui.color("FAILED", :red, :bold)}"
|
166
|
-
raise "Stack did not reach a successful completion state."
|
167
|
-
end
|
168
|
-
else
|
169
|
-
ui.warn "Stack state polling has been disabled."
|
170
|
-
ui.info "Stack plan apply initialized for #{ui.color(name, :green)}"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
rescue Miasma::Error::ApiError::RequestError => e
|
174
|
-
if e.message.downcase.include?("no updates")
|
175
|
-
ui.warn "No changes detected for stack (#{stack.name})"
|
176
|
-
else
|
177
|
-
raise
|
178
|
-
end
|
179
|
-
end
|
135
|
+
namespace.const_get(:Realize).
|
136
|
+
new(config, [name]).execute!
|
180
137
|
end
|
181
138
|
|
182
139
|
# Display plan list in table form
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "sfn"
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
class Command
|
5
|
+
# Realize command
|
6
|
+
class Realize < Command
|
7
|
+
include Sfn::CommandModule::Base
|
8
|
+
include Sfn::CommandModule::Planning
|
9
|
+
|
10
|
+
# Run the stack realize command
|
11
|
+
def execute!
|
12
|
+
name_required!
|
13
|
+
name = name_args.first
|
14
|
+
|
15
|
+
stack_info = "#{ui.color("Name:", :bold)} #{name}"
|
16
|
+
begin
|
17
|
+
stack = provider.stacks.get(name)
|
18
|
+
rescue Miasma::Error::ApiError::RequestError
|
19
|
+
raise Error::StackNotFound,
|
20
|
+
"Failed to locate stack: #{name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
if config[:plan_name]
|
24
|
+
ui.debug "Setting custom plan name - #{config[:plan_name]}"
|
25
|
+
# ensure custom attribute is dirty so we can modify
|
26
|
+
stack.custom = stack.custom.dup
|
27
|
+
stack.custom[:plan_name] = config[:plan_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
ui.info " -> Loading plan information..."
|
31
|
+
|
32
|
+
plan = stack.plan
|
33
|
+
if plan.nil?
|
34
|
+
raise Error::StackPlanNotFound,
|
35
|
+
"Failed to locate plan for stack `#{name}`"
|
36
|
+
end
|
37
|
+
|
38
|
+
display_plan_information(plan)
|
39
|
+
|
40
|
+
return if config[:plan_only]
|
41
|
+
|
42
|
+
if config[:merge_api_options]
|
43
|
+
config.fetch(:options, Smash.new).each_pair do |key, value|
|
44
|
+
if stack.respond_to?("#{key}=")
|
45
|
+
stack.send("#{key}=", value)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
api_action!(:api_stack => stack) do
|
52
|
+
stack.plan_execute
|
53
|
+
if config[:poll]
|
54
|
+
poll_stack(stack.name)
|
55
|
+
if [:update_complete, :create_complete].
|
56
|
+
include?(stack.reload.state)
|
57
|
+
ui.info "Stack plan apply complete: " \
|
58
|
+
"#{ui.color("SUCCESS", :green)}"
|
59
|
+
namespace.const_get(:Describe).
|
60
|
+
new({:outputs => true}, [name]).execute!
|
61
|
+
else
|
62
|
+
ui.fatal "Update of stack #{ui.color(name, :bold)}: " \
|
63
|
+
"#{ui.color("FAILED", :red, :bold)}"
|
64
|
+
raise Error::StackStateIncomplete
|
65
|
+
end
|
66
|
+
else
|
67
|
+
ui.warn "Stack state polling has been disabled."
|
68
|
+
ui.info "Stack plan apply initialized for " \
|
69
|
+
"#{ui.color(name, :green)}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
rescue Miasma::Error::ApiError::RequestError => e
|
73
|
+
if e.message.downcase.include?("no updates")
|
74
|
+
ui.warn "No changes detected for stack (#{stack.name})"
|
75
|
+
else
|
76
|
+
raise
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/sfn/command/update.rb
CHANGED
@@ -27,8 +27,12 @@ module Sfn
|
|
27
27
|
unless print_plan_result(result, [result.name])
|
28
28
|
ui.info "No resources life cycle changes detected in this update!"
|
29
29
|
end
|
30
|
-
|
31
|
-
|
30
|
+
if config[:plan_apply]
|
31
|
+
return ui.info "Realizing this stack plan..."
|
32
|
+
elsif config[:plan_only]
|
33
|
+
return
|
34
|
+
end
|
35
|
+
ui.confirm "Realize this stack plan?"
|
32
36
|
end
|
33
37
|
|
34
38
|
# Print plan information to the UI
|
@@ -340,9 +340,17 @@ module Sfn
|
|
340
340
|
end
|
341
341
|
if current_value && current_value.to_s != stack_value.to_s
|
342
342
|
if config[:parameter_validation] == "default"
|
343
|
-
ui.warn "Nested stack has been altered directly!
|
344
|
-
|
345
|
-
|
343
|
+
ui.warn "Nested stack has been altered directly! " \
|
344
|
+
"This update may cause unexpected modifications!"
|
345
|
+
ui.warn "Stack name: #{c_stack.name}. Parameter: #{p_key}. " \
|
346
|
+
"Current value: #{stack_value}. Expected value: #{current_value} " \
|
347
|
+
"(via: #{c_value.inspect})"
|
348
|
+
if config[:interactive_parameters]
|
349
|
+
answer = ui.ask_question("Use current value or expected value for #{p_key} " \
|
350
|
+
"[current/expected]?", :valid => ["current", "expected"])
|
351
|
+
else
|
352
|
+
raise Error::InteractionDisabled
|
353
|
+
end
|
346
354
|
else
|
347
355
|
answer = config[:parameter_validation]
|
348
356
|
end
|
@@ -588,7 +588,11 @@ module Sfn
|
|
588
588
|
ui.puts "#{output.join("\n")}\n"
|
589
589
|
response = nil
|
590
590
|
until valid[response]
|
591
|
-
|
591
|
+
if config[:interactive_parameters]
|
592
|
+
response = ui.ask_question("Enter selection").to_i
|
593
|
+
else
|
594
|
+
raise Error::InteractionDisabled
|
595
|
+
end
|
592
596
|
end
|
593
597
|
entry = valid[response]
|
594
598
|
if entry[:type] == :collection
|
data/lib/sfn/config.rb
CHANGED
@@ -38,7 +38,9 @@ module Sfn
|
|
38
38
|
end
|
39
39
|
|
40
40
|
# Only values allowed designating bool type
|
41
|
-
BOOLEAN = BOOLEAN_VALUES = [TrueClass, FalseClass]
|
41
|
+
BOOLEAN = BOOLEAN_VALUES = [TrueClass, FalseClass].freeze
|
42
|
+
# Boolean type with nil included
|
43
|
+
TRISTATE_BOOLEAN = (BOOLEAN + [NilClass]).freeze
|
42
44
|
|
43
45
|
autoload :Conf, "sfn/config/conf"
|
44
46
|
autoload :Create, "sfn/config/create"
|
@@ -57,6 +59,7 @@ module Sfn
|
|
57
59
|
autoload :Plan, "sfn/config/plan"
|
58
60
|
autoload :Print, "sfn/config/print"
|
59
61
|
autoload :Promote, "sfn/config/promote"
|
62
|
+
autoload :Realize, "sfn/config/realize"
|
60
63
|
autoload :Update, "sfn/config/update"
|
61
64
|
autoload :Validate, "sfn/config/validate"
|
62
65
|
|
@@ -104,6 +107,11 @@ module Sfn
|
|
104
107
|
:description => "Enable debug output",
|
105
108
|
:short_flag => "u",
|
106
109
|
)
|
110
|
+
attribute(
|
111
|
+
:log, String,
|
112
|
+
:description => "Enable logging with given level",
|
113
|
+
:short_flag => "G",
|
114
|
+
)
|
107
115
|
attribute(
|
108
116
|
:colors, [TrueClass, FalseClass],
|
109
117
|
:description => "Enable colorized output",
|
data/lib/sfn/config/plan.rb
CHANGED
@@ -17,6 +17,24 @@ module Sfn
|
|
17
17
|
:description => "Custom plan name or ID (not applicable to all providers)",
|
18
18
|
)
|
19
19
|
|
20
|
+
attribute(
|
21
|
+
:load_existing, TRISTATE_BOOLEAN,
|
22
|
+
:description => "Load existing plan if exists",
|
23
|
+
:default => nil,
|
24
|
+
)
|
25
|
+
|
26
|
+
attribute(
|
27
|
+
:auto_destroy_stack, TRISTATE_BOOLEAN,
|
28
|
+
:description => "Automatically destroy empty stack",
|
29
|
+
:default => nil,
|
30
|
+
)
|
31
|
+
|
32
|
+
attribute(
|
33
|
+
:auto_destroy_plan, TRISTATE_BOOLEAN,
|
34
|
+
:description => "Automatically destroy generated plan",
|
35
|
+
:default => nil,
|
36
|
+
)
|
37
|
+
|
20
38
|
attribute(
|
21
39
|
:list, BOOLEAN,
|
22
40
|
:description => "List all available plans for stack",
|
data/lib/sfn/config/validate.rb
CHANGED
@@ -81,7 +81,14 @@ module Sfn
|
|
81
81
|
result = Smash.new
|
82
82
|
v.split(",").each do |item_pair|
|
83
83
|
key, value = item_pair.split(/[=:]/, 2)
|
84
|
-
result[key]
|
84
|
+
if !result[key]
|
85
|
+
result[key] = value
|
86
|
+
next
|
87
|
+
end
|
88
|
+
if !result.is_a?(Array)
|
89
|
+
result[key] = [result[key]]
|
90
|
+
end
|
91
|
+
result[key] << value
|
85
92
|
end
|
86
93
|
result
|
87
94
|
when Hash
|