sfn 3.0.28 → 3.0.30

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.
Files changed (77) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +5 -0
  3. data/docs/callbacks.md +1 -0
  4. data/lib/chef/knife/knife_plugin_seed.rb +11 -17
  5. data/lib/sfn.rb +0 -2
  6. data/lib/sfn/api_provider.rb +0 -2
  7. data/lib/sfn/api_provider/google.rb +6 -9
  8. data/lib/sfn/api_provider/terraform.rb +4 -6
  9. data/lib/sfn/cache.rb +36 -39
  10. data/lib/sfn/callback.rb +0 -2
  11. data/lib/sfn/callback/aws_assume_role.rb +7 -8
  12. data/lib/sfn/callback/aws_mfa.rb +7 -8
  13. data/lib/sfn/callback/stack_policy.rb +15 -17
  14. data/lib/sfn/command.rb +9 -11
  15. data/lib/sfn/command/conf.rb +7 -10
  16. data/lib/sfn/command/create.rb +8 -12
  17. data/lib/sfn/command/describe.rb +6 -8
  18. data/lib/sfn/command/destroy.rb +8 -10
  19. data/lib/sfn/command/diff.rb +18 -25
  20. data/lib/sfn/command/events.rb +15 -16
  21. data/lib/sfn/command/export.rb +13 -17
  22. data/lib/sfn/command/graph.rb +11 -13
  23. data/lib/sfn/command/graph/aws.rb +27 -29
  24. data/lib/sfn/command/graph/terraform.rb +22 -23
  25. data/lib/sfn/command/import.rb +13 -16
  26. data/lib/sfn/command/init.rb +5 -7
  27. data/lib/sfn/command/inspect.rb +26 -29
  28. data/lib/sfn/command/lint.rb +10 -12
  29. data/lib/sfn/command/list.rb +5 -8
  30. data/lib/sfn/command/print.rb +3 -5
  31. data/lib/sfn/command/promote.rb +0 -2
  32. data/lib/sfn/command/update.rb +42 -46
  33. data/lib/sfn/command/validate.rb +4 -6
  34. data/lib/sfn/command_module/base.rb +17 -25
  35. data/lib/sfn/command_module/callbacks.rb +12 -8
  36. data/lib/sfn/command_module/stack.rb +39 -43
  37. data/lib/sfn/command_module/template.rb +89 -90
  38. data/lib/sfn/config.rb +30 -31
  39. data/lib/sfn/config/conf.rb +1 -3
  40. data/lib/sfn/config/create.rb +5 -7
  41. data/lib/sfn/config/describe.rb +3 -5
  42. data/lib/sfn/config/diff.rb +1 -1
  43. data/lib/sfn/config/events.rb +6 -8
  44. data/lib/sfn/config/export.rb +4 -7
  45. data/lib/sfn/config/graph.rb +4 -6
  46. data/lib/sfn/config/import.rb +3 -5
  47. data/lib/sfn/config/init.rb +0 -1
  48. data/lib/sfn/config/inspect.rb +7 -9
  49. data/lib/sfn/config/lint.rb +4 -4
  50. data/lib/sfn/config/list.rb +3 -5
  51. data/lib/sfn/config/print.rb +3 -5
  52. data/lib/sfn/config/promote.rb +3 -5
  53. data/lib/sfn/config/update.rb +10 -12
  54. data/lib/sfn/config/validate.rb +18 -20
  55. data/lib/sfn/lint.rb +0 -2
  56. data/lib/sfn/lint/definition.rb +3 -5
  57. data/lib/sfn/lint/rule.rb +7 -8
  58. data/lib/sfn/lint/rule_set.rb +11 -20
  59. data/lib/sfn/monkey_patch/stack.rb +32 -34
  60. data/lib/sfn/monkey_patch/stack/azure.rb +0 -1
  61. data/lib/sfn/monkey_patch/stack/google.rb +15 -16
  62. data/lib/sfn/planner.rb +1 -3
  63. data/lib/sfn/planner/aws.rb +82 -89
  64. data/lib/sfn/provider.rb +21 -23
  65. data/lib/sfn/utils.rb +0 -2
  66. data/lib/sfn/utils/debug.rb +1 -2
  67. data/lib/sfn/utils/json.rb +3 -2
  68. data/lib/sfn/utils/object_storage.rb +1 -2
  69. data/lib/sfn/utils/output.rb +8 -9
  70. data/lib/sfn/utils/path_selector.rb +9 -10
  71. data/lib/sfn/utils/ssher.rb +2 -3
  72. data/lib/sfn/utils/stack_exporter.rb +20 -21
  73. data/lib/sfn/utils/stack_parameter_scrubber.rb +6 -7
  74. data/lib/sfn/utils/stack_parameter_validator.rb +14 -16
  75. data/lib/sfn/version.rb +1 -1
  76. data/sfn.gemspec +1 -1
  77. metadata +8 -8
@@ -3,7 +3,6 @@ require 'sfn'
3
3
  module Sfn
4
4
  # Interface for injecting custom functionality
5
5
  class Callback
6
-
7
6
  autoload :AwsAssumeRole, 'sfn/callback/aws_assume_role'
8
7
  autoload :AwsMfa, 'sfn/callback/aws_mfa'
9
8
  autoload :StackPolicy, 'sfn/callback/stack_policy'
@@ -49,6 +48,5 @@ module Sfn
49
48
  raise
50
49
  end
51
50
  end
52
-
53
51
  end
54
52
  end
@@ -10,7 +10,7 @@ module Sfn
10
10
  :aws_sts_token,
11
11
  :aws_sts_access_key_id,
12
12
  :aws_sts_secret_access_key,
13
- :aws_sts_token_expires
13
+ :aws_sts_token_expires,
14
14
  ]
15
15
 
16
16
  # Prevent callback output to user
@@ -21,7 +21,7 @@ module Sfn
21
21
  # Inject STS related configuration into
22
22
  # API provider credentials
23
23
  def after_config(*_)
24
- if(enabled? && config.fetch(:credentials, :aws_sts_role_arn))
24
+ if enabled? && config.fetch(:credentials, :aws_sts_role_arn)
25
25
  load_stored_session
26
26
  end
27
27
  end
@@ -29,8 +29,8 @@ module Sfn
29
29
  # Store session token if available for
30
30
  # later use
31
31
  def after(*_)
32
- if(enabled?)
33
- if(api.connection.aws_sts_role_arn && api.connection.aws_sts_token)
32
+ if enabled?
33
+ if api.connection.aws_sts_role_arn && api.connection.aws_sts_token
34
34
  path = config.fetch(:aws_assume_role, :cache_file, '.sfn-aws')
35
35
  FileUtils.touch(path)
36
36
  File.chmod(0600, path)
@@ -55,12 +55,12 @@ module Sfn
55
55
  # @return [TrueClass, FalseClass]
56
56
  def load_stored_session
57
57
  path = config.fetch(:aws_assume_role, :cache_file, '.sfn-aws')
58
- if(File.exists?(path))
58
+ if File.exists?(path)
59
59
  values = load_stored_values(path)
60
60
  STS_STORE_ITEMS.each do |key|
61
61
  api.connection.data[key] = values[key]
62
62
  end
63
- if(values[:aws_sts_token_expires])
63
+ if values[:aws_sts_token_expires]
64
64
  begin
65
65
  api.connection.data[:aws_sts_token_expires] = Time.parse(values[:aws_sts_token_expires])
66
66
  rescue
@@ -78,7 +78,7 @@ module Sfn
78
78
  # @return [Hash]
79
79
  def load_stored_values(path)
80
80
  begin
81
- if(File.exists?(path))
81
+ if File.exists?(path)
82
82
  MultiJson.load(File.read(path)).to_smash
83
83
  else
84
84
  Smash.new
@@ -87,7 +87,6 @@ module Sfn
87
87
  Smash.new
88
88
  end
89
89
  end
90
-
91
90
  end
92
91
  end
93
92
  end
@@ -10,7 +10,7 @@ module Sfn
10
10
  :aws_sts_session_token,
11
11
  :aws_sts_session_access_key_id,
12
12
  :aws_sts_session_secret_access_key,
13
- :aws_sts_session_token_expires
13
+ :aws_sts_session_token_expires,
14
14
  ]
15
15
 
16
16
  # Prevent callback output to user
@@ -21,7 +21,7 @@ module Sfn
21
21
  # Inject MFA related configuration into
22
22
  # API provider credentials
23
23
  def after_config(*_)
24
- if(enabled?)
24
+ if enabled?
25
25
  load_stored_session
26
26
  api.connection.aws_sts_session_token_code = method(:prompt_for_code)
27
27
  end
@@ -30,8 +30,8 @@ module Sfn
30
30
  # Store session token if available for
31
31
  # later use
32
32
  def after(*_)
33
- if(enabled?)
34
- if(api.connection.aws_sts_session_token)
33
+ if enabled?
34
+ if api.connection.aws_sts_session_token
35
35
  path = config.fetch(:aws_mfa, :cache_file, '.sfn-aws')
36
36
  FileUtils.touch(path)
37
37
  File.chmod(0600, path)
@@ -56,12 +56,12 @@ module Sfn
56
56
  # @return [TrueClass, FalseClass]
57
57
  def load_stored_session
58
58
  path = config.fetch(:aws_mfa, :cache_file, '.sfn-aws')
59
- if(File.exists?(path))
59
+ if File.exists?(path)
60
60
  values = load_stored_values(path)
61
61
  SESSION_STORE_ITEMS.each do |key|
62
62
  api.connection.data[key] = values[key]
63
63
  end
64
- if(values[:aws_sts_session_token_expires])
64
+ if values[:aws_sts_session_token_expires]
65
65
  begin
66
66
  api.connection.data[:aws_sts_session_token_expires] = Time.parse(values[:aws_sts_session_token_expires])
67
67
  rescue
@@ -79,7 +79,7 @@ module Sfn
79
79
  # @return [Hash]
80
80
  def load_stored_values(path)
81
81
  begin
82
- if(File.exists?(path))
82
+ if File.exists?(path)
83
83
  MultiJson.load(File.read(path)).to_smash
84
84
  else
85
85
  Smash.new
@@ -96,7 +96,6 @@ module Sfn
96
96
  result = ui.ask 'AWS MFA code', :valid => /^\d{6}$/
97
97
  result.strip
98
98
  end
99
-
100
99
  end
101
100
  end
102
101
  end
@@ -7,11 +7,11 @@ module Sfn
7
7
  # Policy to apply prior to stack deletion
8
8
  DEFENSELESS_POLICY = {
9
9
  'Statement' => [{
10
- 'Effect' => 'Allow',
11
- 'Action' => 'Update:*',
12
- 'Resource' => '*',
13
- 'Principal' => '*'
14
- }]
10
+ 'Effect' => 'Allow',
11
+ 'Action' => 'Update:*',
12
+ 'Resource' => '*',
13
+ 'Principal' => '*',
14
+ }],
15
15
  }
16
16
 
17
17
  # @return [Smash] cached policies
@@ -38,6 +38,7 @@ module Sfn
38
38
  end
39
39
  ui.info 'Stack policy documents successfully submitted!'
40
40
  end
41
+
41
42
  alias_method :after_create, :submit_policy
42
43
  alias_method :after_update, :submit_policy
43
44
 
@@ -45,7 +46,7 @@ module Sfn
45
46
  #
46
47
  # @param args [Hash]
47
48
  def before_update(args)
48
- if(config.get(:stack_policy, :update).to_s == 'defenseless')
49
+ if config.get(:stack_policy, :update).to_s == 'defenseless'
49
50
  ui.warn 'Disabling all stack policies for update.'
50
51
  stack = args[:api_stack]
51
52
  ([stack] + stack.nested_stacks).compact.each do |p_stack|
@@ -62,10 +63,9 @@ module Sfn
62
63
  #
63
64
  # @param info [Hash]
64
65
  def template(info)
65
- if(info[:sparkle_stack])
66
+ if info[:sparkle_stack]
66
67
  @policies.set(info.fetch(:stack_name, 'unknown'),
67
- info[:sparkle_stack].generate_policy
68
- )
68
+ info[:sparkle_stack].generate_policy)
69
69
  end
70
70
  end
71
71
 
@@ -76,15 +76,14 @@ module Sfn
76
76
  def save_stack_policy(p_stack)
77
77
  valid_logical_ids = p_stack.resources.reload.all.map(&:logical_id)
78
78
  stack_policy = @policies.fetch(p_stack.id,
79
- @policies.fetch(p_stack.data[:logical_id]),
80
- @policies[p_stack.name]
81
- ).to_smash
82
- if(stack_policy)
79
+ @policies.fetch(p_stack.data[:logical_id]),
80
+ @policies[p_stack.name]).to_smash
81
+ if stack_policy
83
82
  stack_policy[:Statement].delete_if do |policy_item|
84
83
  policy_match = policy_item[:Resource].to_s.match(
85
84
  %r{LogicalResourceId/(?<logical_id>.+)$}
86
85
  )
87
- if(policy_match)
86
+ if policy_match
88
87
  !valid_logical_ids.include?(policy_match["logical_id"])
89
88
  end
90
89
  end
@@ -95,11 +94,10 @@ module Sfn
95
94
  :form => Smash.new(
96
95
  'Action' => 'SetStackPolicy',
97
96
  'StackName' => p_stack.id,
98
- 'StackPolicyBody' => MultiJson.dump(stack_policy)
99
- )
97
+ 'StackPolicyBody' => MultiJson.dump(stack_policy),
98
+ ),
100
99
  )
101
100
  end
102
-
103
101
  end
104
102
  end
105
103
  end
@@ -3,7 +3,6 @@ require 'bogo-cli'
3
3
 
4
4
  module Sfn
5
5
  class Command < Bogo::Cli::Command
6
-
7
6
  include CommandModule::Callbacks
8
7
 
9
8
  autoload :Conf, 'sfn/command/conf'
@@ -34,15 +33,15 @@ module Sfn
34
33
  '.json',
35
34
  '.yaml',
36
35
  '.yml',
37
- '.xml'
36
+ '.xml',
38
37
  ]
39
38
 
40
39
  # Override to provide config file searching
41
40
  def initialize(cli_opts, args)
42
- unless(cli_opts['config'])
41
+ unless cli_opts['config']
43
42
  discover_config(cli_opts)
44
43
  end
45
- unless(ENV['DEBUG'])
44
+ unless ENV['DEBUG']
46
45
  ENV['DEBUG'] = 'true' if cli_opts[:debug]
47
46
  end
48
47
  super(cli_opts, args)
@@ -64,14 +63,14 @@ module Sfn
64
63
  #
65
64
  # @return [TrueClass, FalseClass]
66
65
  def load_api_provider_extensions!
67
- if(config.get(:credentials, :provider))
66
+ if config.get(:credentials, :provider)
68
67
  base_ext = Bogo::Utility.camel(config.get(:credentials, :provider)).to_sym
69
68
  targ_ext = self.class.name.split('::').last
70
- if(ApiProvider.constants.include?(base_ext))
69
+ if ApiProvider.constants.include?(base_ext)
71
70
  base_module = ApiProvider.const_get(base_ext)
72
71
  ui.debug "Loading core provider extensions via `#{base_module}`"
73
72
  extend base_module
74
- if(base_module.constants.include?(targ_ext))
73
+ if base_module.constants.include?(targ_ext)
75
74
  targ_module = base_module.const_get(targ_ext)
76
75
  ui.debug "Loading targeted provider extensions via `#{targ_module}`"
77
76
  extend targ_module
@@ -89,7 +88,7 @@ module Sfn
89
88
  def discover_config(opts)
90
89
  cwd = Dir.pwd.split(File::SEPARATOR)
91
90
  detected_path = ''
92
- until(cwd.empty? || File.exists?(detected_path.to_s))
91
+ until cwd.empty? || File.exists?(detected_path.to_s)
93
92
  detected_path = Dir.glob(
94
93
  (cwd + ["#{CONFIG_BASE_NAME}{#{VALID_CONFIG_EXTENSIONS.join(',')}}"]).join(
95
94
  File::SEPARATOR
@@ -97,7 +96,7 @@ module Sfn
97
96
  ).first
98
97
  cwd.pop
99
98
  end
100
- if(opts.respond_to?(:fetch_option))
99
+ if opts.respond_to?(:fetch_option)
101
100
  opts.fetch_option('config').value = detected_path if detected_path
102
101
  else
103
102
  opts['config'] = detected_path if detected_path
@@ -108,12 +107,11 @@ module Sfn
108
107
  # @return [Class] attempt to return customized configuration class
109
108
  def config_class
110
109
  klass_name = self.class.name.split('::').last
111
- if(Sfn::Config.const_defined?(klass_name))
110
+ if Sfn::Config.const_defined?(klass_name)
112
111
  Sfn::Config.const_get(klass_name)
113
112
  else
114
113
  super
115
114
  end
116
115
  end
117
-
118
116
  end
119
117
  end
@@ -4,19 +4,18 @@ module Sfn
4
4
  class Command
5
5
  # Config command
6
6
  class Conf < Command
7
-
8
7
  include Sfn::CommandModule::Base
9
8
 
10
9
  # Run the list command
11
10
  def execute!
12
11
  ui.info ui.color("Current configuration state:")
13
12
  Config::Conf.attributes.sort_by(&:first).each do |k, val|
14
- if(config.has_key?(k))
13
+ if config.has_key?(k)
15
14
  ui.print " #{ui.color(k, :bold, :green)}: "
16
15
  format_value(config[k], ' ')
17
16
  end
18
17
  end
19
- if(config[:generate])
18
+ if config[:generate]
20
19
  ui.puts
21
20
  ui.info 'Generating .sfn configuration file..'
22
21
  generate_config!
@@ -25,7 +24,7 @@ module Sfn
25
24
  end
26
25
 
27
26
  def generate_config!
28
- if(File.exists?('.sfn'))
27
+ if File.exists?('.sfn')
29
28
  ui.warn 'Existing .sfn configuration file detected!'
30
29
  ui.confirm 'Overwrite current .sfn configuration file?'
31
30
  end
@@ -37,14 +36,14 @@ module Sfn
37
36
  end
38
37
  end
39
38
 
40
- def format_value(value, indent='')
41
- if(value.is_a?(Hash))
39
+ def format_value(value, indent = '')
40
+ if value.is_a?(Hash)
42
41
  ui.puts
43
- value.sort_by(&:first).each do |k,v|
42
+ value.sort_by(&:first).each do |k, v|
44
43
  ui.print "#{indent} #{ui.color(k, :bold)}: "
45
44
  format_value(v, indent + ' ')
46
45
  end
47
- elsif(value.is_a?(Array))
46
+ elsif value.is_a?(Array)
48
47
  ui.puts
49
48
  value.map(&:to_s).sort.each do |v|
50
49
  ui.print "#{indent} "
@@ -134,8 +133,6 @@ Configuration.new do
134
133
  end
135
134
  end
136
135
  EOF
137
-
138
-
139
136
  end
140
137
  end
141
138
  end
@@ -5,7 +5,6 @@ module Sfn
5
5
  class Command
6
6
  # Create command
7
7
  class Create < Command
8
-
9
8
  include Sfn::CommandModule::Base
10
9
  include Sfn::CommandModule::Template
11
10
  include Sfn::CommandModule::Stack
@@ -18,22 +17,22 @@ module Sfn
18
17
  # NOTE: Always disable plans on create
19
18
  config[:plan] = false
20
19
 
21
- if(config[:template])
20
+ if config[:template]
22
21
  file = config[:template]
23
22
  else
24
23
  file = load_template_file
25
24
  end
26
25
 
27
- unless(config[:print_only])
26
+ unless config[:print_only]
28
27
  ui.info "#{ui.color('SparkleFormation:', :bold)} #{ui.color('create', :green)}"
29
28
  end
30
29
 
31
30
  stack_info = "#{ui.color('Name:', :bold)} #{name}"
32
- if(config[:path])
31
+ if config[:path]
33
32
  stack_info << " #{ui.color('Path:', :bold)} #{config[:file]}"
34
33
  end
35
34
 
36
- if(config[:print_only])
35
+ if config[:print_only]
37
36
  ui.puts format_json(parameter_scrub!(template_content(file)))
38
37
  return
39
38
  else
@@ -44,7 +43,7 @@ module Sfn
44
43
  config.fetch(:options, Smash.new).dup.merge(
45
44
  :name => name,
46
45
  :template => template_content(file),
47
- :parameters => Smash.new
46
+ :parameters => Smash.new,
48
47
  )
49
48
  )
50
49
 
@@ -53,7 +52,7 @@ module Sfn
53
52
 
54
53
  stack.parameters = config_root_parameters
55
54
 
56
- if(config[:upload_root_template])
55
+ if config[:upload_root_template]
57
56
  upload_result = store_template(name, file, Smash.new)
58
57
  stack.template_url = upload_result[:url]
59
58
  else
@@ -62,11 +61,11 @@ module Sfn
62
61
 
63
62
  api_action!(:api_stack => stack) do
64
63
  stack.save
65
- if(config[:poll])
64
+ if config[:poll]
66
65
  poll_stack(stack.name)
67
66
  stack = provider.stack(name)
68
67
 
69
- if(stack.reload.state == :create_complete)
68
+ if stack.reload.state == :create_complete
70
69
  ui.info "Stack create complete: #{ui.color('SUCCESS', :green)}"
71
70
  namespace.const_get(:Describe).new({:outputs => true}, [name]).execute!
72
71
  else
@@ -78,10 +77,7 @@ module Sfn
78
77
  ui.info "Stack creation initialized for #{ui.color(name, :green)}"
79
78
  end
80
79
  end
81
-
82
80
  end
83
-
84
81
  end
85
-
86
82
  end
87
83
  end
@@ -4,11 +4,10 @@ module Sfn
4
4
  class Command
5
5
  # Cloudformation describe command
6
6
  class Describe < Command
7
-
8
7
  include Sfn::CommandModule::Base
9
8
 
10
9
  # information available
11
- unless(defined?(AVAILABLE_DISPLAYS))
10
+ unless defined?(AVAILABLE_DISPLAYS)
12
11
  AVAILABLE_DISPLAYS = [:resources, :outputs, :tags]
13
12
  end
14
13
 
@@ -19,12 +18,12 @@ module Sfn
19
18
  root_stack = api_action! do
20
19
  provider.stack(stack_name)
21
20
  end
22
- if(root_stack)
21
+ if root_stack
23
22
  ([root_stack] + root_stack.nested_stacks).compact.each do |stack|
24
23
  ui.info "Stack description of #{ui.color(stack.name, :bold)}:"
25
24
  display = [].tap do |to_display|
26
25
  AVAILABLE_DISPLAYS.each do |display_option|
27
- if(config[display_option])
26
+ if config[display_option]
28
27
  to_display << display_option
29
28
  end
30
29
  end
@@ -54,7 +53,7 @@ module Sfn
54
53
  table(:border => false) do
55
54
  row(:header => true) do
56
55
  allowed_attributes.each do |attr|
57
- column as_title(attr), :width => stack_resources.map{|r| r[attr].to_s.length}.push(as_title(attr).length).max + 2
56
+ column as_title(attr), :width => stack_resources.map { |r| r[attr].to_s.length }.push(as_title(attr).length).max + 2
58
57
  end
59
58
  end
60
59
  stack_resources.each do |resource|
@@ -73,7 +72,7 @@ module Sfn
73
72
  # @param stack [Miasma::Models::Orchestration::Stack]
74
73
  def outputs(stack)
75
74
  ui.info "Outputs for stack: #{ui.color(stack.name, :bold)}"
76
- unless(stack.outputs.nil? || stack.outputs.empty?)
75
+ unless stack.outputs.nil? || stack.outputs.empty?
77
76
  stack.outputs.each do |output|
78
77
  key, value = output.key, output.value
79
78
  key = snake(key).to_s.split('_').map(&:capitalize).join(' ')
@@ -89,7 +88,7 @@ module Sfn
89
88
  # @param stack [Miasma::Models::Orchestration::Stack]
90
89
  def tags(stack)
91
90
  ui.info "Tags for stack: #{ui.color(stack.name, :bold)}"
92
- if(stack.tags && !stack.tags.empty?)
91
+ if stack.tags && !stack.tags.empty?
93
92
  stack.tags.each do |key, value|
94
93
  ui.info [' ', ui.color("#{key}:", :bold), value].join(' ')
95
94
  end
@@ -102,7 +101,6 @@ module Sfn
102
101
  def default_attributes
103
102
  %w(updated logical_id type status status_reason)
104
103
  end
105
-
106
104
  end
107
105
  end
108
106
  end