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.rb
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
|
+
require "bogo-cli"
|
|
3
3
|
|
|
4
4
|
module Sfn
|
|
5
5
|
class Command < Bogo::Cli::Command
|
|
6
6
|
include CommandModule::Callbacks
|
|
7
7
|
|
|
8
|
-
autoload :Conf,
|
|
9
|
-
autoload :Create,
|
|
10
|
-
autoload :Describe,
|
|
11
|
-
autoload :Destroy,
|
|
12
|
-
autoload :Diff,
|
|
13
|
-
autoload :Events,
|
|
14
|
-
autoload :Export,
|
|
15
|
-
autoload :Graph,
|
|
16
|
-
autoload :Import,
|
|
17
|
-
autoload :Init,
|
|
18
|
-
autoload :Inspect,
|
|
19
|
-
autoload :Lint,
|
|
20
|
-
autoload :List,
|
|
21
|
-
autoload :
|
|
22
|
-
autoload :
|
|
23
|
-
autoload :
|
|
24
|
-
autoload :
|
|
8
|
+
autoload :Conf, "sfn/command/conf"
|
|
9
|
+
autoload :Create, "sfn/command/create"
|
|
10
|
+
autoload :Describe, "sfn/command/describe"
|
|
11
|
+
autoload :Destroy, "sfn/command/destroy"
|
|
12
|
+
autoload :Diff, "sfn/command/diff"
|
|
13
|
+
autoload :Events, "sfn/command/events"
|
|
14
|
+
autoload :Export, "sfn/command/export"
|
|
15
|
+
autoload :Graph, "sfn/command/graph"
|
|
16
|
+
autoload :Import, "sfn/command/import"
|
|
17
|
+
autoload :Init, "sfn/command/init"
|
|
18
|
+
autoload :Inspect, "sfn/command/inspect"
|
|
19
|
+
autoload :Lint, "sfn/command/lint"
|
|
20
|
+
autoload :List, "sfn/command/list"
|
|
21
|
+
autoload :Plan, "sfn/command/plan"
|
|
22
|
+
autoload :Print, "sfn/command/print"
|
|
23
|
+
autoload :Promote, "sfn/command/promote"
|
|
24
|
+
autoload :Update, "sfn/command/update"
|
|
25
|
+
autoload :Validate, "sfn/command/validate"
|
|
25
26
|
|
|
26
27
|
# Base name of configuration file
|
|
27
|
-
CONFIG_BASE_NAME =
|
|
28
|
+
CONFIG_BASE_NAME = ".sfn"
|
|
28
29
|
|
|
29
30
|
# Supported configuration file extensions
|
|
30
31
|
VALID_CONFIG_EXTENSIONS = [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
"",
|
|
33
|
+
".rb",
|
|
34
|
+
".json",
|
|
35
|
+
".yaml",
|
|
36
|
+
".yml",
|
|
37
|
+
".xml",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
# Override to provide config file searching
|
|
40
41
|
def initialize(cli_opts, args)
|
|
41
|
-
unless cli_opts[
|
|
42
|
+
unless cli_opts["config"]
|
|
42
43
|
discover_config(cli_opts)
|
|
43
44
|
end
|
|
44
|
-
unless ENV[
|
|
45
|
-
ENV[
|
|
45
|
+
unless ENV["DEBUG"]
|
|
46
|
+
ENV["DEBUG"] = "true" if cli_opts[:debug]
|
|
46
47
|
end
|
|
47
48
|
super(cli_opts, args)
|
|
48
49
|
load_api_provider_extensions!
|
|
49
50
|
run_callbacks_for(:after_config)
|
|
50
|
-
run_callbacks_for("after_config_#{Bogo::Utility.snake(self.class.name.split(
|
|
51
|
+
run_callbacks_for("after_config_#{Bogo::Utility.snake(self.class.name.split("::").last)}")
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
# @return [Smash]
|
|
@@ -65,7 +66,7 @@ module Sfn
|
|
|
65
66
|
def load_api_provider_extensions!
|
|
66
67
|
if config.get(:credentials, :provider)
|
|
67
68
|
base_ext = Bogo::Utility.camel(config.get(:credentials, :provider)).to_sym
|
|
68
|
-
targ_ext = self.class.name.split(
|
|
69
|
+
targ_ext = self.class.name.split("::").last
|
|
69
70
|
if ApiProvider.constants.include?(base_ext)
|
|
70
71
|
base_module = ApiProvider.const_get(base_ext)
|
|
71
72
|
ui.debug "Loading core provider extensions via `#{base_module}`"
|
|
@@ -87,26 +88,26 @@ module Sfn
|
|
|
87
88
|
# @return [Slop]
|
|
88
89
|
def discover_config(opts)
|
|
89
90
|
cwd = Dir.pwd.split(File::SEPARATOR)
|
|
90
|
-
detected_path =
|
|
91
|
+
detected_path = ""
|
|
91
92
|
until cwd.empty? || File.exists?(detected_path.to_s)
|
|
92
93
|
detected_path = Dir.glob(
|
|
93
|
-
(cwd + ["#{CONFIG_BASE_NAME}{#{VALID_CONFIG_EXTENSIONS.join(
|
|
94
|
+
(cwd + ["#{CONFIG_BASE_NAME}{#{VALID_CONFIG_EXTENSIONS.join(",")}}"]).join(
|
|
94
95
|
File::SEPARATOR
|
|
95
96
|
)
|
|
96
97
|
).first
|
|
97
98
|
cwd.pop
|
|
98
99
|
end
|
|
99
100
|
if opts.respond_to?(:fetch_option)
|
|
100
|
-
opts.fetch_option(
|
|
101
|
+
opts.fetch_option("config").value = detected_path if detected_path
|
|
101
102
|
else
|
|
102
|
-
opts[
|
|
103
|
+
opts["config"] = detected_path if detected_path
|
|
103
104
|
end
|
|
104
105
|
opts
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
# @return [Class] attempt to return customized configuration class
|
|
108
109
|
def config_class
|
|
109
|
-
klass_name = self.class.name.split(
|
|
110
|
+
klass_name = self.class.name.split("::").last
|
|
110
111
|
if Sfn::Config.const_defined?(klass_name)
|
|
111
112
|
Sfn::Config.const_get(klass_name)
|
|
112
113
|
else
|
data/lib/sfn/command/conf.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
2
|
|
|
3
3
|
module Sfn
|
|
4
4
|
class Command
|
|
@@ -12,42 +12,42 @@ module Sfn
|
|
|
12
12
|
Config::Conf.attributes.sort_by(&:first).each do |k, val|
|
|
13
13
|
if config.has_key?(k)
|
|
14
14
|
ui.print " #{ui.color(k, :bold, :green)}: "
|
|
15
|
-
format_value(config[k],
|
|
15
|
+
format_value(config[k], " ")
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
if config[:generate]
|
|
19
19
|
ui.puts
|
|
20
|
-
ui.info
|
|
20
|
+
ui.info "Generating .sfn configuration file.."
|
|
21
21
|
generate_config!
|
|
22
|
-
ui.info "Generation of .sfn configuration file #{ui.color(
|
|
22
|
+
ui.info "Generation of .sfn configuration file #{ui.color("complete!", :green, :bold)}"
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def generate_config!
|
|
27
|
-
if File.exists?(
|
|
28
|
-
ui.warn
|
|
29
|
-
ui.confirm
|
|
27
|
+
if File.exists?(".sfn")
|
|
28
|
+
ui.warn "Existing .sfn configuration file detected!"
|
|
29
|
+
ui.confirm "Overwrite current .sfn configuration file?"
|
|
30
30
|
end
|
|
31
|
-
run_action
|
|
32
|
-
File.open(
|
|
31
|
+
run_action "Writing .sfn file" do
|
|
32
|
+
File.open(".sfn", "w") do |file|
|
|
33
33
|
file.write SFN_CONFIG_CONTENTS
|
|
34
34
|
end
|
|
35
35
|
nil
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def format_value(value, indent =
|
|
39
|
+
def format_value(value, indent = "")
|
|
40
40
|
if value.is_a?(Hash)
|
|
41
41
|
ui.puts
|
|
42
42
|
value.sort_by(&:first).each do |k, v|
|
|
43
43
|
ui.print "#{indent} #{ui.color(k, :bold)}: "
|
|
44
|
-
format_value(v, indent +
|
|
44
|
+
format_value(v, indent + " ")
|
|
45
45
|
end
|
|
46
46
|
elsif value.is_a?(Array)
|
|
47
47
|
ui.puts
|
|
48
48
|
value.map(&:to_s).sort.each do |v|
|
|
49
49
|
ui.print "#{indent} "
|
|
50
|
-
format_value(v, indent +
|
|
50
|
+
format_value(v, indent + " ")
|
|
51
51
|
end
|
|
52
52
|
else
|
|
53
53
|
ui.puts value.to_s
|
data/lib/sfn/command/create.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "sparkle_formation"
|
|
2
|
+
require "sfn"
|
|
3
3
|
|
|
4
4
|
module Sfn
|
|
5
5
|
class Command
|
|
@@ -24,12 +24,12 @@ module Sfn
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
unless config[:print_only]
|
|
27
|
-
ui.info "#{ui.color(
|
|
27
|
+
ui.info "#{ui.color("SparkleFormation:", :bold)} #{ui.color("create", :green)}"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
stack_info = "#{ui.color(
|
|
30
|
+
stack_info = "#{ui.color("Name:", :bold)} #{name}"
|
|
31
31
|
if config[:path]
|
|
32
|
-
stack_info << " #{ui.color(
|
|
32
|
+
stack_info << " #{ui.color("Path:", :bold)} #{config[:file]}"
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
if config[:print_only]
|
|
@@ -66,14 +66,14 @@ module Sfn
|
|
|
66
66
|
stack = provider.stack(name)
|
|
67
67
|
|
|
68
68
|
if stack.reload.state == :create_complete
|
|
69
|
-
ui.info "Stack create complete: #{ui.color(
|
|
69
|
+
ui.info "Stack create complete: #{ui.color("SUCCESS", :green)}"
|
|
70
70
|
namespace.const_get(:Describe).new({:outputs => true}, [name]).execute!
|
|
71
71
|
else
|
|
72
|
-
ui.fatal "Create of new stack #{ui.color(name, :bold)}: #{ui.color(
|
|
73
|
-
raise
|
|
72
|
+
ui.fatal "Create of new stack #{ui.color(name, :bold)}: #{ui.color("FAILED", :red, :bold)}"
|
|
73
|
+
raise "Stack did not reach a successful completion state."
|
|
74
74
|
end
|
|
75
75
|
else
|
|
76
|
-
ui.warn
|
|
76
|
+
ui.warn "Stack state polling has been disabled."
|
|
77
77
|
ui.info "Stack creation initialized for #{ui.color(name, :green)}"
|
|
78
78
|
end
|
|
79
79
|
end
|
data/lib/sfn/command/describe.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
2
|
|
|
3
3
|
module Sfn
|
|
4
4
|
class Command
|
|
@@ -75,11 +75,11 @@ module Sfn
|
|
|
75
75
|
unless stack.outputs.nil? || stack.outputs.empty?
|
|
76
76
|
stack.outputs.each do |output|
|
|
77
77
|
key, value = output.key, output.value
|
|
78
|
-
key = snake(key).to_s.split(
|
|
79
|
-
ui.info [
|
|
78
|
+
key = snake(key).to_s.split("_").map(&:capitalize).join(" ")
|
|
79
|
+
ui.info [" ", ui.color("#{key}:", :bold), value].join(" ")
|
|
80
80
|
end
|
|
81
81
|
else
|
|
82
|
-
ui.info " #{ui.color(
|
|
82
|
+
ui.info " #{ui.color("No outputs found")}"
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -90,10 +90,10 @@ module Sfn
|
|
|
90
90
|
ui.info "Tags for stack: #{ui.color(stack.name, :bold)}"
|
|
91
91
|
if stack.tags && !stack.tags.empty?
|
|
92
92
|
stack.tags.each do |key, value|
|
|
93
|
-
ui.info [
|
|
93
|
+
ui.info [" ", ui.color("#{key}:", :bold), value].join(" ")
|
|
94
94
|
end
|
|
95
95
|
else
|
|
96
|
-
ui.info " #{ui.color(
|
|
96
|
+
ui.info " #{ui.color("No tags found")}"
|
|
97
97
|
end
|
|
98
98
|
end
|
|
99
99
|
|
data/lib/sfn/command/destroy.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "sfn"
|
|
2
2
|
|
|
3
3
|
module Sfn
|
|
4
4
|
class Command
|
|
@@ -9,7 +9,7 @@ module Sfn
|
|
|
9
9
|
def execute!
|
|
10
10
|
name_required!
|
|
11
11
|
stacks = name_args.sort
|
|
12
|
-
plural =
|
|
12
|
+
plural = "s" if stacks.size > 1
|
|
13
13
|
globs = stacks.find_all do |s|
|
|
14
14
|
s !~ /^[a-zA-Z0-9-]+$/
|
|
15
15
|
end
|
|
@@ -23,7 +23,7 @@ module Sfn
|
|
|
23
23
|
stacks -= globs
|
|
24
24
|
stacks.sort!
|
|
25
25
|
end
|
|
26
|
-
ui.warn "Destroying Stack#{plural}: #{ui.color(stacks.join(
|
|
26
|
+
ui.warn "Destroying Stack#{plural}: #{ui.color(stacks.join(", "), :bold)}"
|
|
27
27
|
ui.confirm "Destroy listed stack#{plural}?"
|
|
28
28
|
stacks.each do |stack_name|
|
|
29
29
|
stack = provider.connection.stacks.get(stack_name)
|
|
@@ -56,7 +56,7 @@ module Sfn
|
|
|
56
56
|
ui.error "Stack polling is not available when multiple stack deletion is requested!"
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
|
-
ui.info " -> Destroyed SparkleFormation#{plural}: #{ui.color(stacks.join(
|
|
59
|
+
ui.info " -> Destroyed SparkleFormation#{plural}: #{ui.color(stacks.join(", "), :bold, :red)}"
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# Cleanup persisted templates if nested stack resources are included
|
|
@@ -64,12 +64,12 @@ module Sfn
|
|
|
64
64
|
stack.nested_stacks.each do |n_stack|
|
|
65
65
|
nested_stack_cleanup!(n_stack)
|
|
66
66
|
end
|
|
67
|
-
nest_stacks = stack.template.fetch(
|
|
68
|
-
provider.connection.data[:stack_types].include?(resource[
|
|
67
|
+
nest_stacks = stack.template.fetch("Resources", {}).values.find_all do |resource|
|
|
68
|
+
provider.connection.data[:stack_types].include?(resource["Type"])
|
|
69
69
|
end.each do |resource|
|
|
70
|
-
url = resource[
|
|
70
|
+
url = resource["Properties"]["TemplateURL"]
|
|
71
71
|
if url && url.is_a?(String)
|
|
72
|
-
_, bucket_name, path = URI.parse(url).path.split(
|
|
72
|
+
_, bucket_name, path = URI.parse(url).path.split("/", 3)
|
|
73
73
|
bucket = provider.connection.api_for(:storage).buckets.get(bucket_name)
|
|
74
74
|
if bucket
|
|
75
75
|
file = bucket.files.get(path)
|
data/lib/sfn/command/diff.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "sparkle_formation"
|
|
2
|
+
require "sfn"
|
|
3
|
+
require "hashdiff"
|
|
4
4
|
|
|
5
5
|
module Sfn
|
|
6
6
|
class Command
|
|
@@ -26,7 +26,7 @@ module Sfn
|
|
|
26
26
|
file = load_template_file
|
|
27
27
|
file = parameter_scrub!(file.dump)
|
|
28
28
|
|
|
29
|
-
ui.info "#{ui.color(
|
|
29
|
+
ui.info "#{ui.color("SparkleFormation:", :bold)} #{ui.color("diff", :blue)} - #{name}"
|
|
30
30
|
ui.puts
|
|
31
31
|
|
|
32
32
|
diff_stack(stack, MultiJson.load(MultiJson.dump(file)).to_smash)
|
|
@@ -40,8 +40,8 @@ module Sfn
|
|
|
40
40
|
def diff_stack(stack, file, parent_names = [])
|
|
41
41
|
stack_template = stack.template
|
|
42
42
|
nested_stacks = Hash[
|
|
43
|
-
file.fetch(
|
|
44
|
-
value.fetch(
|
|
43
|
+
file.fetch("Resources", file.fetch("resources", {})).find_all do |name, value|
|
|
44
|
+
value.fetch("Properties", {})["Stack"]
|
|
45
45
|
end
|
|
46
46
|
]
|
|
47
47
|
nested_stacks.each do |name, value|
|
|
@@ -49,58 +49,58 @@ module Sfn
|
|
|
49
49
|
ns.data[:logical_id] == name
|
|
50
50
|
end
|
|
51
51
|
if n_stack
|
|
52
|
-
diff_stack(n_stack, value[
|
|
52
|
+
diff_stack(n_stack, value["Properties"]["Stack"], [*parent_names, stack.data.fetch(:logical_id, stack.name)].compact)
|
|
53
53
|
end
|
|
54
|
-
file[
|
|
54
|
+
file["Resources"][name]["Properties"].delete("Stack")
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
ui.info "#{ui.color(
|
|
57
|
+
ui.info "#{ui.color("Stack diff:", :bold)} #{ui.color((parent_names + [stack.data.fetch(:logical_id, stack.name)]).compact.join(" > "), :blue)}"
|
|
58
58
|
|
|
59
59
|
stack_diff = HashDiff.diff(stack.template, file)
|
|
60
60
|
|
|
61
61
|
if config[:raw_diff]
|
|
62
62
|
ui.info "Dumping raw template diff:"
|
|
63
|
-
require
|
|
63
|
+
require "pp"
|
|
64
64
|
pp stack_diff
|
|
65
65
|
else
|
|
66
66
|
added_resources = stack_diff.find_all do |item|
|
|
67
|
-
item.first ==
|
|
67
|
+
item.first == "+" && item[1].match(/Resources\.[^.]+$/)
|
|
68
68
|
end
|
|
69
69
|
removed_resources = stack_diff.find_all do |item|
|
|
70
|
-
item.first ==
|
|
70
|
+
item.first == "-" && item[1].match(/Resources\.[^.]+$/)
|
|
71
71
|
end
|
|
72
72
|
modified_resources = stack_diff.find_all do |item|
|
|
73
|
-
item[1].start_with?(
|
|
74
|
-
!item[1].end_with?(
|
|
75
|
-
!item[1].include?(
|
|
73
|
+
item[1].start_with?("Resources.") &&
|
|
74
|
+
!item[1].end_with?("TemplateURL") &&
|
|
75
|
+
!item[1].include?("Properties.Parameters")
|
|
76
76
|
end - added_resources - removed_resources
|
|
77
77
|
|
|
78
78
|
if added_resources.empty? && removed_resources.empty? && modified_resources.empty?
|
|
79
|
-
ui.info
|
|
79
|
+
ui.info "No changes detected"
|
|
80
80
|
ui.puts
|
|
81
81
|
else
|
|
82
82
|
unless added_resources.empty?
|
|
83
|
-
ui.info ui.color(
|
|
83
|
+
ui.info ui.color("Added Resources:", :green, :bold)
|
|
84
84
|
added_resources.each do |item|
|
|
85
|
-
ui.print ui.color(" -> #{item[1].split(
|
|
86
|
-
ui.puts " [#{item[2][
|
|
85
|
+
ui.print ui.color(" -> #{item[1].split(".").last}", :green)
|
|
86
|
+
ui.puts " [#{item[2]["Type"]}]"
|
|
87
87
|
end
|
|
88
88
|
ui.puts
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
unless modified_resources.empty?
|
|
92
|
-
ui.info ui.color(
|
|
92
|
+
ui.info ui.color("Modified Resources:", :yellow, :bold)
|
|
93
93
|
m_resources = Hash.new.tap do |hash|
|
|
94
94
|
modified_resources.each do |item|
|
|
95
|
-
_, key, path = item[1].split(
|
|
95
|
+
_, key, path = item[1].split(".", 3)
|
|
96
96
|
hash[key] ||= {}
|
|
97
|
-
prefix, a_key = path.split(
|
|
97
|
+
prefix, a_key = path.split(".", 2)
|
|
98
98
|
hash[key][prefix] ||= []
|
|
99
99
|
matched = hash[key][prefix].detect do |i|
|
|
100
100
|
i[:path] == a_key
|
|
101
101
|
end
|
|
102
102
|
if matched
|
|
103
|
-
if item.first ==
|
|
103
|
+
if item.first == "-"
|
|
104
104
|
matched[:original] = item[2]
|
|
105
105
|
else
|
|
106
106
|
matched[:new] = item[2]
|
|
@@ -109,10 +109,10 @@ module Sfn
|
|
|
109
109
|
hash[key][prefix] << Hash.new.tap do |info|
|
|
110
110
|
info[:path] = a_key
|
|
111
111
|
case item.first
|
|
112
|
-
when
|
|
112
|
+
when "~"
|
|
113
113
|
info[:original] = item[2]
|
|
114
114
|
info[:new] = item[3]
|
|
115
|
-
when
|
|
115
|
+
when "+"
|
|
116
116
|
info[:new] = item[2]
|
|
117
117
|
else
|
|
118
118
|
info[:original] = item[2]
|
|
@@ -121,12 +121,12 @@ module Sfn
|
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
end.to_smash(:sorted).each do |key, value|
|
|
124
|
-
ui.puts ui.color(" - #{key}", :yellow) + " [#{stack.template[
|
|
124
|
+
ui.puts ui.color(" - #{key}", :yellow) + " [#{stack.template["Resources"][key]["Type"]}]"
|
|
125
125
|
value.each do |prefix, items|
|
|
126
126
|
ui.puts ui.color(" #{prefix}:", :bold)
|
|
127
127
|
items.each do |item|
|
|
128
|
-
original = item[:original].nil? ? ui.color(
|
|
129
|
-
new_val = item[:new].nil? ? ui.color(
|
|
128
|
+
original = item[:original].nil? ? ui.color("(none)", :yellow) : ui.color(item[:original].inspect, :red)
|
|
129
|
+
new_val = item[:new].nil? ? ui.color("(deleted)", :red) : ui.color(item[:new].inspect, :green)
|
|
130
130
|
ui.puts " #{item[:path]}: #{original} -> #{new_val}"
|
|
131
131
|
end
|
|
132
132
|
end
|
|
@@ -135,10 +135,10 @@ module Sfn
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
unless removed_resources.empty?
|
|
138
|
-
ui.info ui.color(
|
|
138
|
+
ui.info ui.color("Removed Resources:", :red, :bold)
|
|
139
139
|
removed_resources.each do |item|
|
|
140
|
-
ui.print ui.color(" <- #{item[1].split(
|
|
141
|
-
ui.puts " [#{item[2][
|
|
140
|
+
ui.print ui.color(" <- #{item[1].split(".").last}", :red)
|
|
141
|
+
ui.puts " [#{item[2]["Type"]}]"
|
|
142
142
|
end
|
|
143
143
|
ui.puts
|
|
144
144
|
end
|