sfn 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3673b71af607d02ac346e2419e0f3ee10d29e7fb
4
- data.tar.gz: 43fd1549a7deed286de8a4c59b9c1b3148700d3e
3
+ metadata.gz: 1e25f48c5fa80038624e08c5e224e62c740be5a8
4
+ data.tar.gz: 2a983906bcdb4b5e02d81c9762b1a033c3b4410e
5
5
  SHA512:
6
- metadata.gz: 3e47a279dc5d53e18674281d2371f0cd72539dd797d2a0e0276813125acc8118ee5087e96278b2bdf960f42147fda969bf1ab855c4b1b5b5f7532a6d85e62e46
7
- data.tar.gz: 28ab80f21ef727f319a7c5c3ac685ced8f33c7fadcc5e680b444812957734c49cf13e30d27e18d0944004d91d5065e79b71c9135dbb6df58d6f7f075fa8bc090
6
+ metadata.gz: 9b41dd94c68cff3b29a487c4d03e86c36d352b4a3403d99787db582d3eaf54e7b2f93418e54398c4dcf8e3083813830cbc24a1a897c70765cce0869c236a5f76
7
+ data.tar.gz: 230a3ef0bcf85adc1c6fd4bb80617dd2b20ab19de4c6b91b43a319fb2265a3cf9d50b7cee4195b119c787ea68528bd47817c75ccddbfb147670c6dd727974103
data/CHANGELOG.md CHANGED
@@ -1,11 +1,22 @@
1
- ## v0.5.0
2
- * Add support for compile time parameters
3
-
4
- ## v0.4.16
5
- * Fix apply stack on update command when template provides new parameters
6
-
7
- ## v0.4.14
8
- * Add constraint on sparkle_formation library to stay below 1.0 release
1
+ ## v1.0.0
2
+
3
+ _NOTE: This release contains breaking changes! Please review the
4
+ changes in this release and test your configuration and
5
+ templates. Changes that may introduce breakage have been
6
+ labeled below._
7
+
8
+ * [BREAKING] Update sparkle_formation constraint to 1.0 versions
9
+ * [BREAKING] Default to "deep" style nesting (previous default: "shallow")
10
+ * [BREAKING] Template loading via SparklePacks in place of direct file loading
11
+ * Provide support for shallow and deep stack nesting styles
12
+ * Add support for customized callbacks
13
+ * Fix `--print-only` behavior on `update` command, add to `validate` command
14
+ * On validation of template with nesting, validate all nested templates _and_ root template
15
+ * Display details of all nested stacks on `describe` command
16
+ * Disable automatic stack inspection on failed `create` / `update` command
17
+ * Add support for SparklePacks
18
+ * Add support for AWS stack policies via optional callback
19
+ * And lots of internal refactors!
9
20
 
10
21
  ## v0.4.12
11
22
  * Fix transient uninitialized constant error for update command
data/README.md CHANGED
@@ -1,8 +1,14 @@
1
+ ![SparkleFormation CLI](img/sfn.jpg)
2
+
1
3
  # SparkleFormation CLI
2
4
 
3
5
  SparkleFormation command line interface for interacting
4
6
  with orchestration APIs.
5
7
 
8
+ ## Extra Documentation
9
+
10
+ * [User Documentation](https://sparkleformation.github.io/sfn/UserDocs)
11
+
6
12
  ## API Compatibility
7
13
 
8
14
  * AWS
@@ -26,7 +32,7 @@ formats:
26
32
  ```json
27
33
  {
28
34
  "credentials": {
29
- AWS_CREDENTIALS
35
+ MIASMA_CREDENTIALS
30
36
  },
31
37
  "options": {
32
38
  "disable_rollback": true
@@ -34,7 +40,7 @@ formats:
34
40
  }
35
41
  ```
36
42
 
37
- ### YAML
43
+ #### YAML
38
44
 
39
45
  ```yaml
40
46
  ---
@@ -44,12 +50,12 @@ formats:
44
50
  :disable_rollback: true
45
51
  ```
46
52
 
47
- ### XML
53
+ #### XML
48
54
 
49
55
  ```xml
50
56
  <configuration>
51
57
  <credentials>
52
- AWS_CREDENTIALS
58
+ MIASMA_CREDENTIALS
53
59
  </credentials>
54
60
  <options>
55
61
  <disable_rollback>
@@ -59,17 +65,86 @@ formats:
59
65
  </configuration>
60
66
  ```
61
67
 
62
- ### Ruby
68
+ #### Ruby
63
69
 
64
70
  ```ruby
65
71
  Configuration.new do
66
72
  credentials do
67
- AWS_CREDENTIALS
73
+ MIASMA_CREDENTIALS
68
74
  end
69
- options.disable_rollback true
75
+ options.on_failure 'nothing'
70
76
  end
71
77
  ```
72
78
 
79
+ ### Configuration Options
80
+
81
+ * `processing` - Enable SparkleFormation processing
82
+ * Valid: Boolean
83
+ * Default: `true`
84
+
85
+ * `apply_nesting` - Style of nested stack processing
86
+ * Valid: `"shallow"`, `"deep"`
87
+ * Default: `"deep"`
88
+
89
+ * `options` - API options for target orchestration API (see miasma)
90
+ * Valid: `Hash`
91
+ * Default: none
92
+
93
+ * `ssh_attempt_users` - List of users to attempt SSH connection on node failure
94
+ * Valid: `Array<String>`
95
+ * Default: none
96
+
97
+ * `identity_file` - Custom SSH identity file to use for connection on node failure
98
+ * Valid: `String`
99
+ * Default: none
100
+
101
+ * `nesting_bucket` - Name of bucket to store nested stack templates
102
+ * Valid: `String`
103
+ * Default: none
104
+
105
+ * `credentials` - API credentials for target orchestration API (see [miasma](https://github.com/miasma-rb/miasma))
106
+ * Valid: `Hash`
107
+ * Default: none
108
+
109
+ * `callbacks` - Callbacks to execute around API calls
110
+ * Valid: `Hash`
111
+ * Default: none
112
+ * `before` - Callbacks to execute before _any_ API call
113
+ * Valid: `Array<String>`
114
+ * Default: none
115
+ * `after` - Callbacks to execute after _any_ API call
116
+ * Valid: `Array<String>`
117
+ * Default: none
118
+ * `before_COMMAND` - Callbacks to execute before specific `COMMAND` API call
119
+ * Valid: `Array<String>`
120
+ * Default: none
121
+ * `after_COMMAND` - Callbacks to execute after specific `COMMAND` API call
122
+ * Valid: `Array<String>`
123
+ * Default: none
124
+ * `template` - Callbacks to execute on template
125
+ * Valid: `Array<String>`
126
+ * Default: none
127
+ * `default` - Callbacks to always execute
128
+ * Valid: `Array<String>`
129
+ * Default: none
130
+ * `require` - List of custom libraries to load
131
+ * Valid: `Array<String>`
132
+ * Default: none
133
+
134
+ * `retry` - Configuration of API request retries
135
+ * Valid: `Hash`
136
+ * Default: none
137
+ * `type` - Type of retry
138
+ * Valid: `"flat"`, `"linear"`, `"exponential"`
139
+ * Default: `"exponential"`
140
+ * `interval` - Base wait interval for retry
141
+ * Valid: `Numeric`
142
+ * Default: 5
143
+ * `max_attempts` - Maximum number of attempts allowed
144
+ * Valid: `Numeric`
145
+ * Default: 20
146
+ * _NOTE_: Set to `nil` for infinite retry
147
+
73
148
  ## Commands
74
149
 
75
150
  * `sfn list`
@@ -81,45 +156,20 @@ end
81
156
  * `sfn inspect`
82
157
  * `sfn validate`
83
158
 
159
+ _NOTE: All commands respond to `--help` and will provide a full list of valid options._
160
+
84
161
  ### `sfn list`
85
162
 
86
163
  Provides listing of current stacks and state of each stack.
87
164
 
88
- #### Supported options
89
-
90
- * `--attribute ATTR` stack attribute to display
91
- * `--status STATUS` match stacks with given status
92
-
93
165
  ### `sfn validate`
94
166
 
95
167
  Validates template with API
96
168
 
97
- #### Supported options
98
-
99
- * `--[no-]processing` enable template processing
100
- * `--file PATH` path to stack template file
101
- * `--translate PROVIDER` translate template to provider
102
- * `--[no-]apply-nesting` apply template nesting logic
103
- * `--nesting-bucket BUCKET` asset store bucket to place nested stack templates
104
-
105
169
  ### `sfn create NAME`
106
170
 
107
171
  Creates a new stack with the provided name (`NAME`).
108
172
 
109
- #### Supported options
110
-
111
- * `--timeout MINUTES` stack creation timeout limit
112
- * `--[no-]rollback` disable rollback on failure
113
- * `--capability CAPABILITY` enable capability within API
114
- * `--notifications ARN` add notification ARN
115
- * `--print-only` print stack template JSON and exit
116
- * `--apply-stack NAME` apply existing stack outputs
117
- * `--[no-]processing` enable template processing
118
- * `--file PATH` path to stack template file
119
- * `--translate PROVIDER` translate template to provider
120
- * `--[no-]apply-nesting` apply template nesting logic
121
- * `--nesting-bucket BUCKET` asset store bucket to place nested stack templates
122
-
123
173
  #### Apply Stacks
124
174
 
125
175
  The `--apply-stack` option allows providing the name of an existing
@@ -193,16 +243,6 @@ resources are supported.
193
243
 
194
244
  Update an existing stack.
195
245
 
196
- #### Supported options
197
-
198
- * `--print-only` print stack template JSON and exit
199
- * `--apply-stack NAME` apply existing stack outputs
200
- * `--[no-]processing` enable template processing
201
- * `--file PATH` path to stack template file
202
- * `--translate PROVIDER` translate template to provider
203
- * `--[no-]apply-nesting` apply template nesting logic
204
- * `--nesting-bucket BUCKET` asset store bucket to place nested stack templates
205
-
206
246
  ### `sfn destroy STACK`
207
247
 
208
248
  Destroy an existing stack.
@@ -232,18 +272,10 @@ stack is "in progress", the polling option will result in
232
272
  polling and displaying new events until the stack reaches a
233
273
  completed state.
234
274
 
235
- #### Supported options
236
-
237
- * `--[no-]poll` poll for new events until completed state reached
238
-
239
275
  ### `sfn describe STACK`
240
276
 
241
277
  Display resources and outputs of give stack.
242
278
 
243
- #### Supported options
244
-
245
- * `--resources` display resources
246
- * `--outputs` display outputs
247
279
 
248
280
  ### `sfn inspect STACK`
249
281
 
@@ -252,7 +284,7 @@ underlying resource modeling objects provided via the
252
284
  [miasma][miasma] library. It also provides extra helpers for
253
285
  running common inspection commands.
254
286
 
255
- ### Supported options
287
+ ### Interesting `inspect` options
256
288
 
257
289
  * `--nodes` list node addresses within stack
258
290
  * `--instance-failure [LOG_FILE]` print log file from failed instance
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+
4
+ unless(system("yardoc"))
5
+ $stderr.puts 'ACK: Failed to create docs!'
6
+ exit -1
7
+ end
8
+
9
+ FileUtils.mkdir_p('doc/UserDocs')
10
+
11
+ Dir.glob('docs/**/*').each do |path|
12
+ next unless File.file?(path)
13
+ content = File.read(path)
14
+ rel_path = path.sub(/.*?docs\//, '')
15
+ new_path = File.join('doc/UserDocs', rel_path)
16
+ user_doc_root = (['..'] * rel_path.scan('/').size).join('/')
17
+ unless(user_doc_root.to_s.empty?)
18
+ user_doc_root << '/'
19
+ end
20
+ FileUtils.mkdir_p(File.dirname(new_path))
21
+ File.open(new_path, 'w') do |file|
22
+ file.puts content
23
+ end
24
+ if(new_path.end_with?('.md'))
25
+ File.open(new_path.sub('.md', '.html'), 'w') do |file|
26
+ file.print "<!DOCTYPE html><html><head><title>SparkleFormation CLI User Documentation</title><script src=\"#{user_doc_root}v/0.3.2/marked.js\"></script><script src=\"#{user_doc_root}v/jquery-2.1.3.min.js\"></script><script src=\"#{user_doc_root}v/loader.js\"></script><script src=\"#{user_doc_root}v/highlight.min.js\"></script><link rel=\"stylesheet\" href=\"#{user_doc_root}v/bootstrap.min.css\"></link><link rel=\"stylesheet\" href=\"#{user_doc_root}v/highlight.min.css\"></link><link rel=\"stylesheet\" href=\"#{user_doc_root}v/finalizer.css\"></link>"
27
+ file.print "</head><body><div class=\"markdown-body\"><div class=\"navbar navbar-top\"><div class=\"navbar-inner\"><div class=\"container\"><div class=\"navbar-brand\"><a href=\"#{user_doc_root}README.html\">SparkleFormation - User documentation</a></div></div></div></div><div class=\"panel panel-default\"><div class=\"panel-body\" id=\"content\"></div></div></div>"
28
+ file.print "</body></html>"
29
+ end
30
+ end
31
+ end
32
+
33
+ File.open('doc/UserDocs/index.html', 'w') do |file|
34
+ file.puts '<html><head><meta http-equiv="refresh" content="0; url=README.html" /></head></html>'
35
+ end
36
+
37
+ puts 'done.'
@@ -0,0 +1,94 @@
1
+ require 'sfn'
2
+
3
+ module Sfn
4
+ class Callback
5
+ class StackPolicy < Callback
6
+
7
+ # Policy to apply prior to stack deletion
8
+ DEFENSELESS_POLICY = {
9
+ 'Statement' => [{
10
+ 'Effect' => 'Allow',
11
+ 'Action' => 'Update:*',
12
+ 'Resource' => '*',
13
+ 'Principal' => '*'
14
+ }]
15
+ }
16
+
17
+ # @return [Smash] cached policies
18
+ attr_reader :policies
19
+
20
+ # Overload to init policy cache
21
+ #
22
+ # @return [self]
23
+ def initialize(*args)
24
+ super
25
+ @policies = Smash.new
26
+ end
27
+
28
+ # Submit all cached policies
29
+ #
30
+ # @param args [Hash]
31
+ def submit_policy(args)
32
+ ui.info 'Submitting stack policy documents'
33
+ stack = args[:api_stack]
34
+ ([stack] + stack.nested_stacks).compact.each do |p_stack|
35
+ run_action "Applying stack policy to #{ui.color(p_stack.name, :yellow)}" do
36
+ save_stack_policy(p_stack)
37
+ end
38
+ end
39
+ ui.info 'Stack policy documents successfully submitted!'
40
+ end
41
+ alias_method :after_create, :submit_policy
42
+ alias_method :after_update, :submit_policy
43
+
44
+ # Update all policies to allow resource destruction
45
+ def before_destroy(args)
46
+ ui.warn 'All policies will be disabled for stack destruction!'
47
+ ui.confirm 'Continue with stack destruction'
48
+ stack = args[:api_stack]
49
+ ([stack] + stack.nested_stacks).compact.each do |p_stack|
50
+ @policies[p_stack.name] = DEFENSELESS_POLICY
51
+ run_action "Disabling stack policy for #{ui.color(p_stack.name, :yellow)}" do
52
+ save_stack_policy(p_stack)
53
+ end
54
+ end
55
+ ui.warn "Policy modification for deletion not currently enabled!"
56
+ end
57
+
58
+ # Generate stack policy for stack and cache for the after hook
59
+ # to handle
60
+ #
61
+ # @param info [Hash]
62
+ def template(info)
63
+ if(info[:sparkle_stack])
64
+ @policies.set(info[:stack_name],
65
+ info[:sparkle_stack].generate_policy
66
+ )
67
+ end
68
+ end
69
+
70
+ # Save the cached policy for the given stack
71
+ #
72
+ # @param p_stack [Miasma::Models::Orchestration::Stack]
73
+ # @return [NilClass]
74
+ def save_stack_policy(p_stack)
75
+ result = p_stack.api.request(
76
+ :path => '/',
77
+ :method => :post,
78
+ :form => Smash.new(
79
+ 'Action' => 'SetStackPolicy',
80
+ 'StackName' => p_stack.id,
81
+ 'StackPolicyBody' => MultiJson.dump(
82
+ @policies.fetch(p_stack.id,
83
+ @policies.fetch(p_stack.data[:logical_id],
84
+ @policies[p_stack.name]
85
+ )
86
+ )
87
+ )
88
+ )
89
+ )
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,48 @@
1
+ require 'sfn'
2
+
3
+ module Sfn
4
+ # Interface for injecting custom functionality
5
+ class Callback
6
+
7
+ autoload :StackPolicy, 'sfn/callback/stack_policy'
8
+
9
+ # @return [Bogo::Ui]
10
+ attr_reader :ui
11
+ # @return [Smash]
12
+ attr_reader :config
13
+
14
+ # Create a new callback instance
15
+ #
16
+ # @param [Bogo::Ui]
17
+ # @param [Smash] configuration hash
18
+ # @param [Array<String>] arguments from the CLI
19
+ # @param [Provider] API connection
20
+ #
21
+ # @return [self]
22
+ def initialize(ui, config, arguments, api)
23
+ @ui = ui
24
+ @config = config
25
+ @arguments = arguments
26
+ @api = api
27
+ end
28
+
29
+ # Wrap action within status text
30
+ #
31
+ # @param msg [String] action text
32
+ # @yieldblock action to perform
33
+ # @return [Object] result of yield
34
+ def run_action(msg)
35
+ ui.info("#{msg}... ", :nonewline)
36
+ begin
37
+ result = yield
38
+ ui.puts ui.color('complete!', :green, :bold)
39
+ result
40
+ rescue => e
41
+ ui.puts ui.color('error!', :red, :bold)
42
+ ui.error "Reason - #{e}"
43
+ raise
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -25,7 +25,7 @@ module Sfn
25
25
  end
26
26
 
27
27
  unless(config[:print_only])
28
- ui.info "#{ui.color('SparkleFormation:', :bold)} #{ui.color('create', :green)}"
28
+ ui.info "#{ui.color('Cloud Formation:', :bold)} #{ui.color('create', :green)}"
29
29
  end
30
30
 
31
31
  stack_info = "#{ui.color('Name:', :bold)} #{name}"
@@ -66,32 +66,31 @@ module Sfn
66
66
  end
67
67
 
68
68
  populate_parameters!(stack.template)
69
- stack.parameters = config[:parameters]
69
+ stack.parameters = config_root_parameters
70
70
 
71
71
  stack.template = translate_template(stack.template)
72
- stack.save
73
72
 
74
- end
75
-
76
- if(stack)
77
- if(config[:poll])
78
- poll_stack(stack.name)
79
- stack = provider.connection.stacks.get(name)
80
-
81
- if(stack.reload.state == :create_complete)
82
- ui.info "Stack create complete: #{ui.color('SUCCESS', :green)}"
83
- namespace.const_get(:Describe).new({:outputs => true}, [name]).execute!
73
+ api_action!(:api_stack => stack) do
74
+ stack.save
75
+ if(config[:poll])
76
+ poll_stack(stack.name)
77
+ stack = provider.connection.stacks.get(name)
78
+
79
+ if(stack.reload.state == :create_complete)
80
+ ui.info "Stack create complete: #{ui.color('SUCCESS', :green)}"
81
+ namespace.const_get(:Describe).new({:outputs => true}, [name]).execute!
82
+ else
83
+ ui.fatal "Create of new stack #{ui.color(name, :bold)}: #{ui.color('FAILED', :red, :bold)}"
84
+ raise
85
+ end
84
86
  else
85
- ui.fatal "Create of new stack #{ui.color(name, :bold)}: #{ui.color('FAILED', :red, :bold)}"
86
- ui.info ""
87
- namespace.const_get(:Inspect).new({:instance_failure => true}, [name]).execute!
88
- raise
87
+ ui.warn 'Stack state polling has been disabled.'
88
+ ui.info "Stack creation initialized for #{ui.color(name, :green)}"
89
89
  end
90
- else
91
- ui.warn 'Stack state polling has been disabled.'
92
- ui.info "Stack creation initialized for #{ui.color(name, :green)}"
93
90
  end
91
+
94
92
  end
93
+
95
94
  end
96
95
 
97
96
  end
@@ -15,18 +15,24 @@ module Sfn
15
15
  # Run the stack describe action
16
16
  def execute!
17
17
  stack_name = name_args.last
18
- stack = provider.connection.stacks.get(stack_name)
19
- if(stack)
20
- display = [].tap do |to_display|
21
- AVAILABLE_DISPLAYS.each do |display_option|
22
- if(config[display_option])
23
- to_display << display_option
18
+ root_stack = api_action! do
19
+ provider.connection.stacks.get(stack_name)
20
+ end
21
+ if(root_stack)
22
+ ([root_stack] + root_stack.nested_stacks).compact.each do |stack|
23
+ ui.info "Stack description of #{ui.color(stack.name, :bold)}:"
24
+ display = [].tap do |to_display|
25
+ AVAILABLE_DISPLAYS.each do |display_option|
26
+ if(config[display_option])
27
+ to_display << display_option
28
+ end
24
29
  end
25
30
  end
26
- end
27
- display = AVAILABLE_DISPLAYS.dup if display.empty?
28
- display.each do |display_method|
29
- self.send(display_method, stack)
31
+ display = AVAILABLE_DISPLAYS.dup if display.empty?
32
+ display.each do |display_method|
33
+ self.send(display_method, stack)
34
+ end
35
+ ui.puts
30
36
  end
31
37
  else
32
38
  ui.fatal "Failed to find requested stack: #{ui.color(stack_name, :bold, :red)}"
@@ -29,7 +29,9 @@ module Sfn
29
29
  stack = provider.connection.stacks.get(stack_name)
30
30
  if(stack)
31
31
  nested_stack_cleanup!(stack)
32
- stack.destroy
32
+ api_action!(:api_stack => stack) do
33
+ stack.destroy
34
+ end
33
35
  ui.info "Destroy request complete for stack: #{ui.color(stack_name, :red)}"
34
36
  else
35
37
  ui.warn "Failed to locate requested stack: #{ui.color(stack_name, :bold)}"
@@ -42,13 +44,16 @@ module Sfn
42
44
  ui.error "Stack polling is not available when multiple stack deletion is requested!"
43
45
  end
44
46
  end
45
- ui.info " -> Destroyed SparkleFormation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
47
+ ui.info " -> Destroyed Cloud Formation#{plural}: #{ui.color(stacks.join(', '), :bold, :red)}"
46
48
  end
47
49
 
48
50
  # Cleanup persisted templates if nested stack resources are included
49
51
  def nested_stack_cleanup!(stack)
52
+ stack.nested_stacks.each do |n_stack|
53
+ nested_stack_cleanup!(n_stack)
54
+ end
50
55
  nest_stacks = stack.template.fetch('Resources', {}).values.find_all do |resource|
51
- resource['Type'] == 'AWS::CloudFormation::Stack'
56
+ resource['Type'] == stack.api.class.const_get(:RESOURCE_MAPPING).key(stack.class).to_s
52
57
  end.each do |resource|
53
58
  url = resource['Properties']['TemplateURL']
54
59
  if(url)
@@ -19,32 +19,34 @@ module Sfn
19
19
  @stacks << stack
20
20
  discover_stacks(stack)
21
21
  if(stack)
22
- table = ui.table(self) do
23
- table(:border => false) do
24
- events = get_events
25
- row(:header => true) do
26
- allowed_attributes.each do |attr|
27
- column attr.split('_').map(&:capitalize).join(' '), :width => ((val = events.map{|e| e[attr].to_s.length}.push(attr.length).max + 2) > 70 ? 70 : val)
28
- end
29
- end
30
- events.each do |event|
31
- row do
22
+ api_action!(:api_stack => stack) do
23
+ table = ui.table(self) do
24
+ table(:border => false) do
25
+ events = get_events
26
+ row(:header => true) do
32
27
  allowed_attributes.each do |attr|
33
- column event[attr]
28
+ column attr.split('_').map(&:capitalize).join(' '), :width => ((val = events.map{|e| e[attr].to_s.length}.push(attr.length).max + 2) > 70 ? 70 : val)
29
+ end
30
+ end
31
+ events.each do |event|
32
+ row do
33
+ allowed_attributes.each do |attr|
34
+ column event[attr]
35
+ end
34
36
  end
35
37
  end
36
38
  end
37
- end
38
- end.display
39
- if(config[:poll])
40
- while(stack.in_progress?)
41
- to_wait = config.fetch(:poll_wait_time, 10).to_f
42
- while(to_wait > 0)
43
- sleep(0.1)
44
- to_wait -= 0.1
39
+ end.display
40
+ if(config[:poll])
41
+ while(stack.in_progress?)
42
+ to_wait = config.fetch(:poll_wait_time, 10).to_f
43
+ while(to_wait > 0)
44
+ sleep(0.1)
45
+ to_wait -= 0.1
46
+ end
47
+ stack.reload
48
+ table.display
45
49
  end
46
- stack.reload
47
- table.display
48
50
  end
49
51
  end
50
52
  else
@@ -64,7 +66,7 @@ module Sfn
64
66
  stack.events.all.map do |e|
65
67
  e.attributes.merge(:stack_name => stack.name).to_smash
66
68
  end
67
- end.flatten.compact
69
+ end.flatten.compact.find_all{|e| e[:time] }
68
70
  stack_events.sort do |x,y|
69
71
  Time.parse(x[:time].to_s) <=> Time.parse(y[:time].to_s)
70
72
  end
@@ -13,12 +13,14 @@ module Sfn
13
13
  stack_name = name_args.last
14
14
  stack = provider.connection.stacks.get(stack_name)
15
15
  ui.info "Stack inspection #{ui.color(stack_name, :bold)}:"
16
- outputs = [:attribute, :nodes, :instance_failure].map do |key|
17
- if(config.has_key?(key))
18
- send("display_#{key}", stack)
19
- key
20
- end
21
- end.compact
16
+ outputs = api_action!(:api_stack => stack) do
17
+ [:attribute, :nodes, :instance_failure].map do |key|
18
+ if(config.has_key?(key))
19
+ send("display_#{key}", stack)
20
+ key
21
+ end
22
+ end.compact
23
+ end
22
24
  if(outputs.empty?)
23
25
  ui.info ' Stack dump:'
24
26
  ui.puts MultiJson.dump(