cfncli 0.3.0 → 0.3.1
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 +8 -8
- data/README.md +1 -0
- data/exe/cfncli +1 -2
- data/lib/cfncli/cli.rb +70 -9
- data/lib/cfncli/cloudformation.rb +36 -10
- data/lib/cfncli/config.rb +0 -63
- data/lib/cfncli/event_streamer.rb +13 -4
- data/lib/cfncli/stack.rb +20 -5
- data/lib/cfncli/thor_yaml.rb +9 -0
- data/lib/cfncli/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTFjZTBhZDk1Y2JjZWM5YWE0MWFlOTlhNTEwZDEyYjRjZWFkNDk2Zg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTFiMzU2MzM0YjVkNjNiNWY0Yzg3YjdhYjBhOTg0Y2FlZGRjMWFlNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDM5NmE0MzczOTMzN2E1ODljYjgwYjE4ZjNhZjllYWVjM2Y4ZWI0NjEwMzFj
|
10
|
+
MzFmM2EyNWM4ZDA2NTE3YjU3ZjM3NzljMDdiMzczNDkwM2VlYWRhZWFlNTYy
|
11
|
+
MmEwZjg0ZWFhZmI5ZjVhYTE2ZGU1MTFmMGRhMTRiNDdjZmZiNGE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGViNTIwM2FjOGE4NjViM2M4MzA5ZTdkY2U3OWJmZTM2YThlMjVjOWMwNzdm
|
14
|
+
YWVhMjM1YjJmZGI2ZTU4OWVhM2FhODExNzU2MzcwNTVmNmJkYTFjNzZmMTgz
|
15
|
+
M2IzMmVlM2U3MjU3ZWJjNGMyMDE4ZDFkMWU2MWE5Njk3MzNlMmE=
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# cfncli
|
2
|
+
[](https://badge.fury.io/rb/cfncli)
|
2
3
|
[](https://codeclimate.com/github/lethalpaga/cfncli)
|
3
4
|
[](https://codeclimate.com/github/lethalpaga/cfncli/coverage)
|
4
5
|
|
data/exe/cfncli
CHANGED
data/lib/cfncli/cli.rb
CHANGED
@@ -3,9 +3,14 @@ require 'aws-sdk'
|
|
3
3
|
|
4
4
|
require 'cfncli/cloudformation'
|
5
5
|
require 'cfncli/config'
|
6
|
+
require 'cfncli/thor_yaml'
|
7
|
+
require 'cfncli/logger'
|
8
|
+
require 'cfncli/version'
|
6
9
|
|
7
10
|
module CfnCli
|
8
11
|
class Cli < Thor
|
12
|
+
include ThorYamlLoader
|
13
|
+
include Loggable
|
9
14
|
|
10
15
|
module ExitCode
|
11
16
|
OK = 0
|
@@ -18,12 +23,16 @@ module CfnCli
|
|
18
23
|
type: :numeric,
|
19
24
|
default: 1,
|
20
25
|
desc: 'Log level to display (0=DEBUG, 1=INFO, 2=ERROR, 3=CRITICAL)'
|
26
|
+
|
27
|
+
class_option 'config_file',
|
28
|
+
type: :string,
|
29
|
+
default: 'cfncli.yml',
|
30
|
+
desc: 'Configuration file'
|
21
31
|
|
22
32
|
# Stack options
|
23
33
|
method_option 'stack_name',
|
24
34
|
alias: '-n',
|
25
35
|
type: :string,
|
26
|
-
required: true,
|
27
36
|
desc: 'Cloudformation stack name'
|
28
37
|
|
29
38
|
method_option 'template_body',
|
@@ -114,22 +123,27 @@ module CfnCli
|
|
114
123
|
opts = process_params(options)
|
115
124
|
|
116
125
|
stack_name = opts['stack_name']
|
117
|
-
|
118
|
-
|
126
|
+
fail ArgumentError, 'stack_name is required' unless stack_name
|
127
|
+
|
119
128
|
timeout = consume_option(opts, 'timeout')
|
120
|
-
|
129
|
+
interval = consume_option(opts, 'interval')
|
130
|
+
retries = timeout / interval
|
121
131
|
fail_on_noop = consume_option(opts, 'fail_on_noop')
|
122
132
|
list_events = consume_option(opts, 'list_events')
|
133
|
+
config_file = consume_option(opts, 'config_file')
|
123
134
|
|
124
135
|
ENV['CFNCLI_LOG_LEVEL'] = consume_option(opts, 'log_level').to_s
|
125
136
|
|
137
|
+
logger.debug "Apply parameters: #{options.inspect}"
|
138
|
+
|
126
139
|
client_config = Config::CfnClient.new(interval, retries, fail_on_noop)
|
127
140
|
|
128
141
|
res = ExitCode::OK
|
129
|
-
cfn.create_stack(opts, client_config)
|
130
142
|
if list_events
|
131
|
-
cfn.
|
143
|
+
cfn.apply_and_list_events(opts, client_config)
|
132
144
|
res = ExitCode::STACK_ERROR unless cfn.stack_successful? stack_name
|
145
|
+
else
|
146
|
+
cfn.create_stack(opts, client_config)
|
133
147
|
end
|
134
148
|
|
135
149
|
puts "Stack creation #{res == 0 ? 'successful' : 'failed'}"
|
@@ -142,10 +156,9 @@ module CfnCli
|
|
142
156
|
method_option 'stack_name',
|
143
157
|
alias: '-n',
|
144
158
|
type: :string,
|
145
|
-
required: true,
|
146
159
|
desc: 'Name or ID of the Cloudformation stack'
|
147
|
-
|
148
|
-
# Application options
|
160
|
+
|
161
|
+
# Application options.
|
149
162
|
method_option 'interval',
|
150
163
|
type: :numeric,
|
151
164
|
default: 10,
|
@@ -159,9 +172,57 @@ module CfnCli
|
|
159
172
|
desc 'events', 'Displays the events for a stack in realtime'
|
160
173
|
def events
|
161
174
|
stack_name = options['stack_name']
|
175
|
+
|
176
|
+
fail ArgumentError, 'stack_name is required' unless stack_name
|
177
|
+
|
162
178
|
config = Config::CfnClient.new(options['interval'], options['retries'])
|
163
179
|
cfn.events(stack_name, config)
|
164
180
|
end
|
181
|
+
|
182
|
+
method_option 'stack_name',
|
183
|
+
aliases: ['-n'],
|
184
|
+
type: :string,
|
185
|
+
desc: 'Name or ID of the Cloudformation stack'
|
186
|
+
|
187
|
+
# Application options.
|
188
|
+
method_option 'interval',
|
189
|
+
type: :numeric,
|
190
|
+
default: 10,
|
191
|
+
desc: 'Polling interval (in seconds) for the cloudformation events'
|
192
|
+
|
193
|
+
method_option 'timeout',
|
194
|
+
type: :numeric,
|
195
|
+
default: 1800,
|
196
|
+
desc: 'Timeout (in seconds) for the stack event listing'
|
197
|
+
|
198
|
+
desc 'delete', 'Deletes a stack'
|
199
|
+
def delete
|
200
|
+
opts = options.dup
|
201
|
+
stack_name = opts['stack_name']
|
202
|
+
|
203
|
+
fail ArgumentError, 'stack_name is required' unless stack_name
|
204
|
+
|
205
|
+
interval = consume_option(opts, 'interval')
|
206
|
+
timeout = consume_option(opts, 'timeout')
|
207
|
+
consume_option(opts, 'log_level')
|
208
|
+
consume_option(opts, 'config_file')
|
209
|
+
retries = timeout / interval
|
210
|
+
|
211
|
+
config = Config::CfnClient.new(interval, retries)
|
212
|
+
cfn.delete_stack(opts, config)
|
213
|
+
end
|
214
|
+
|
215
|
+
method_option 'verbose',
|
216
|
+
aliases: ['-v'],
|
217
|
+
type: :boolean,
|
218
|
+
default: false,
|
219
|
+
desc: 'Displays the full path to the command'
|
220
|
+
desc 'version', 'Display the version'
|
221
|
+
def version
|
222
|
+
program_name = $PROGRAM_NAME
|
223
|
+
program_name = File.basename program_name unless options['verbose']
|
224
|
+
puts "#{program_name} v#{CfnCli::VERSION}"
|
225
|
+
end
|
165
226
|
|
166
227
|
no_tasks do
|
167
228
|
# Reads an option from a hash and deletes it
|
@@ -15,7 +15,7 @@ module CfnCli
|
|
15
15
|
end
|
16
16
|
|
17
17
|
# Creates a stack and wait for the creation to be finished
|
18
|
-
# @param options [Hash] Options for the stack creation
|
18
|
+
# @param options [Hash] Options for the stack creation
|
19
19
|
# (@see http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudFormation/Client.html)
|
20
20
|
def create_stack(options, config = nil)
|
21
21
|
create_or_update_stack(options, config)
|
@@ -24,27 +24,51 @@ module CfnCli
|
|
24
24
|
# Creates a stack if it doesn't exist otherwise update it
|
25
25
|
def create_or_update_stack(options, config = nil)
|
26
26
|
opts = process_params(options.dup)
|
27
|
-
|
27
|
+
|
28
28
|
stack_name = opts['stack_name']
|
29
|
-
|
30
29
|
stack = create_stack_obj(stack_name, config)
|
31
|
-
|
30
|
+
|
32
31
|
if stack.exists?
|
33
32
|
stack.update(opts)
|
34
33
|
else
|
35
34
|
stack.create(opts)
|
36
35
|
end
|
37
|
-
|
36
|
+
|
38
37
|
stack
|
39
38
|
end
|
40
39
|
|
40
|
+
# Creates or update the stack and list events
|
41
|
+
def apply_and_list_events(options, config = nil)
|
42
|
+
# Create/update the stack
|
43
|
+
logger.debug "Creating stack #{options['stack_name']}"
|
44
|
+
stack = create_or_update_stack(options, config)
|
45
|
+
|
46
|
+
events(stack.stack_id)
|
47
|
+
end
|
48
|
+
|
41
49
|
# List stack events
|
42
|
-
def events(
|
43
|
-
stack =
|
44
|
-
stack
|
50
|
+
def events(stack_or_name, reset_events = true, poller = nil, streamer = nil, config = nil)
|
51
|
+
stack = stack_or_name
|
52
|
+
stack = create_stack_obj(stack_or_name, config) unless stack_or_name.is_a? CfnCli::Stack
|
53
|
+
|
54
|
+
poller ||= EventPoller.new
|
55
|
+
streamer ||= EventStreamer.new(stack, config)
|
56
|
+
|
57
|
+
streamer.reset_events if reset_events
|
58
|
+
|
59
|
+
logger.debug "Listing events for stack #{stack.stack_name}"
|
60
|
+
stack.list_events(poller, streamer, config)
|
45
61
|
end
|
46
62
|
|
47
|
-
#
|
63
|
+
# Delete a stack
|
64
|
+
def delete_stack(options, config)
|
65
|
+
stack = create_stack_obj(options['stack_name'], config)
|
66
|
+
options['stack_name'] = stack.stack_id
|
67
|
+
stack.delete(options, config)
|
68
|
+
events(stack.stack_id, config)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the stack status
|
48
72
|
def stack_successful?(stack_name)
|
49
73
|
Stack.new(stack_name).succeeded?
|
50
74
|
end
|
@@ -66,7 +90,9 @@ module CfnCli
|
|
66
90
|
# Creates a new stack object
|
67
91
|
# Mainly useful to mock it in unit tests
|
68
92
|
def create_stack_obj(stack_name, config)
|
69
|
-
CfnCli::Stack.new(stack_name, config)
|
93
|
+
stack = CfnCli::Stack.new(stack_name, config)
|
94
|
+
stack.fetch_stack_id if stack.exists?
|
95
|
+
stack
|
70
96
|
end
|
71
97
|
|
72
98
|
# Process the parameters
|
data/lib/cfncli/config.rb
CHANGED
@@ -20,68 +20,5 @@ module CfnCli
|
|
20
20
|
@fail_on_noop = fail_on_noop
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
24
|
-
class Parameters
|
25
|
-
def initialize(content)
|
26
|
-
@content = content
|
27
|
-
end
|
28
|
-
|
29
|
-
# Converts parameters to command-line arguments
|
30
|
-
def to_args(content = nil)
|
31
|
-
content ||= to_a
|
32
|
-
content.join(' ')
|
33
|
-
end
|
34
|
-
|
35
|
-
# Get an array of parameters
|
36
|
-
def to_a
|
37
|
-
from_hash(@content) if @content.is_a? Hash
|
38
|
-
end
|
39
|
-
|
40
|
-
# Format parameters for thor
|
41
|
-
# @param given_args [Array] Optional array of existing arguments
|
42
|
-
def to_thor(given_args = nil)
|
43
|
-
args = []
|
44
|
-
|
45
|
-
if given_args
|
46
|
-
given_args = given_args.dup
|
47
|
-
args << given_args.shift
|
48
|
-
end
|
49
|
-
args += to_a
|
50
|
-
args += given_args if given_args
|
51
|
-
|
52
|
-
args
|
53
|
-
end
|
54
|
-
|
55
|
-
protected
|
56
|
-
|
57
|
-
def from_hash(content)
|
58
|
-
args = []
|
59
|
-
content.each_pair do |key, value|
|
60
|
-
case value
|
61
|
-
when Hash
|
62
|
-
value = parse_hash(value)
|
63
|
-
when Array
|
64
|
-
value = parse_array(value)
|
65
|
-
end
|
66
|
-
|
67
|
-
args += ["--#{key}", value]
|
68
|
-
end
|
69
|
-
|
70
|
-
args
|
71
|
-
end
|
72
|
-
|
73
|
-
def parse_hash(content)
|
74
|
-
args = []
|
75
|
-
content.each_pair do |key, value|
|
76
|
-
args += ["#{key}:#{value}"]
|
77
|
-
end
|
78
|
-
|
79
|
-
args
|
80
|
-
end
|
81
|
-
|
82
|
-
def parse_array(value)
|
83
|
-
"[#{value.join(',')}]"
|
84
|
-
end
|
85
|
-
end
|
86
23
|
end
|
87
24
|
end
|
@@ -16,16 +16,25 @@ module CfnCli
|
|
16
16
|
# Wait for events. This will exit when the
|
17
17
|
# stack reaches a finished state
|
18
18
|
# @yields [CfnEvent] Events for the stack
|
19
|
-
def each_event
|
19
|
+
def each_event(&block)
|
20
20
|
Waiting.wait(interval: config.interval, max_attempts: config.retries) do |waiter|
|
21
|
-
|
22
|
-
yield event unless seen?(event)
|
23
|
-
end
|
21
|
+
list_events(&block)
|
24
22
|
|
25
23
|
waiter.done if stack.finished?
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
27
|
+
def list_events(&block)
|
28
|
+
@next_token = stack.events(@next_token).each do |event|
|
29
|
+
yield event unless seen?(event) if block_given?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Mark all the existing events as 'seen'
|
34
|
+
def reset_events
|
35
|
+
list_events do; end
|
36
|
+
end
|
37
|
+
|
29
38
|
private
|
30
39
|
|
31
40
|
attr_accessor :seen_events
|
data/lib/cfncli/stack.rb
CHANGED
@@ -15,7 +15,7 @@ module CfnCli
|
|
15
15
|
attr_reader :stack_name
|
16
16
|
|
17
17
|
class StackNotFoundError < StandardError; end
|
18
|
-
|
18
|
+
|
19
19
|
def initialize(stack_name, config = nil)
|
20
20
|
@stack = nil
|
21
21
|
@stack_id = nil
|
@@ -65,7 +65,15 @@ module CfnCli
|
|
65
65
|
raise e
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
|
+
# Deletes an existing stack
|
70
|
+
def delete(opts, config)
|
71
|
+
logger.debug "Deleting stack #{opts.inspect}"
|
72
|
+
# Always use the real stack ID as the stack won't be available once deleted
|
73
|
+
id = fetch_stack_id
|
74
|
+
cfn.client.delete_stack(stack_name: id)
|
75
|
+
end
|
76
|
+
|
69
77
|
# Waits for a stack to be in a finished state
|
70
78
|
# @return A boolean indicating if the operation was succesful
|
71
79
|
def wait_for_completion
|
@@ -80,13 +88,13 @@ module CfnCli
|
|
80
88
|
|
81
89
|
# List all events in real time
|
82
90
|
# @param poller [CfnCli::Poller] Poller class to display events
|
83
|
-
def list_events(poller, config = nil)
|
84
|
-
streamer
|
91
|
+
def list_events(poller, streamer = nil, config = nil)
|
92
|
+
streamer ||= EventStreamer.new(self, config)
|
85
93
|
streamer.each_event do |event|
|
86
94
|
poller.event(event)
|
87
95
|
end
|
88
96
|
end
|
89
|
-
|
97
|
+
|
90
98
|
# Get the events from the cfn stack
|
91
99
|
def events(next_token)
|
92
100
|
stack.events(next_token)
|
@@ -111,6 +119,13 @@ module CfnCli
|
|
111
119
|
transitive_states.include? stack.stack_status
|
112
120
|
end
|
113
121
|
|
122
|
+
# Gets stack id from the cfn API
|
123
|
+
def fetch_stack_id
|
124
|
+
@stack = cfn.stack(stack_id)
|
125
|
+
@stack_id = @stack.stack_id
|
126
|
+
@stack_id
|
127
|
+
end
|
128
|
+
|
114
129
|
private
|
115
130
|
|
116
131
|
# Gets stack info from the cfn API
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module ThorYamlLoader
|
2
|
+
def options
|
3
|
+
original_options = super
|
4
|
+
config_file = original_options['config_file']
|
5
|
+
return original_options unless File.exists?(config_file)
|
6
|
+
defaults = ::YAML::load_file(config_file) || {}
|
7
|
+
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
8
|
+
end
|
9
|
+
end
|
data/lib/cfncli/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfncli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- lethalpaga
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -208,6 +208,7 @@ files:
|
|
208
208
|
- lib/cfncli/logger.rb
|
209
209
|
- lib/cfncli/stack.rb
|
210
210
|
- lib/cfncli/states.rb
|
211
|
+
- lib/cfncli/thor_yaml.rb
|
211
212
|
- lib/cfncli/version.rb
|
212
213
|
homepage: https://github.com/lethalpaga/cfncli
|
213
214
|
licenses: []
|