automan 2.3.6 → 2.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Rakefile +5 -0
- data/automan.gemspec +3 -3
- data/lib/automan/base.rb +7 -3
- data/lib/automan/beanstalk/deployer.rb +1 -1
- data/lib/automan/beanstalk/package.rb +1 -1
- data/lib/automan/beanstalk/router.rb +1 -1
- data/lib/automan/cli/snapper.rb +29 -29
- data/lib/automan/cli/stacker.rb +19 -0
- data/lib/automan/cloudformation/launcher.rb +6 -3
- data/lib/automan/cloudformation/replacer.rb +1 -1
- data/lib/automan/cloudformation/terminator.rb +1 -1
- data/lib/automan/ec2/image.rb +3 -3
- data/lib/automan/elasticache/router.rb +79 -0
- data/lib/automan/mixins/aws_caller.rb +21 -7
- data/lib/automan/rds/snapshot.rb +35 -14
- data/lib/automan/s3/downloader.rb +10 -1
- data/lib/automan/version.rb +1 -1
- data/lib/automan.rb +1 -0
- data/spec/beanstalk/application_spec.rb +17 -17
- data/spec/beanstalk/configuration_spec.rb +24 -24
- data/spec/beanstalk/deployer_spec.rb +65 -65
- data/spec/beanstalk/router_spec.rb +18 -19
- data/spec/beanstalk/terminator_spec.rb +16 -16
- data/spec/beanstalk/uploader_spec.rb +13 -13
- data/spec/beanstalk/version_spec.rb +8 -10
- data/spec/cloudformation/launcher_spec.rb +63 -65
- data/spec/cloudformation/replacer_spec.rb +10 -10
- data/spec/cloudformation/terminator_spec.rb +23 -23
- data/spec/cloudformation/uploader_spec.rb +13 -13
- data/spec/ec2/image_spec.rb +55 -55
- data/spec/ec2/instance_spec.rb +17 -17
- data/spec/elasticache/router_spec.rb +29 -0
- data/spec/mixins/aws_caller_spec.rb +9 -9
- data/spec/mixins/utils_spec.rb +27 -28
- data/spec/rds/snapshot_spec.rb +44 -46
- data/templates/stacker/.env.example +14 -0
- data/templates/stacker/.gitignore +2 -0
- data/templates/stacker/.ruby-gemset.tt +1 -0
- data/templates/stacker/.ruby-version +1 -0
- data/templates/stacker/Gemfile +4 -0
- data/templates/stacker/bin/%app_name%.tt +62 -0
- data/templates/stacker/bin/launch_%app_name%.sh.tt +12 -0
- metadata +20 -7
@@ -17,30 +17,29 @@ describe Automan::Cloudformation::Launcher do
|
|
17
17
|
it { should respond_to :stack_update_complete? }
|
18
18
|
|
19
19
|
describe '#read_manifest' do
|
20
|
-
subject(
|
20
|
+
subject() do
|
21
21
|
AWS.stub!
|
22
|
-
|
23
|
-
s
|
22
|
+
Automan::Cloudformation::Launcher.new
|
24
23
|
end
|
25
24
|
|
26
25
|
it 'raises MissingManifestError if manifest does not exist' do
|
27
|
-
|
26
|
+
allow(subject).to receive(:manifest_exists?).and_return(false)
|
28
27
|
expect {
|
29
|
-
|
28
|
+
subject.read_manifest
|
30
29
|
}.to raise_error(Automan::Cloudformation::MissingManifestError)
|
31
30
|
end
|
32
31
|
|
33
32
|
it 'merges manifest contents into parameters hash' do
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
allow(subject).to receive(:manifest_exists?).and_return(true)
|
34
|
+
allow(subject).to receive(:s3_read).and_return('{"foo": "bar", "big": "poppa"}')
|
35
|
+
subject.parameters = {'foo'=> 'baz', 'fo'=> 'shizzle'}
|
36
|
+
subject.read_manifest
|
37
|
+
expect(subject.parameters).to eq({'foo' => 'bar', 'fo' => 'shizzle', 'big' => 'poppa'})
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
41
|
describe '#parse_template_parameters' do
|
43
|
-
subject(
|
42
|
+
subject() do
|
44
43
|
AWS.stub!
|
45
44
|
s = Automan::Cloudformation::Launcher.new
|
46
45
|
s.cfn = AWS::CloudFormation.new
|
@@ -49,31 +48,30 @@ describe Automan::Cloudformation::Launcher do
|
|
49
48
|
end
|
50
49
|
|
51
50
|
it "returns a hash" do
|
52
|
-
|
53
|
-
result =
|
54
|
-
result.class.
|
51
|
+
subject.template = File.join(File.dirname(__FILE__), 'templates', 'worker_role.json')
|
52
|
+
result = subject.parse_template_parameters
|
53
|
+
expect(result.class).to eq(Hash)
|
55
54
|
end
|
56
55
|
|
57
56
|
end
|
58
57
|
|
59
58
|
describe '#template_handle' do
|
60
|
-
subject(
|
59
|
+
subject() do
|
61
60
|
AWS.stub!
|
62
|
-
|
63
|
-
s
|
61
|
+
Automan::Cloudformation::Launcher.new
|
64
62
|
end
|
65
63
|
|
66
64
|
it "returns an s3 key if it is an s3 path" do
|
67
|
-
|
65
|
+
expect(subject.template_handle("s3://foo/bar/baz")).to be_a AWS::S3::S3Object
|
68
66
|
end
|
69
67
|
|
70
68
|
it "returns a string if it is a local file" do
|
71
|
-
|
69
|
+
expect(subject.template_handle(__FILE__)).to be_a String
|
72
70
|
end
|
73
71
|
end
|
74
72
|
|
75
73
|
describe '#validate_parameters' do
|
76
|
-
subject(
|
74
|
+
subject() do
|
77
75
|
AWS.stub!
|
78
76
|
s = Automan::Cloudformation::Launcher.new
|
79
77
|
s.logger = Logger.new('/dev/null')
|
@@ -82,99 +80,99 @@ describe Automan::Cloudformation::Launcher do
|
|
82
80
|
end
|
83
81
|
|
84
82
|
it "raises error if no parameters were specified" do
|
85
|
-
|
83
|
+
subject.parameters = nil
|
86
84
|
expect {
|
87
|
-
|
85
|
+
subject.validate_parameters
|
88
86
|
}.to raise_error Automan::Cloudformation::MissingParametersError
|
89
87
|
end
|
90
88
|
|
91
89
|
it "raises error if the template doesn't validate" do
|
92
|
-
|
90
|
+
allow(subject).to receive(:parse_template_parameters).and_return({code: 'foo', message: 'bar'})
|
93
91
|
expect {
|
94
|
-
|
92
|
+
subject.validate_parameters
|
95
93
|
}.to raise_error Automan::Cloudformation::BadTemplateError
|
96
94
|
end
|
97
95
|
|
98
96
|
it "raises error if a required parameter isn't present" do
|
99
|
-
|
97
|
+
allow(subject).to receive(:parse_template_parameters).and_return(parameters: [{parameter_key: 'foo'}])
|
100
98
|
expect {
|
101
|
-
|
99
|
+
subject.validate_parameters
|
102
100
|
}.to raise_error Automan::Cloudformation::MissingParametersError
|
103
101
|
end
|
104
102
|
|
105
103
|
it "raises no error if all required parameters are present" do
|
106
|
-
|
107
|
-
|
104
|
+
allow(subject).to receive(:parse_template_parameters).and_return(parameters: [{parameter_key: 'foo'}])
|
105
|
+
subject.parameters = {'foo' => 'bar'}
|
108
106
|
expect {
|
109
|
-
|
107
|
+
subject.validate_parameters
|
110
108
|
}.not_to raise_error
|
111
109
|
end
|
112
110
|
|
113
111
|
it "raises no error if there are no required parameters" do
|
114
|
-
|
112
|
+
allow(subject).to receive(:parse_template_parameters).and_return(parameters: [{parameter_key: 'foo', default_value: 'bar'}])
|
115
113
|
expect {
|
116
|
-
|
114
|
+
subject.validate_parameters
|
117
115
|
}.not_to raise_error
|
118
116
|
end
|
119
117
|
end
|
120
118
|
|
121
119
|
describe '#update' do
|
122
|
-
subject(
|
120
|
+
subject() do
|
123
121
|
AWS.stub!
|
124
122
|
s = Automan::Cloudformation::Launcher.new
|
125
123
|
s.logger = Logger.new('/dev/null')
|
126
|
-
s.
|
124
|
+
allow(s).to receive(:template_handle).and_return('foo')
|
127
125
|
s
|
128
126
|
end
|
129
127
|
|
130
128
|
it "ignores AWS::CloudFormation::Errors::ValidationError when no updates are to be performed" do
|
131
|
-
|
129
|
+
allow(subject).to receive(:update_stack).and_raise AWS::CloudFormation::Errors::ValidationError.new("No updates are to be performed.")
|
132
130
|
expect {
|
133
|
-
|
131
|
+
subject.update
|
134
132
|
}.to_not raise_error
|
135
133
|
end
|
136
134
|
|
137
135
|
it 're-raises any other ValidationError' do
|
138
|
-
|
136
|
+
allow(subject).to receive(:update_stack).and_raise AWS::CloudFormation::Errors::ValidationError.new("foo")
|
139
137
|
expect {
|
140
|
-
|
138
|
+
subject.update
|
141
139
|
}.to raise_error AWS::CloudFormation::Errors::ValidationError
|
142
140
|
end
|
143
141
|
end
|
144
142
|
|
145
143
|
describe '#launch_or_update' do
|
146
|
-
subject(
|
144
|
+
subject() do
|
147
145
|
AWS.stub!
|
148
146
|
s = Automan::Cloudformation::Launcher.new
|
149
147
|
s.logger = Logger.new('/dev/null')
|
150
|
-
s.
|
148
|
+
allow(s).to receive(:validate_parameters)
|
151
149
|
s
|
152
150
|
end
|
153
151
|
|
154
152
|
it "launches a new stack if it does not exist" do
|
155
|
-
|
156
|
-
|
157
|
-
|
153
|
+
allow(subject).to receive(:stack_exists?).and_return(false)
|
154
|
+
expect(subject).to receive(:launch)
|
155
|
+
subject.launch_or_update
|
158
156
|
end
|
159
157
|
|
160
158
|
it "updates an existing stack if update is enabled" do
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
159
|
+
allow(subject).to receive(:stack_exists?).and_return(true)
|
160
|
+
allow(subject).to receive(:enable_update).and_return(true)
|
161
|
+
expect(subject).to receive(:update)
|
162
|
+
subject.launch_or_update
|
165
163
|
end
|
166
164
|
|
167
165
|
it "raises an error when updating an existing stack w/o update enabled" do
|
168
|
-
|
169
|
-
|
166
|
+
allow(subject).to receive(:stack_exists?).and_return(true)
|
167
|
+
allow(subject).to receive(:enable_update).and_return(false)
|
170
168
|
expect {
|
171
|
-
|
169
|
+
subject.launch_or_update
|
172
170
|
}.to raise_error Automan::Cloudformation::StackExistsError
|
173
171
|
end
|
174
172
|
end
|
175
173
|
|
176
174
|
describe '#stack_launch_complete?' do
|
177
|
-
subject(
|
175
|
+
subject() do
|
178
176
|
AWS.stub!
|
179
177
|
s = Automan::Cloudformation::Launcher.new
|
180
178
|
s.logger = Logger.new('/dev/null')
|
@@ -182,35 +180,35 @@ describe Automan::Cloudformation::Launcher do
|
|
182
180
|
end
|
183
181
|
|
184
182
|
it 'returns true when the stack completes' do
|
185
|
-
|
186
|
-
|
183
|
+
allow(subject).to receive(:stack_status).and_return('CREATE_COMPLETE')
|
184
|
+
expect(subject.stack_launch_complete?).to be_truthy
|
187
185
|
end
|
188
186
|
|
189
187
|
it 'raises an error when the stack fails' do
|
190
|
-
|
188
|
+
allow(subject).to receive(:stack_status).and_return('CREATE_FAILED')
|
191
189
|
expect {
|
192
|
-
|
190
|
+
subject.stack_launch_complete?
|
193
191
|
}.to raise_error Automan::Cloudformation::StackCreationError
|
194
192
|
end
|
195
193
|
|
196
194
|
rollback_states = %w[ROLLBACK_COMPLETE ROLLBACK_FAILED ROLLBACK_IN_PROGRESS]
|
197
195
|
rollback_states.each do |state|
|
198
196
|
it "raises an error when the stack goes into #{state} state" do
|
199
|
-
|
197
|
+
allow(subject).to receive(:stack_status).and_return(state)
|
200
198
|
expect {
|
201
|
-
|
199
|
+
subject.stack_launch_complete?
|
202
200
|
}.to raise_error Automan::Cloudformation::StackCreationError
|
203
201
|
end
|
204
202
|
end
|
205
203
|
|
206
204
|
it 'returns false for any other state' do
|
207
|
-
|
208
|
-
|
205
|
+
allow(subject).to receive(:stack_status).and_return('foo')
|
206
|
+
expect(subject.stack_launch_complete?).to be_falsey
|
209
207
|
end
|
210
208
|
end
|
211
209
|
|
212
210
|
describe '#stack_update_complete?' do
|
213
|
-
subject(
|
211
|
+
subject() do
|
214
212
|
AWS.stub!
|
215
213
|
s = Automan::Cloudformation::Launcher.new
|
216
214
|
s.logger = Logger.new('/dev/null')
|
@@ -218,23 +216,23 @@ describe Automan::Cloudformation::Launcher do
|
|
218
216
|
end
|
219
217
|
|
220
218
|
it 'returns true when the stack completes' do
|
221
|
-
|
222
|
-
|
219
|
+
allow(subject).to receive(:stack_status).and_return('UPDATE_COMPLETE')
|
220
|
+
expect(subject.stack_update_complete?).to be_truthy
|
223
221
|
end
|
224
222
|
|
225
223
|
rollback_states = %w[UPDATE_ROLLBACK_COMPLETE UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS UPDATE_ROLLBACK_FAILED UPDATE_ROLLBACK_IN_PROGRESS]
|
226
224
|
rollback_states.each do |state|
|
227
225
|
it "raises an error when the stack enters #{state} state" do
|
228
|
-
|
226
|
+
allow(subject).to receive(:stack_status).and_return(state)
|
229
227
|
expect {
|
230
|
-
|
228
|
+
subject.stack_update_complete?
|
231
229
|
}.to raise_error Automan::Cloudformation::StackUpdateError
|
232
230
|
end
|
233
231
|
end
|
234
232
|
|
235
233
|
it 'returns false for any other state' do
|
236
|
-
|
237
|
-
|
234
|
+
allow(subject).to receive(:stack_status).and_return('foo')
|
235
|
+
expect(subject.stack_update_complete?).to be_falsey
|
238
236
|
end
|
239
237
|
end
|
240
238
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'automan'
|
2
2
|
|
3
3
|
describe Automan::Cloudformation::Replacer do
|
4
|
-
it {
|
5
|
-
it {
|
6
|
-
it {
|
7
|
-
it {
|
4
|
+
it { is_expected.to respond_to :name }
|
5
|
+
it { is_expected.to respond_to :replace_instances }
|
6
|
+
it { is_expected.to respond_to :ok_to_replace_instances? }
|
7
|
+
it { is_expected.to respond_to :stack_exists? }
|
8
8
|
|
9
9
|
describe '#ok_to_replace_instances?' do
|
10
|
-
subject(
|
10
|
+
subject() do
|
11
11
|
AWS.stub!
|
12
|
-
|
12
|
+
Automan::Cloudformation::Replacer.new
|
13
13
|
end
|
14
14
|
|
15
15
|
good_states = %w[UPDATE_COMPLETE]
|
@@ -30,28 +30,28 @@ describe Automan::Cloudformation::Replacer do
|
|
30
30
|
|
31
31
|
good_states.each do |state|
|
32
32
|
it "returns true when stack is in the right state (state: #{state})" do
|
33
|
-
|
33
|
+
expect(subject.ok_to_replace_instances?(state, Time.now)).to be_truthy
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
good_states.each do |state|
|
38
38
|
it "returns false when stack is in the right state but has not been updated in the last 5m" do
|
39
39
|
last_updated = Time.now - (5 * 60 + 1)
|
40
|
-
|
40
|
+
expect(subject.ok_to_replace_instances?(state, last_updated)).to be_falsey
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
44
|
bad_states.each do |state|
|
45
45
|
it "raises error when stack is borked (state: #{state})" do
|
46
46
|
expect {
|
47
|
-
|
47
|
+
subject.ok_to_replace_instances?(state, Time.now)
|
48
48
|
}.to raise_error Automan::Cloudformation::StackBrokenError
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
wait_states.each do |state|
|
53
53
|
it "returns false when it is not yet ok to replace (state: #{state})" do
|
54
|
-
|
54
|
+
expect(subject.ok_to_replace_instances?(state, Time.now)).to be_falsey
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'automan'
|
2
2
|
|
3
3
|
describe Automan::Cloudformation::Terminator do
|
4
|
-
it {
|
5
|
-
it {
|
6
|
-
it {
|
7
|
-
it {
|
8
|
-
it {
|
9
|
-
it {
|
4
|
+
it { is_expected.to respond_to :name }
|
5
|
+
it { is_expected.to respond_to :terminate }
|
6
|
+
it { is_expected.to respond_to :stack_exists? }
|
7
|
+
it { is_expected.to respond_to :stack_deleted? }
|
8
|
+
it { is_expected.to respond_to :delete_stack }
|
9
|
+
it { is_expected.to respond_to :wait_for_completion }
|
10
10
|
|
11
11
|
describe '#terminate' do
|
12
|
-
subject(
|
12
|
+
subject() do
|
13
13
|
AWS.stub!
|
14
14
|
t = Automan::Cloudformation::Terminator.new
|
15
15
|
t.logger = Logger.new('/dev/null')
|
@@ -17,20 +17,20 @@ describe Automan::Cloudformation::Terminator do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'should call #delete_stack if the stack exists' do
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
allow(subject).to receive(:stack_exists?).and_return(true)
|
21
|
+
expect(subject).to receive :delete_stack
|
22
|
+
subject.terminate
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should not call #delete_stack if the stack does not exist' do
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
allow(subject).to receive(:stack_exists?).and_return(false)
|
27
|
+
expect(subject).to_not receive :delete_stack
|
28
|
+
subject.terminate
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
describe '#stack_deleted?' do
|
33
|
-
subject(
|
33
|
+
subject() do
|
34
34
|
AWS.stub!
|
35
35
|
t = Automan::Cloudformation::Terminator.new
|
36
36
|
t.logger = Logger.new('/dev/null')
|
@@ -38,26 +38,26 @@ describe Automan::Cloudformation::Terminator do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'returns true if the stack does not exist' do
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
allow(subject).to receive(:stack_exists?).and_return(false)
|
42
|
+
allow(subject).to receive(:stack_status).and_raise StandardError
|
43
|
+
expect(subject.stack_deleted?('foo')).to be_truthy
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'returns true when status is DELETE_COMPLETE' do
|
47
|
-
|
48
|
-
|
47
|
+
allow(subject).to receive(:stack_status).and_return('DELETE_COMPLETE')
|
48
|
+
expect(subject.stack_deleted?('foo')).to be_truthy
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'raises an error when the delete fails' do
|
52
|
-
|
52
|
+
allow(subject).to receive(:stack_status).and_return('DELETE_FAILED')
|
53
53
|
expect {
|
54
|
-
|
54
|
+
subject.stack_deleted?('foo')
|
55
55
|
}.to raise_error Automan::Cloudformation::StackDeletionError
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'returns false for any other status' do
|
59
|
-
|
60
|
-
|
59
|
+
allow(subject).to receive(:stack_status).and_return('foo')
|
60
|
+
expect(subject.stack_deleted?('foo')).to be_falsey
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'automan'
|
2
2
|
|
3
3
|
describe Automan::Cloudformation::Uploader do
|
4
|
-
subject(
|
4
|
+
subject() do
|
5
5
|
AWS.stub!
|
6
6
|
u = Automan::Cloudformation::Uploader.new
|
7
7
|
u.logger = Logger.new('/dev/null')
|
8
|
-
u.
|
8
|
+
allow(u).to receive(:templates).and_return(%w[a b c])
|
9
9
|
u
|
10
10
|
end
|
11
11
|
|
@@ -16,35 +16,35 @@ describe Automan::Cloudformation::Uploader do
|
|
16
16
|
|
17
17
|
describe '#all_templates_valid?' do
|
18
18
|
it "raises error if there are no templates" do
|
19
|
-
|
19
|
+
allow(subject).to receive(:templates).and_return([])
|
20
20
|
expect {
|
21
|
-
|
21
|
+
subject.all_templates_valid?
|
22
22
|
}.to raise_error Automan::Cloudformation::NoTemplatesError
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'returns true if templates are valid' do
|
26
|
-
|
27
|
-
|
26
|
+
expect(subject).to receive(:template_valid?).exactly(3).times.and_return(true)
|
27
|
+
expect(subject.all_templates_valid?).to be_truthy
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'returns false if any templates are invalid' do
|
31
|
-
|
32
|
-
|
31
|
+
allow(subject).to receive(:template_valid?).and_return(false)
|
32
|
+
expect(subject.all_templates_valid?).to be_falsey
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
36
|
describe '#upload_templates' do
|
37
37
|
it 'raises error if any template fails validation' do
|
38
|
-
|
38
|
+
allow(subject).to receive(:all_templates_valid?).and_return(false)
|
39
39
|
expect {
|
40
|
-
|
40
|
+
subject.upload_templates
|
41
41
|
}.to raise_error(Automan::Cloudformation::InvalidTemplateError)
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'uploads files if all are valid' do
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
allow(subject).to receive(:all_templates_valid?).and_return(true)
|
46
|
+
expect(subject).to receive(:upload_file).exactly(3).times
|
47
|
+
subject.upload_templates
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|