sfn 3.0.30 → 3.0.32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/bin/sfn +16 -14
- data/lib/chef/knife/knife_plugin_seed.rb +12 -12
- data/lib/sfn.rb +17 -17
- data/lib/sfn/api_provider.rb +3 -3
- data/lib/sfn/api_provider/google.rb +2 -2
- data/lib/sfn/api_provider/terraform.rb +2 -2
- data/lib/sfn/cache.rb +9 -9
- data/lib/sfn/callback.rb +6 -6
- data/lib/sfn/callback/aws_assume_role.rb +5 -5
- data/lib/sfn/callback/aws_mfa.rb +8 -6
- data/lib/sfn/callback/stack_policy.rb +15 -15
- data/lib/sfn/command.rb +37 -36
- data/lib/sfn/command/conf.rb +12 -12
- data/lib/sfn/command/create.rb +9 -9
- data/lib/sfn/command/describe.rb +6 -6
- data/lib/sfn/command/destroy.rb +8 -8
- data/lib/sfn/command/diff.rb +31 -31
- data/lib/sfn/command/events.rb +6 -6
- data/lib/sfn/command/export.rb +8 -8
- data/lib/sfn/command/graph.rb +21 -21
- data/lib/sfn/command/graph/aws.rb +34 -34
- data/lib/sfn/command/graph/provider.rb +1 -1
- data/lib/sfn/command/graph/terraform.rb +41 -41
- data/lib/sfn/command/import.rb +17 -17
- data/lib/sfn/command/init.rb +15 -15
- data/lib/sfn/command/inspect.rb +16 -16
- data/lib/sfn/command/lint.rb +6 -6
- data/lib/sfn/command/list.rb +2 -2
- data/lib/sfn/command/plan.rb +227 -0
- data/lib/sfn/command/print.rb +4 -4
- data/lib/sfn/command/promote.rb +2 -2
- data/lib/sfn/command/update.rb +19 -144
- data/lib/sfn/command/validate.rb +17 -13
- data/lib/sfn/command_module.rb +6 -5
- data/lib/sfn/command_module/base.rb +8 -8
- data/lib/sfn/command_module/callbacks.rb +5 -5
- data/lib/sfn/command_module/planning.rb +151 -0
- data/lib/sfn/command_module/stack.rb +34 -34
- data/lib/sfn/command_module/template.rb +50 -50
- data/lib/sfn/config.rb +46 -44
- data/lib/sfn/config/conf.rb +3 -3
- data/lib/sfn/config/create.rb +9 -9
- data/lib/sfn/config/describe.rb +7 -7
- data/lib/sfn/config/destroy.rb +1 -1
- data/lib/sfn/config/diff.rb +3 -3
- data/lib/sfn/config/events.rb +9 -9
- data/lib/sfn/config/export.rb +5 -5
- data/lib/sfn/config/graph.rb +10 -10
- data/lib/sfn/config/import.rb +4 -4
- data/lib/sfn/config/init.rb +1 -1
- data/lib/sfn/config/inspect.rb +16 -16
- data/lib/sfn/config/lint.rb +5 -5
- data/lib/sfn/config/list.rb +6 -6
- data/lib/sfn/config/plan.rb +28 -0
- data/lib/sfn/config/print.rb +5 -5
- data/lib/sfn/config/promote.rb +4 -4
- data/lib/sfn/config/update.rb +18 -18
- data/lib/sfn/config/validate.rb +30 -30
- data/lib/sfn/lint.rb +5 -5
- data/lib/sfn/lint/definition.rb +3 -3
- data/lib/sfn/lint/rule.rb +3 -3
- data/lib/sfn/lint/rule_set.rb +2 -2
- data/lib/sfn/monkey_patch.rb +2 -2
- data/lib/sfn/monkey_patch/stack.rb +27 -27
- data/lib/sfn/monkey_patch/stack/azure.rb +1 -1
- data/lib/sfn/monkey_patch/stack/google.rb +5 -5
- data/lib/sfn/planner.rb +4 -4
- data/lib/sfn/planner/aws.rb +114 -70
- data/lib/sfn/provider.rb +13 -13
- data/lib/sfn/utils.rb +10 -10
- data/lib/sfn/utils/debug.rb +2 -2
- data/lib/sfn/utils/json.rb +1 -1
- data/lib/sfn/utils/object_storage.rb +3 -3
- data/lib/sfn/utils/output.rb +4 -4
- data/lib/sfn/utils/path_selector.rb +15 -15
- data/lib/sfn/utils/ssher.rb +4 -4
- data/lib/sfn/utils/stack_exporter.rb +16 -16
- data/lib/sfn/utils/stack_parameter_scrubber.rb +6 -6
- data/lib/sfn/utils/stack_parameter_validator.rb +22 -22
- data/lib/sfn/version.rb +1 -1
- data/sfn.gemspec +32 -32
- metadata +16 -13
data/lib/sfn/command/import.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "stringio"
|
2
|
+
require "sfn"
|
3
3
|
|
4
4
|
module Sfn
|
5
5
|
class Command
|
@@ -12,27 +12,27 @@ module Sfn
|
|
12
12
|
|
13
13
|
# Run the import action
|
14
14
|
def execute!
|
15
|
-
raise NotImplementedError.new
|
15
|
+
raise NotImplementedError.new "Implementation updates required"
|
16
16
|
stack_name, json_file = name_args
|
17
|
-
ui.info "#{ui.color(
|
17
|
+
ui.info "#{ui.color("Stack Import:", :bold)} #{stack_name}"
|
18
18
|
unless json_file
|
19
19
|
entries = [].tap do |_entries|
|
20
|
-
_entries.push(
|
21
|
-
_entries.push(
|
20
|
+
_entries.push("s3") if config[:bucket]
|
21
|
+
_entries.push("fs") if config[:path]
|
22
22
|
end
|
23
23
|
if entries.size > 1
|
24
24
|
valid = false
|
25
25
|
until valid
|
26
|
-
answer = ui.ask_question(
|
26
|
+
answer = ui.ask_question("Import via file system (fs) or remote bucket (remote)?", :default => "remote")
|
27
27
|
valid = true if %w(remote fs).include?(answer)
|
28
28
|
entries = [answer]
|
29
29
|
end
|
30
30
|
elsif entries.size < 1
|
31
|
-
ui.fatal
|
31
|
+
ui.fatal "No path or bucket set. Unable to perform dynamic lookup!"
|
32
32
|
exit 1
|
33
33
|
end
|
34
34
|
case entries.first
|
35
|
-
when
|
35
|
+
when "remote"
|
36
36
|
json_file = remote_discovery
|
37
37
|
else
|
38
38
|
json_file = local_discovery
|
@@ -49,9 +49,9 @@ module Sfn
|
|
49
49
|
),
|
50
50
|
[stack_name]
|
51
51
|
)
|
52
|
-
ui.info
|
52
|
+
ui.info " - Starting creation of import"
|
53
53
|
creator.execute!
|
54
|
-
ui.info "#{ui.color(
|
54
|
+
ui.info "#{ui.color("Stack Import", :bold)} (#{json_file}): #{ui.color("complete", :green)}"
|
55
55
|
rescue => e
|
56
56
|
ui.fatal "Failed to import stack: #{e}"
|
57
57
|
debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
@@ -84,8 +84,8 @@ module Sfn
|
|
84
84
|
directory = storage.directories.get(config[:bucket])
|
85
85
|
file = prompt_for_file(
|
86
86
|
directory,
|
87
|
-
:directories_name =>
|
88
|
-
:files_names =>
|
87
|
+
:directories_name => "Collections",
|
88
|
+
:files_names => "Exports",
|
89
89
|
:filter_prefix => bucket_prefix,
|
90
90
|
)
|
91
91
|
if file
|
@@ -98,15 +98,15 @@ module Sfn
|
|
98
98
|
#
|
99
99
|
# @return [IO] stack export IO
|
100
100
|
def local_discovery
|
101
|
-
_, bucket = config[:path].split(
|
101
|
+
_, bucket = config[:path].split("/", 2)
|
102
102
|
storage = provider.service_for(:storage,
|
103
103
|
:provider => :local,
|
104
|
-
:local_root =>
|
104
|
+
:local_root => "/")
|
105
105
|
directory = storage.directories.get(bucket)
|
106
106
|
prompt_for_file(
|
107
107
|
directory,
|
108
|
-
:directories_name =>
|
109
|
-
:files_names =>
|
108
|
+
:directories_name => "Collections",
|
109
|
+
:files_names => "Exports",
|
110
110
|
)
|
111
111
|
end
|
112
112
|
end
|
data/lib/sfn/command/init.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "sfn"
|
2
|
+
require "fileutils"
|
3
3
|
|
4
4
|
module Sfn
|
5
5
|
class Command
|
@@ -8,15 +8,15 @@ module Sfn
|
|
8
8
|
include Sfn::CommandModule::Base
|
9
9
|
|
10
10
|
INIT_DIRECTORIES = [
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
"sparkleformation/dynamics",
|
12
|
+
"sparkleformation/components",
|
13
|
+
"sparkleformation/registry",
|
14
14
|
]
|
15
15
|
|
16
16
|
# Run the init command to initialize new project
|
17
17
|
def execute!
|
18
18
|
unless name_args.size == 1
|
19
|
-
raise ArgumentError.new
|
19
|
+
raise ArgumentError.new "Please provide path argument only for project initialization"
|
20
20
|
else
|
21
21
|
path = name_args.first
|
22
22
|
end
|
@@ -25,33 +25,33 @@ module Sfn
|
|
25
25
|
end
|
26
26
|
if File.directory?(path)
|
27
27
|
ui.warn "Project directory already exists at given path. (`#{path}`)"
|
28
|
-
ui.confirm
|
28
|
+
ui.confirm "Overwrite existing files?"
|
29
29
|
end
|
30
|
-
run_action
|
30
|
+
run_action "Creating base project directories" do
|
31
31
|
INIT_DIRECTORIES.each do |new_dir|
|
32
32
|
FileUtils.mkdir_p(File.join(path, new_dir))
|
33
33
|
end
|
34
34
|
nil
|
35
35
|
end
|
36
|
-
run_action
|
37
|
-
File.open(File.join(path,
|
36
|
+
run_action "Creating project bundle" do
|
37
|
+
File.open(File.join(path, "Gemfile"), "w") do |file|
|
38
38
|
file.puts "source 'https://rubygems.org'\n\ngem 'sfn'"
|
39
39
|
end
|
40
40
|
nil
|
41
41
|
end
|
42
|
-
ui.info
|
42
|
+
ui.info "Generating .sfn configuration file"
|
43
43
|
Dir.chdir(path) do
|
44
44
|
Conf.new({:generate => true}, []).execute!
|
45
45
|
end
|
46
|
-
ui.info
|
46
|
+
ui.info "Installing project bundle"
|
47
47
|
Dir.chdir(path) do
|
48
48
|
if defined?(Bundler)
|
49
|
-
Bundler.clean_system(
|
49
|
+
Bundler.clean_system("bundle install")
|
50
50
|
else
|
51
|
-
system(
|
51
|
+
system("bundle install")
|
52
52
|
end
|
53
53
|
end
|
54
|
-
ui.info
|
54
|
+
ui.info "Project initialization complete!"
|
55
55
|
ui.puts " Project path -> #{File.expand_path(path)}"
|
56
56
|
end
|
57
57
|
end
|
data/lib/sfn/command/inspect.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "sfn"
|
2
2
|
|
3
3
|
module Sfn
|
4
4
|
class Command
|
@@ -22,7 +22,7 @@ module Sfn
|
|
22
22
|
end.compact
|
23
23
|
end
|
24
24
|
if outputs.empty?
|
25
|
-
ui.info
|
25
|
+
ui.info " Stack dump:"
|
26
26
|
ui.puts MultiJson.dump(
|
27
27
|
MultiJson.load(
|
28
28
|
stack.reload.to_json
|
@@ -34,25 +34,25 @@ module Sfn
|
|
34
34
|
|
35
35
|
def display_instance_failure(stack)
|
36
36
|
instances = stack.resources.all.find_all do |resource|
|
37
|
-
resource.state.to_s.end_with?(
|
37
|
+
resource.state.to_s.end_with?("failed")
|
38
38
|
end.map do |resource|
|
39
39
|
# If compute instance, simply expand
|
40
40
|
if resource.within?(:compute, :servers)
|
41
41
|
resource.instance
|
42
42
|
# If a waitcondition, check for instance ID
|
43
|
-
elsif resource.type.to_s.downcase.end_with?(
|
44
|
-
if resource.status_reason.to_s.include?(
|
45
|
-
srv_id = resource.status_reason.split(
|
43
|
+
elsif resource.type.to_s.downcase.end_with?("waitcondition")
|
44
|
+
if resource.status_reason.to_s.include?("uniqueId")
|
45
|
+
srv_id = resource.status_reason.split(" ").last.strip
|
46
46
|
provider.connection.api_for(:compute).servers.get(srv_id)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end.compact
|
50
50
|
if instances.empty?
|
51
|
-
ui.error
|
51
|
+
ui.error "Failed to locate any failed instances"
|
52
52
|
else
|
53
53
|
log_path = config[:failure_log_path]
|
54
54
|
if log_path.to_s.empty?
|
55
|
-
log_path =
|
55
|
+
log_path = "/var/log/chef/client.log"
|
56
56
|
end
|
57
57
|
opts = ssh_key ? {:keys => [ssh_key]} : {}
|
58
58
|
instances.each do |instance|
|
@@ -84,7 +84,7 @@ module Sfn
|
|
84
84
|
#
|
85
85
|
# @return [Array<String>] usernames for ssh connect attempt
|
86
86
|
def ssh_attempt_users
|
87
|
-
[config[:ssh_user], config[:ssh_attempt_users], ENV[
|
87
|
+
[config[:ssh_user], config[:ssh_attempt_users], ENV["USER"]].flatten.compact.uniq
|
88
88
|
end
|
89
89
|
|
90
90
|
def ssh_key
|
@@ -93,14 +93,14 @@ module Sfn
|
|
93
93
|
|
94
94
|
def display_attribute(stack)
|
95
95
|
[config[:attribute]].flatten.compact.each do |stack_attribute|
|
96
|
-
attr = stack_attribute.split(
|
96
|
+
attr = stack_attribute.split(".").inject(stack) do |memo, key|
|
97
97
|
args = key.scan(/\(([^\)]*)\)/).flatten.first.to_s
|
98
98
|
if args
|
99
|
-
args = args.split(
|
100
|
-
key = key.split(
|
99
|
+
args = args.split(",").map { |a| a.to_i.to_s == a ? a.to_i : a }
|
100
|
+
key = key.split("(").first
|
101
101
|
end
|
102
102
|
if memo.public_methods.include?(key.to_sym)
|
103
|
-
if args.size == 1 && args.first.to_s.start_with?(
|
103
|
+
if args.size == 1 && args.first.to_s.start_with?("&")
|
104
104
|
memo.send(key, &args.first.slice(2, args.first.size).to_sym)
|
105
105
|
else
|
106
106
|
memo.send(*[key, args].flatten.compact)
|
@@ -152,11 +152,11 @@ module Sfn
|
|
152
152
|
end.compact
|
153
153
|
]
|
154
154
|
unless asg_nodes.empty?
|
155
|
-
ui.info
|
155
|
+
ui.info " AutoScale Group Instances:"
|
156
156
|
ui.puts MultiJson.dump(asg_nodes, :pretty => true)
|
157
157
|
end
|
158
158
|
unless compute_nodes.empty?
|
159
|
-
ui.info
|
159
|
+
ui.info " Compute Instances:"
|
160
160
|
ui.puts MultiJson.dump(compute_nodes, :pretty => true)
|
161
161
|
end
|
162
162
|
end
|
@@ -179,7 +179,7 @@ module Sfn
|
|
179
179
|
end
|
180
180
|
]
|
181
181
|
unless load_balancers.empty?
|
182
|
-
ui.info
|
182
|
+
ui.info " Load Balancer Instances:"
|
183
183
|
ui.puts MultiJson.dump(load_balancers, :pretty => true)
|
184
184
|
end
|
185
185
|
end
|
data/lib/sfn/command/lint.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "sfn"
|
2
2
|
|
3
3
|
module Sfn
|
4
4
|
class Command
|
@@ -12,7 +12,7 @@ module Sfn
|
|
12
12
|
print_only_original = config[:print_only]
|
13
13
|
config[:print_only] = true
|
14
14
|
file = load_template_file
|
15
|
-
ui.info "#{ui.color("Template Linting (#{provider.connection.provider}): ", :bold)} #{config[:file].sub(Dir.pwd,
|
15
|
+
ui.info "#{ui.color("Template Linting (#{provider.connection.provider}): ", :bold)} #{config[:file].sub(Dir.pwd, "").sub(%r{^/}, "")}"
|
16
16
|
config[:print_only] = print_only_original
|
17
17
|
|
18
18
|
raw_template = parameter_scrub!(template_content(file))
|
@@ -22,16 +22,16 @@ module Sfn
|
|
22
22
|
else
|
23
23
|
result = lint_template(raw_template)
|
24
24
|
if result == true
|
25
|
-
ui.info ui.color(
|
25
|
+
ui.info ui.color(" -> VALID", :green, :bold)
|
26
26
|
else
|
27
|
-
ui.info ui.color(
|
27
|
+
ui.info ui.color(" -> INVALID", :red, :bold)
|
28
28
|
result.each do |failure|
|
29
29
|
ui.error "Result Set: #{ui.color(failure[:rule_set].name, :red, :bold)}"
|
30
30
|
failure[:failures].each do |f_msg|
|
31
31
|
ui.fatal f_msg
|
32
32
|
end
|
33
33
|
end
|
34
|
-
raise
|
34
|
+
raise "Linting failure"
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -54,7 +54,7 @@ module Sfn
|
|
54
54
|
def rule_sets
|
55
55
|
sets = [config[:lint_directory]].flatten.compact.map do |directory|
|
56
56
|
if File.directory?(directory)
|
57
|
-
files = Dir.glob(File.join(directory,
|
57
|
+
files = Dir.glob(File.join(directory, "**", "**", "*.rb"))
|
58
58
|
files.map do |path|
|
59
59
|
begin
|
60
60
|
Sfn::Lint.class_eval(
|
data/lib/sfn/command/list.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "sfn"
|
2
2
|
|
3
3
|
module Sfn
|
4
4
|
class Command
|
@@ -15,7 +15,7 @@ module Sfn
|
|
15
15
|
allowed_attributes.each do |attr|
|
16
16
|
width_val = stacks.map { |e| e[attr].to_s.length }.push(attr.length).max + 2
|
17
17
|
width_val = width_val > 70 ? 70 : width_val < 20 ? 20 : width_val
|
18
|
-
column attr.split(
|
18
|
+
column attr.split("_").map(&:capitalize).join(" "), :width => width_val
|
19
19
|
end
|
20
20
|
end
|
21
21
|
get_stacks.each do |stack|
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require "sfn"
|
2
|
+
|
3
|
+
module Sfn
|
4
|
+
class Command
|
5
|
+
# Plan command
|
6
|
+
class Plan < Command
|
7
|
+
include Sfn::CommandModule::Base
|
8
|
+
include Sfn::CommandModule::Planning
|
9
|
+
include Sfn::CommandModule::Stack
|
10
|
+
include Sfn::CommandModule::Template
|
11
|
+
|
12
|
+
# Run the stack planning command
|
13
|
+
def execute!
|
14
|
+
name_required!
|
15
|
+
name = name_args.first
|
16
|
+
|
17
|
+
stack_info = "#{ui.color("Name:", :bold)} #{name}"
|
18
|
+
begin
|
19
|
+
stack = provider.stacks.get(name)
|
20
|
+
rescue Miasma::Error::ApiError::RequestError
|
21
|
+
stack = provider.stacks.build(name: name)
|
22
|
+
end
|
23
|
+
|
24
|
+
return display_plan_lists(stack) if config[:list]
|
25
|
+
|
26
|
+
if config[:plan_name]
|
27
|
+
# ensure custom attribute is dirty so we can modify
|
28
|
+
stack.custom = stack.custom.dup
|
29
|
+
stack.custom[:plan_name] = config[:plan_name]
|
30
|
+
end
|
31
|
+
|
32
|
+
use_existing = false
|
33
|
+
|
34
|
+
unless config[:print_only]
|
35
|
+
ui.info "#{ui.color("SparkleFormation:", :bold)} #{ui.color("plan", :green)}"
|
36
|
+
if stack && stack.plan
|
37
|
+
ui.warn "Found existing plan for this stack"
|
38
|
+
begin
|
39
|
+
ui.confirm "Destroy existing plan?"
|
40
|
+
ui.info "Destroying existing plan to generate new plan"
|
41
|
+
stack.plan.destroy
|
42
|
+
rescue Bogo::Ui::ConfirmationDeclined
|
43
|
+
ui.info "Loading existing stack plan for #{ui.color(stack.name, :bold)}..."
|
44
|
+
use_existing = true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
unless use_existing
|
50
|
+
config[:compile_parameters] ||= Smash.new
|
51
|
+
|
52
|
+
if config[:file]
|
53
|
+
s_name = [name]
|
54
|
+
|
55
|
+
c_setter = lambda do |c_stack|
|
56
|
+
if c_stack.outputs
|
57
|
+
compile_params = c_stack.outputs.detect do |output|
|
58
|
+
output.key == "CompileState"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if compile_params
|
62
|
+
compile_params = MultiJson.load(compile_params.value)
|
63
|
+
c_current = config[:compile_parameters].fetch(s_name.join("__"), Smash.new)
|
64
|
+
config[:compile_parameters][s_name.join("__")] = compile_params.merge(c_current)
|
65
|
+
end
|
66
|
+
c_stack.nested_stacks(false).each do |n_stack|
|
67
|
+
s_name.push(n_stack.data.fetch(:logical_id, n_stack.name))
|
68
|
+
c_setter.call(n_stack)
|
69
|
+
s_name.pop
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if stack && stack.persisted?
|
74
|
+
c_setter.call(stack)
|
75
|
+
end
|
76
|
+
|
77
|
+
ui.debug "Compile parameters - #{config[:compile_parameters]}"
|
78
|
+
file = load_template_file(:stack => stack)
|
79
|
+
stack_info << " #{ui.color("Path:", :bold)} #{config[:file]}"
|
80
|
+
else
|
81
|
+
file = stack.template.dup
|
82
|
+
end
|
83
|
+
|
84
|
+
unless file
|
85
|
+
if config[:template]
|
86
|
+
file = config[:template]
|
87
|
+
stack_info << " #{ui.color("(template provided)", :green)}"
|
88
|
+
else
|
89
|
+
stack_info << " #{ui.color("(no template update)", :yellow)}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
unless config[:print_only]
|
93
|
+
ui.info " -> #{stack_info}"
|
94
|
+
end
|
95
|
+
if file
|
96
|
+
if config[:print_only]
|
97
|
+
ui.puts format_json(parameter_scrub!(template_content(file)))
|
98
|
+
return
|
99
|
+
end
|
100
|
+
|
101
|
+
original_parameters = stack.parameters
|
102
|
+
|
103
|
+
apply_stacks!(stack)
|
104
|
+
|
105
|
+
populate_parameters!(file, :current_parameters => stack.root_parameters)
|
106
|
+
|
107
|
+
stack.parameters = config_root_parameters
|
108
|
+
|
109
|
+
if config[:upload_root_template]
|
110
|
+
upload_result = store_template(name, file, Smash.new)
|
111
|
+
stack.template_url = upload_result[:url]
|
112
|
+
else
|
113
|
+
stack.template = parameter_scrub!(template_content(file, :scrub))
|
114
|
+
end
|
115
|
+
else
|
116
|
+
apply_stacks!(stack)
|
117
|
+
original_parameters = stack.parameters
|
118
|
+
populate_parameters!(stack.template, :current_parameters => stack.root_parameters)
|
119
|
+
stack.parameters = config_root_parameters
|
120
|
+
end
|
121
|
+
|
122
|
+
# Set options defined within config into stack instance for update request
|
123
|
+
|
124
|
+
ui.info " -> Generating plan information..."
|
125
|
+
else
|
126
|
+
ui.info " -> Loading plan information..."
|
127
|
+
end
|
128
|
+
|
129
|
+
plan = stack.plan || stack.plan_generate
|
130
|
+
|
131
|
+
begin
|
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
|
180
|
+
end
|
181
|
+
|
182
|
+
# Display plan list in table form
|
183
|
+
#
|
184
|
+
# @param [Miasma::Models::Orchestration::Stack]
|
185
|
+
def display_plan_lists(stack)
|
186
|
+
unless stack
|
187
|
+
raise "Failed to locate requested stack `#{name_args.first}`"
|
188
|
+
end
|
189
|
+
plans = stack.plans.all
|
190
|
+
if plans.empty?
|
191
|
+
ui.warn "No plans found for stack `#{stack.name}`"
|
192
|
+
return
|
193
|
+
end
|
194
|
+
ui.info "Plans for stack: #{ui.color(stack.name, :bold)}\n"
|
195
|
+
n_width = "Plan Name".length
|
196
|
+
i_width = "Plan ID".length
|
197
|
+
s_width = "Plan State".length
|
198
|
+
c_width = "Created".length
|
199
|
+
plan_info = plans.map do |plan|
|
200
|
+
plan_id = plan.id.to_s.split("/").last
|
201
|
+
n_width = plan.name.to_s.length if plan.name.to_s.length > n_width
|
202
|
+
i_width = plan_id.to_s.length if plan_id.length > i_width
|
203
|
+
s_width = plan.state.to_s.length if plan.state.to_s.length > s_width
|
204
|
+
c_width = plan.created_at.to_s.length if plan.created_at.to_s.length > c_width
|
205
|
+
[plan.name, plan_id, plan.state, plan.created_at]
|
206
|
+
end
|
207
|
+
table = ui.table(self) do
|
208
|
+
table(:border => false) do
|
209
|
+
row(:header => true) do
|
210
|
+
column "Plan Name", :width => n_width + 5
|
211
|
+
column "Plan ID", :width => i_width + 5
|
212
|
+
column "Plan State", :width => s_width + 5
|
213
|
+
column "Created", :width => c_width + 5
|
214
|
+
end
|
215
|
+
plan_info.sort_by(&:first).each do |plan|
|
216
|
+
row do
|
217
|
+
plan.each do |item|
|
218
|
+
column item
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end.display
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|