stacker 0.1.0 → 0.2.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: 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: []