stacker 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc3ba2bbeac3d470e4d355cf4605df35e6640720
4
- data.tar.gz: c6e53ace12414864ae41bbfe9a3ed32135daab50
3
+ metadata.gz: 2fc55049bd623960704c1fd41806d7cd649e32b7
4
+ data.tar.gz: a1251cd848cd6fc8e24ed819df0c113e9997757b
5
5
  SHA512:
6
- metadata.gz: 669c65102b76e13165b7668e56631f6b97d02ee5643f703ed2f0cdffa41c8d8844ae320e6ccfc218582a41f2e27f1f5079ecbe1ca10dd736bc42b768caae6662
7
- data.tar.gz: 25f33fa2551709a54a55cd5a3633374c183078f8dbb7508f4ff986d180e9470d0ce1910bedf3cc13a507ad7a936ab3b7c99aa20832b4ddd5e163d9ab89e01096
6
+ metadata.gz: bba3693278a33e76d4dba9ffd54e3e355fc01c72a8430aaec6566c38c5570c612b21eccfa537d302d0b5224c84f7e4e5c293c892c71dbc72500593a442ce1c76
7
+ data.tar.gz: fd29533ef84783f7e303f118ecf15e5dcea095ac0098490c3fd629833dd5db2dd4af9f87fc87e8039ce5840b931167c3cd523f75154f263346d96675873332be
data/lib/stacker/cli.rb CHANGED
@@ -10,8 +10,15 @@ module Stacker
10
10
  default_path = ENV['STACKER_PATH'] || '.'
11
11
  default_region = ENV['STACKER_REGION'] || 'us-east-1'
12
12
 
13
- method_option :path, default: default_path, banner: 'project path'
14
- method_option :region, default: default_region, banner: 'AWS region name'
13
+ method_option :path, type: :string, default: default_path,
14
+ banner: 'project path'
15
+
16
+ method_option :region, type: :string, default: default_region,
17
+ banner: 'AWS region name'
18
+
19
+ method_option :allow_destructive, type: :boolean, default: false,
20
+ banner: 'allow destructive updates'
21
+
15
22
  def initialize(*args); super(*args) end
16
23
 
17
24
  desc "init [PATH]", "Create stacker project directories"
@@ -63,9 +70,9 @@ module Stacker
63
70
 
64
71
  if yes? "Update remote template with these changes (y/n)?"
65
72
  time = Benchmark.realtime do
66
- stack.update
73
+ stack.update allow_destructive: options['allow_destructive']
67
74
  end
68
- Stacker.logger.info time stack_name, 'updated', time
75
+ Stacker.logger.info formatted_time stack_name, 'updated', time
69
76
  else
70
77
  Stacker.logger.warn 'Update skipped'
71
78
  end
@@ -74,7 +81,7 @@ module Stacker
74
81
  time = Benchmark.realtime do
75
82
  stack.create
76
83
  end
77
- Stacker.logger.info time stack_name, 'created', time
84
+ Stacker.logger.info formatted_time stack_name, 'created', time
78
85
  else
79
86
  Stacker.logger.warn 'Create skipped'
80
87
  end
@@ -139,6 +146,10 @@ YAML
139
146
  end
140
147
  end
141
148
 
149
+ def formatted_time stack, action, benchmark
150
+ "Stack #{stack} #{action} in: #{(benchmark / 60).floor} min and #{(benchmark % 60).round} seconds."
151
+ end
152
+
142
153
  def full_diff stack
143
154
  templ_diff = stack.template.diff :color
144
155
  param_diff = stack.parameters.diff :color
@@ -182,10 +193,6 @@ YAML
182
193
  stack.parameters.resolved
183
194
  end
184
195
 
185
- def time (stack, action, benchmark)
186
- return "Stack #{stack} #{action} in: #{(benchmark / 60).floor} min and #{(benchmark % 60).round} seconds."
187
- end
188
-
189
196
  def with_one_or_all stack_name = nil, &block
190
197
  yield_with_stack = proc do |stack|
191
198
  Stacker.logger.info "#{stack.name}:"
@@ -199,6 +206,14 @@ YAML
199
206
  region.stacks.each(&yield_with_stack)
200
207
  end
201
208
 
209
+ rescue Stacker::Stack::StackPolicyError => err
210
+ if options['allow_destructive']
211
+ Stacker.logger.fatal err.message
212
+ else
213
+ Stacker.logger.fatal 'Stack update policy prevents replacing or destroying resources.'
214
+ Stacker.logger.warn 'Try running again with \'--allow-destructive\''
215
+ end
216
+ exit 1
202
217
  rescue Stacker::Stack::Error => err
203
218
  Stacker.logger.fatal err.message
204
219
  exit 1
data/lib/stacker/stack.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext/hash/keys'
1
2
  require 'active_support/core_ext/hash/slice'
2
3
  require 'active_support/core_ext/module/delegation'
3
4
  require 'aws-sdk'
@@ -9,10 +10,11 @@ require 'stacker/stack/template'
9
10
  module Stacker
10
11
  class Stack
11
12
 
12
- class Error < StandardError; end
13
+ class Error < StandardError; end
14
+ class StackPolicyError < Error; end
13
15
  class DoesNotExistError < Error; end
14
16
  class MissingParameters < Error; end
15
- class UpToDateError < Error; end
17
+ class UpToDateError < Error; end
16
18
 
17
19
  extend Memoist
18
20
 
@@ -25,6 +27,25 @@ module Stacker
25
27
  status_reason
26
28
  ]
27
29
 
30
+ SAFE_UPDATE_POLICY = <<-JSON
31
+ {
32
+ "Statement" : [
33
+ {
34
+ "Effect" : "Deny",
35
+ "Action" : ["Update:Replace", "Update:Delete"],
36
+ "Principal" : "*",
37
+ "Resource" : "*"
38
+ },
39
+ {
40
+ "Effect" : "Allow",
41
+ "Action" : "Update:*",
42
+ "Principal" : "*",
43
+ "Resource" : "*"
44
+ }
45
+ ]
46
+ }
47
+ JSON
48
+
28
49
  attr_reader :region, :name, :options
29
50
 
30
51
  def initialize region, name, options = {}
@@ -87,7 +108,12 @@ module Stacker
87
108
  raise Error.new err.message
88
109
  end
89
110
 
90
- def update blocking = true
111
+ def update options = {}
112
+ options.assert_valid_keys(:blocking, :allow_destructive)
113
+
114
+ blocking = options.fetch(:blocking, true)
115
+ allow_destructive = options.fetch(:allow_destructive, false)
116
+
91
117
  if parameters.missing.any?
92
118
  raise MissingParameters.new(
93
119
  "Required parameters missing: #{parameters.missing.join ', '}"
@@ -96,11 +122,17 @@ module Stacker
96
122
 
97
123
  Stacker.logger.info 'Updating stack'
98
124
 
99
- client.update(
125
+ update_params = {
100
126
  template: template.local,
101
127
  parameters: parameters.resolved,
102
128
  capabilities: capabilities.local
103
- )
129
+ }
130
+
131
+ unless allow_destructive
132
+ update_params[:stack_policy_during_update_body] = SAFE_UPDATE_POLICY
133
+ end
134
+
135
+ client.update(update_params)
104
136
 
105
137
  wait_while_status 'UPDATE_IN_PROGRESS' if blocking
106
138
  rescue AWS::CloudFormation::Errors::ValidationError => err
@@ -116,12 +148,32 @@ module Stacker
116
148
 
117
149
  private
118
150
 
151
+ def report_status
152
+ case status
153
+ when /_COMPLETE$/
154
+ Stacker.logger.info "#{name} Status => #{status}"
155
+ when /_ROLLBACK_IN_PROGRESS$/
156
+ failure_event = client.events.enum(limit: 30).find do |event|
157
+ event.resource_status =~ /_FAILED$/
158
+ end
159
+ failure_reason = failure_event.resource_status_reason
160
+ if failure_reason =~ /stack policy/
161
+ raise StackPolicyError.new failure_reason
162
+ else
163
+ Stacker.logger.fatal "#{name} Status => #{status}"
164
+ raise Error.new "Failure Reason: #{failure_reason}"
165
+ end
166
+ else
167
+ Stacker.logger.debug "#{name} Status => #{status}"
168
+ end
169
+ end
170
+
119
171
  def wait_while_status wait_status
120
172
  while flush_cache(:status) && status == wait_status
121
- Stacker.logger.debug "#{name} Status => #{status}"
173
+ report_status
122
174
  sleep 5
123
175
  end
124
- Stacker.logger.info "#{name} Status => #{status}"
176
+ report_status
125
177
  end
126
178
 
127
179
  end
@@ -1,3 +1,3 @@
1
1
  module Stacker
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,125 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cotap, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-25 00:00:00.000000000 Z
11
+ date: 2015-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: aws-sdk
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: coderay
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: diffy
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
61
  version: '3.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: indentation
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0.0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: memoist
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ~>
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0.9'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ~>
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.9'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rainbow
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ~>
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '1.1'
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ~>
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: thor
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ~>
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0.18'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ~>
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0.18'
125
125
  - !ruby/object:Gem::Dependency
@@ -182,12 +182,12 @@ require_paths:
182
182
  - lib
183
183
  required_ruby_version: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - '>='
185
+ - - ">="
186
186
  - !ruby/object:Gem::Version
187
187
  version: 1.9.3
188
188
  required_rubygems_version: !ruby/object:Gem::Requirement
189
189
  requirements:
190
- - - '>='
190
+ - - ">="
191
191
  - !ruby/object:Gem::Version
192
192
  version: '0'
193
193
  requirements: []