sfn 3.0.30 → 3.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|