automan 2.1.2
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 +7 -0
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +29 -0
- data/Rakefile +6 -0
- data/automan.gemspec +30 -0
- data/bin/baker +4 -0
- data/bin/mover +4 -0
- data/bin/scanner +4 -0
- data/bin/snapper +4 -0
- data/bin/stacker +4 -0
- data/bin/stalker +4 -0
- data/lib/automan.rb +27 -0
- data/lib/automan/base.rb +64 -0
- data/lib/automan/beanstalk/application.rb +74 -0
- data/lib/automan/beanstalk/configuration.rb +137 -0
- data/lib/automan/beanstalk/deployer.rb +193 -0
- data/lib/automan/beanstalk/errors.rb +22 -0
- data/lib/automan/beanstalk/package.rb +39 -0
- data/lib/automan/beanstalk/router.rb +102 -0
- data/lib/automan/beanstalk/terminator.rb +60 -0
- data/lib/automan/beanstalk/uploader.rb +58 -0
- data/lib/automan/beanstalk/version.rb +100 -0
- data/lib/automan/chef/uploader.rb +30 -0
- data/lib/automan/cli/baker.rb +63 -0
- data/lib/automan/cli/base.rb +14 -0
- data/lib/automan/cli/mover.rb +47 -0
- data/lib/automan/cli/scanner.rb +24 -0
- data/lib/automan/cli/snapper.rb +78 -0
- data/lib/automan/cli/stacker.rb +106 -0
- data/lib/automan/cli/stalker.rb +279 -0
- data/lib/automan/cloudformation/errors.rb +40 -0
- data/lib/automan/cloudformation/launcher.rb +196 -0
- data/lib/automan/cloudformation/replacer.rb +102 -0
- data/lib/automan/cloudformation/terminator.rb +61 -0
- data/lib/automan/cloudformation/uploader.rb +57 -0
- data/lib/automan/ec2/errors.rb +4 -0
- data/lib/automan/ec2/image.rb +137 -0
- data/lib/automan/ec2/instance.rb +83 -0
- data/lib/automan/mixins/aws_caller.rb +115 -0
- data/lib/automan/mixins/utils.rb +18 -0
- data/lib/automan/rds/errors.rb +7 -0
- data/lib/automan/rds/snapshot.rb +244 -0
- data/lib/automan/s3/downloader.rb +25 -0
- data/lib/automan/s3/uploader.rb +20 -0
- data/lib/automan/version.rb +3 -0
- data/lib/automan/wait_rescuer.rb +17 -0
- data/spec/beanstalk/application_spec.rb +49 -0
- data/spec/beanstalk/configuration_spec.rb +98 -0
- data/spec/beanstalk/deployer_spec.rb +162 -0
- data/spec/beanstalk/package_spec.rb +9 -0
- data/spec/beanstalk/router_spec.rb +65 -0
- data/spec/beanstalk/terminator_spec.rb +67 -0
- data/spec/beanstalk/uploader_spec.rb +53 -0
- data/spec/beanstalk/version_spec.rb +60 -0
- data/spec/chef/uploader_spec.rb +9 -0
- data/spec/cloudformation/launcher_spec.rb +240 -0
- data/spec/cloudformation/replacer_spec.rb +58 -0
- data/spec/cloudformation/templates/worker_role.json +337 -0
- data/spec/cloudformation/terminator_spec.rb +63 -0
- data/spec/cloudformation/uploader_spec.rb +50 -0
- data/spec/ec2/image_spec.rb +158 -0
- data/spec/ec2/instance_spec.rb +57 -0
- data/spec/mixins/aws_caller_spec.rb +39 -0
- data/spec/mixins/utils_spec.rb +44 -0
- data/spec/rds/snapshot_spec.rb +152 -0
- metadata +278 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'automan'
|
|
2
|
+
|
|
3
|
+
describe Automan::Cloudformation::Terminator do
|
|
4
|
+
it { should respond_to :name }
|
|
5
|
+
it { should respond_to :terminate }
|
|
6
|
+
it { should respond_to :stack_exists? }
|
|
7
|
+
it { should respond_to :stack_deleted? }
|
|
8
|
+
it { should respond_to :delete_stack }
|
|
9
|
+
it { should respond_to :wait_for_completion }
|
|
10
|
+
|
|
11
|
+
describe '#terminate' do
|
|
12
|
+
subject(:t) do
|
|
13
|
+
AWS.stub!
|
|
14
|
+
t = Automan::Cloudformation::Terminator.new
|
|
15
|
+
t.logger = Logger.new('/dev/null')
|
|
16
|
+
t
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should call #delete_stack if the stack exists' do
|
|
20
|
+
t.stub(:stack_exists?).and_return(true)
|
|
21
|
+
t.should_receive :delete_stack
|
|
22
|
+
t.terminate
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should not call #delete_stack if the stack does not exist' do
|
|
26
|
+
t.stub(:stack_exists?).and_return(false)
|
|
27
|
+
t.should_not_receive :delete_stack
|
|
28
|
+
t.terminate
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#stack_deleted?' do
|
|
33
|
+
subject(:t) do
|
|
34
|
+
AWS.stub!
|
|
35
|
+
t = Automan::Cloudformation::Terminator.new
|
|
36
|
+
t.logger = Logger.new('/dev/null')
|
|
37
|
+
t
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'returns true if the stack does not exist' do
|
|
41
|
+
t.stub(:stack_exists?).and_return(false)
|
|
42
|
+
t.stub(:stack_status).and_raise StandardError
|
|
43
|
+
t.stack_deleted?('foo').should be_true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'returns true when status is DELETE_COMPLETE' do
|
|
47
|
+
t.stub(:stack_status).and_return('DELETE_COMPLETE')
|
|
48
|
+
t.stack_deleted?('foo').should be_true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'raises an error when the delete fails' do
|
|
52
|
+
t.stub(:stack_status).and_return('DELETE_FAILED')
|
|
53
|
+
expect {
|
|
54
|
+
t.stack_deleted?('foo')
|
|
55
|
+
}.to raise_error Automan::Cloudformation::StackDeletionError
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'returns false for any other status' do
|
|
59
|
+
t.stub(:stack_status).and_return('foo')
|
|
60
|
+
t.stack_deleted?('foo').should be_false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'automan'
|
|
2
|
+
|
|
3
|
+
describe Automan::Cloudformation::Uploader do
|
|
4
|
+
subject(:u) do
|
|
5
|
+
AWS.stub!
|
|
6
|
+
u = Automan::Cloudformation::Uploader.new
|
|
7
|
+
u.logger = Logger.new('/dev/null')
|
|
8
|
+
u.stub(:templates).and_return(%w[a b c])
|
|
9
|
+
u
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it { should respond_to :template_files }
|
|
13
|
+
it { should respond_to :s3_path }
|
|
14
|
+
it { should respond_to :all_templates_valid? }
|
|
15
|
+
it { should respond_to :upload_templates }
|
|
16
|
+
|
|
17
|
+
describe '#all_templates_valid?' do
|
|
18
|
+
it "raises error if there are no templates" do
|
|
19
|
+
u.stub(:templates).and_return([])
|
|
20
|
+
expect {
|
|
21
|
+
u.all_templates_valid?
|
|
22
|
+
}.to raise_error Automan::Cloudformation::NoTemplatesError
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'returns true if templates are valid' do
|
|
26
|
+
u.should_receive(:template_valid?).exactly(3).times.and_return(true)
|
|
27
|
+
u.all_templates_valid?.should be_true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'returns false if any templates are invalid' do
|
|
31
|
+
u.stub(:template_valid?).and_return(false)
|
|
32
|
+
u.all_templates_valid?.should be_false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#upload_templates' do
|
|
37
|
+
it 'raises error if any template fails validation' do
|
|
38
|
+
u.stub(:all_templates_valid?).and_return(false)
|
|
39
|
+
expect {
|
|
40
|
+
u.upload_templates
|
|
41
|
+
}.to raise_error(Automan::Cloudformation::InvalidTemplateError)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'uploads files if all are valid' do
|
|
45
|
+
u.stub(:all_templates_valid?).and_return(true)
|
|
46
|
+
u.should_receive(:upload_file).exactly(3).times
|
|
47
|
+
u.upload_templates
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require 'automan'
|
|
2
|
+
require 'logger'
|
|
3
|
+
|
|
4
|
+
describe Automan::Ec2::Image do
|
|
5
|
+
it { should respond_to :ec2 }
|
|
6
|
+
it { should respond_to :create }
|
|
7
|
+
it { should respond_to :prune }
|
|
8
|
+
it { should respond_to :default_image_name }
|
|
9
|
+
it { should respond_to :image_snapshot_exists? }
|
|
10
|
+
it { should respond_to :image_snapshot }
|
|
11
|
+
it { should respond_to :delete_snapshots }
|
|
12
|
+
it { should respond_to :is_more_than_month_old? }
|
|
13
|
+
|
|
14
|
+
describe '#default_image_name' do
|
|
15
|
+
subject(:s) do
|
|
16
|
+
AWS.stub!
|
|
17
|
+
s = Automan::Ec2::Image.new
|
|
18
|
+
s.logger = Logger.new('/dev/null')
|
|
19
|
+
s.stub(:name).and_return('skee-lo')
|
|
20
|
+
s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "never returns nil" do
|
|
24
|
+
s.default_image_name().should_not be_nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "returns name dash iso8601 string" do
|
|
28
|
+
name = s.default_image_name()
|
|
29
|
+
name.should match /^skee-lo-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})[+-](\d{2})-(\d{2})/
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#image_snapshot' do
|
|
35
|
+
subject(:s) do
|
|
36
|
+
AWS.stub!
|
|
37
|
+
s = Automan::Ec2::Image.new
|
|
38
|
+
s.logger = Logger.new('/dev/null')
|
|
39
|
+
s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "returns id if the snapshot exists" do
|
|
43
|
+
fake_ami = double("ami")
|
|
44
|
+
fake_ami.stub(:block_devices) { [ { ebs: {snapshot_id: 'foo' } } ] }
|
|
45
|
+
s.image_snapshot(fake_ami).should eq('foo')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "returns nil if the snapshot is nil" do
|
|
49
|
+
fake_ami = double("ami")
|
|
50
|
+
fake_ami.stub(:block_devices) { [ { ebs: {snapshot_id: nil } } ] }
|
|
51
|
+
s.image_snapshot(fake_ami).should be_nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "returns nil if the ebs is nil" do
|
|
55
|
+
fake_ami = double("ami")
|
|
56
|
+
fake_ami.stub(:block_devices) { [ { ebs: nil } ] }
|
|
57
|
+
s.image_snapshot(fake_ami).should be_nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "returns nil if the device is nil" do
|
|
61
|
+
fake_ami = double("ami")
|
|
62
|
+
fake_ami.stub(:block_devices) { [ nil ] }
|
|
63
|
+
s.image_snapshot(fake_ami).should be_nil
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#delete_snapshots' do
|
|
68
|
+
subject(:s) do
|
|
69
|
+
AWS.stub!
|
|
70
|
+
s = Automan::Ec2::Image.new
|
|
71
|
+
s.logger = Logger.new('/dev/null')
|
|
72
|
+
s
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'does not delete snapshots with status != :completed' do
|
|
76
|
+
snapshot = double(:snapshot)
|
|
77
|
+
snapshot.stub(:status).and_return(:foo)
|
|
78
|
+
snapshot.stub(:id)
|
|
79
|
+
snapshot.should_not_receive(:delete)
|
|
80
|
+
s.delete_snapshots( [ snapshot ])
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'deletes snapshots with status == :completed' do
|
|
84
|
+
snapshot = double(:snapshot)
|
|
85
|
+
snapshot.stub(:status).and_return(:completed)
|
|
86
|
+
snapshot.stub(:id)
|
|
87
|
+
snapshot.should_receive(:delete)
|
|
88
|
+
s.delete_snapshots( [ snapshot ])
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe '#deregister_images' do
|
|
93
|
+
subject(:s) do
|
|
94
|
+
AWS.stub!
|
|
95
|
+
s = Automan::Ec2::Image.new
|
|
96
|
+
s.logger = Logger.new('/dev/null')
|
|
97
|
+
s.stub(:image_snapshot).and_return('foo')
|
|
98
|
+
s
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
before(:each) do
|
|
102
|
+
@image = double(:image)
|
|
103
|
+
@image.stub(:id)
|
|
104
|
+
s.stub(:my_images).and_return( [ @image ] )
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'does not deregister images with state != :available' do
|
|
108
|
+
@image.stub(:state).and_return(:foo)
|
|
109
|
+
@image.stub(:tags).and_return( {'CanPrune' => 'yes'} )
|
|
110
|
+
@image.should_not_receive(:delete)
|
|
111
|
+
s.deregister_images( [ 'foo' ] )
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'does not deregister images w/o prunable tag' do
|
|
115
|
+
@image.stub(:state).and_return(:available)
|
|
116
|
+
@image.stub(:tags).and_return( Hash.new )
|
|
117
|
+
@image.should_not_receive(:delete)
|
|
118
|
+
s.deregister_images( [ 'foo' ] )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'does not deregister images whose snapshots are not in the list' do
|
|
122
|
+
@image.stub(:state).and_return(:available)
|
|
123
|
+
@image.stub(:tags).and_return( {'CanPrune' => 'yes'} )
|
|
124
|
+
@image.should_not_receive(:delete)
|
|
125
|
+
s.deregister_images( [ 'bar' ] )
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'deletes available, prunable images on the list' do
|
|
129
|
+
@image.stub(:state).and_return(:available)
|
|
130
|
+
@image.stub(:tags).and_return( {'CanPrune' => 'yes'} )
|
|
131
|
+
@image.should_receive(:delete)
|
|
132
|
+
s.deregister_images( [ 'foo' ] )
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe '#is_more_than_month_old?' do
|
|
137
|
+
subject(:s) do
|
|
138
|
+
AWS.stub!
|
|
139
|
+
s = Automan::Ec2::Image.new
|
|
140
|
+
s.logger = Logger.new('/dev/null')
|
|
141
|
+
s
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'returns false if argument is not a Time object' do
|
|
145
|
+
s.is_more_than_month_old?(Object.new).should be_false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'returns true if time is more than 30 days in the past' do
|
|
149
|
+
days_of_yore = Time.now - (60*60*24*30)
|
|
150
|
+
s.is_more_than_month_old?(days_of_yore).should be_true
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'returns false if time is in the last 30 days' do
|
|
154
|
+
just_yesterday = Time.now - (60*60*24)
|
|
155
|
+
s.is_more_than_month_old?(just_yesterday).should be_false
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require "automan"
|
|
2
|
+
|
|
3
|
+
describe Automan::Ec2::Instance do
|
|
4
|
+
|
|
5
|
+
it { should respond_to :windows_password }
|
|
6
|
+
it { should respond_to :password_data }
|
|
7
|
+
it { should respond_to :decrypt_password }
|
|
8
|
+
it { should respond_to :windows_name }
|
|
9
|
+
it { should respond_to :show_env }
|
|
10
|
+
|
|
11
|
+
describe '#windows_password' do
|
|
12
|
+
subject(:i) do
|
|
13
|
+
AWS.stub!
|
|
14
|
+
i = Automan::Ec2::Instance.new
|
|
15
|
+
i.logger = Logger.new('/dev/null')
|
|
16
|
+
i
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "returns nil if password could not be found" do
|
|
20
|
+
i.stub(:password_data).and_return nil
|
|
21
|
+
i.windows_password('foo','bar').should be_nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "returns nil if the aws request fails" do
|
|
25
|
+
i.stub(:password_data).and_raise Automan::Ec2::RequestFailedError
|
|
26
|
+
i.windows_password('foo','bar').should be_nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "returns nil if the decrypt fails" do
|
|
30
|
+
i.stub(:password_data).and_return 'foo'
|
|
31
|
+
i.stub(:decrypt_password).and_return nil
|
|
32
|
+
i.windows_password('foo','bar').should be_nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#windows_name' do
|
|
37
|
+
subject(:i) do
|
|
38
|
+
AWS.stub!
|
|
39
|
+
i = Automan::Ec2::Instance.new
|
|
40
|
+
i.logger = Logger.new('/dev/null')
|
|
41
|
+
i
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "returns nil if ip_address is nil" do
|
|
45
|
+
i.windows_name(nil).should be_nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "returns nil if ip_address is empty" do
|
|
49
|
+
i.windows_name("").should be_nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "converts ip_address to ip-xxxxxxxx" do
|
|
53
|
+
i.windows_name('54.56.78.90').should eq 'ip-36384e5a'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'automan/mixins/aws_caller'
|
|
2
|
+
|
|
3
|
+
describe Automan::Mixins::AwsCaller do
|
|
4
|
+
let(:s) { (Class.new { include Automan::Mixins::AwsCaller }).new }
|
|
5
|
+
|
|
6
|
+
services = [
|
|
7
|
+
:s3, :cfn, :as, :eb, :r53, :elb, :rds, :ec2
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
services.each do |svc|
|
|
11
|
+
it { s.should respond_to svc }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it { s.should respond_to :parse_s3_path }
|
|
15
|
+
|
|
16
|
+
describe '#parse_s3_path' do
|
|
17
|
+
it "parses s3 path into bucket and key" do
|
|
18
|
+
bucket, key = s.parse_s3_path('s3://foo/bar/baz')
|
|
19
|
+
bucket.should eq('foo')
|
|
20
|
+
key.should eq ('bar/baz')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "raises error if it doesn't start with s3://" do
|
|
24
|
+
expect {
|
|
25
|
+
s.parse_s3_path 'foo'
|
|
26
|
+
}.to raise_error ArgumentError
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe '#looks_like_s3_path?' do
|
|
31
|
+
it 'returns true if it begins with "s3://"' do
|
|
32
|
+
s.looks_like_s3_path?('s3://foo').should be_true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'returns false if it does not start with "s3://"' do
|
|
36
|
+
s.looks_like_s3_path?('foobar').should be_false
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'automan/mixins/utils'
|
|
2
|
+
|
|
3
|
+
describe Automan::Mixins::Utils do
|
|
4
|
+
let(:s) { (Class.new { include Automan::Mixins::Utils }).new }
|
|
5
|
+
|
|
6
|
+
it "adds String#underscore" do
|
|
7
|
+
s = String.new
|
|
8
|
+
s.should respond_to :underscore
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "String#underscore" do
|
|
12
|
+
it "underscore's like a boss" do
|
|
13
|
+
"Namespace".underscore.should eq "namespace"
|
|
14
|
+
"OptionName".underscore.should eq "option_name"
|
|
15
|
+
"Value".underscore.should eq "value"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it { s.should respond_to :region_from_az }
|
|
20
|
+
|
|
21
|
+
it "returns az properly" do
|
|
22
|
+
s.region_from_az("us-east-1a").should eq 'us-east-1'
|
|
23
|
+
s.region_from_az("us-east-1b").should eq 'us-east-1'
|
|
24
|
+
s.region_from_az("us-east-1c").should eq 'us-east-1'
|
|
25
|
+
s.region_from_az("us-east-1d").should eq 'us-east-1'
|
|
26
|
+
s.region_from_az("ap-northeast-1a").should eq 'ap-northeast-1'
|
|
27
|
+
s.region_from_az("ap-northeast-1b").should eq 'ap-northeast-1'
|
|
28
|
+
s.region_from_az("ap-northeast-1c").should eq 'ap-northeast-1'
|
|
29
|
+
s.region_from_az("sa-east-1a").should eq 'sa-east-1'
|
|
30
|
+
s.region_from_az("sa-east-1b").should eq 'sa-east-1'
|
|
31
|
+
s.region_from_az("ap-southeast-1a").should eq 'ap-southeast-1'
|
|
32
|
+
s.region_from_az("ap-southeast-1b").should eq 'ap-southeast-1'
|
|
33
|
+
s.region_from_az("ap-southeast-2a").should eq 'ap-southeast-2'
|
|
34
|
+
s.region_from_az("ap-southeast-2b").should eq 'ap-southeast-2'
|
|
35
|
+
s.region_from_az("us-west-2a").should eq 'us-west-2'
|
|
36
|
+
s.region_from_az("us-west-2b").should eq 'us-west-2'
|
|
37
|
+
s.region_from_az("us-west-2c").should eq 'us-west-2'
|
|
38
|
+
s.region_from_az("us-west-1a").should eq 'us-west-1'
|
|
39
|
+
s.region_from_az("us-west-1c").should eq 'us-west-1'
|
|
40
|
+
s.region_from_az("eu-west-1a").should eq 'eu-west-1'
|
|
41
|
+
s.region_from_az("eu-west-1b").should eq 'eu-west-1'
|
|
42
|
+
s.region_from_az("eu-west-1c").should eq 'eu-west-1'
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require 'automan'
|
|
2
|
+
require 'logger'
|
|
3
|
+
|
|
4
|
+
ENV['MAX_SNAPSHOTS'] = "50"
|
|
5
|
+
|
|
6
|
+
describe Automan::RDS::Snapshot do
|
|
7
|
+
it { should respond_to :rds }
|
|
8
|
+
it { should respond_to :create }
|
|
9
|
+
it { should respond_to :delete }
|
|
10
|
+
it { should respond_to :prune }
|
|
11
|
+
it { should respond_to :latest }
|
|
12
|
+
it { should respond_to :default_snapshot_name }
|
|
13
|
+
|
|
14
|
+
describe '#default_snapshot_name' do
|
|
15
|
+
subject(:s) do
|
|
16
|
+
AWS.stub!
|
|
17
|
+
s = Automan::RDS::Snapshot.new
|
|
18
|
+
s.logger = Logger.new('/dev/null')
|
|
19
|
+
s.stub(:db_environment).and_return('dev1')
|
|
20
|
+
s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "never returns nil" do
|
|
24
|
+
s.default_snapshot_name('somedb').should_not be_nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "returns environment dash iso8601 string" do
|
|
28
|
+
name = s.default_snapshot_name('somedb')
|
|
29
|
+
name.should match /^dev1-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})[+-](\d{2})-(\d{2})/
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#create' do
|
|
35
|
+
subject(:s) do
|
|
36
|
+
AWS.stub!
|
|
37
|
+
s = Automan::RDS::Snapshot.new
|
|
38
|
+
s.logger = Logger.new('/dev/null')
|
|
39
|
+
s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "raises error if could not find database" do
|
|
43
|
+
s.stub(:find_db).and_return(nil)
|
|
44
|
+
expect {
|
|
45
|
+
s.create
|
|
46
|
+
}.to raise_error Automan::RDS::DatabaseDoesNotExistError
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "raises error if RDS says database does not exist" do
|
|
50
|
+
db = double(:db)
|
|
51
|
+
db.stub(:exists?).and_return(false)
|
|
52
|
+
s.stub(:find_db).and_return(db)
|
|
53
|
+
|
|
54
|
+
expect {
|
|
55
|
+
s.create
|
|
56
|
+
}.to raise_error Automan::RDS::DatabaseDoesNotExistError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe '#tagged_can_prune?' do
|
|
62
|
+
subject(:s) do
|
|
63
|
+
AWS.stub!
|
|
64
|
+
s = Automan::RDS::Snapshot.new
|
|
65
|
+
s.logger = Logger.new('/dev/null')
|
|
66
|
+
s.stub(:snapshot_arn)
|
|
67
|
+
s
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'returns true if snapshot is tagged with CanPrune=yes' do
|
|
71
|
+
s.stub(:tags).and_return( {'CanPrune' => 'yes'} )
|
|
72
|
+
s.tagged_can_prune?( double() ).should be_true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'returns false if snapshot is missing CanPrune tag' do
|
|
76
|
+
s.stub(:tags).and_return( {} )
|
|
77
|
+
s.tagged_can_prune?( double() ).should be_false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'returns false if snapshot is tagged with CanPrune=nil' do
|
|
81
|
+
s.stub(:tags).and_return( {'CanPrune' => nil} )
|
|
82
|
+
s.tagged_can_prune?( double() ).should be_false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'returns false if snapshot is tagged with CanPrune=foo' do
|
|
86
|
+
s.stub(:tags).and_return( {'CanPrune' => 'foo'} )
|
|
87
|
+
s.tagged_can_prune?( double() ).should be_false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#available?' do
|
|
92
|
+
subject(:s) do
|
|
93
|
+
AWS.stub!
|
|
94
|
+
s = Automan::RDS::Snapshot.new
|
|
95
|
+
s.logger = Logger.new('/dev/null')
|
|
96
|
+
s
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'returns true if status is "available"' do
|
|
100
|
+
snap = double(:snap)
|
|
101
|
+
snap.stub(:status).and_return('available')
|
|
102
|
+
s.available?(snap).should be_true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'returns false if status is foo' do
|
|
106
|
+
snap = double(:snap)
|
|
107
|
+
snap.stub(:status).and_return('foo')
|
|
108
|
+
s.available?(snap).should be_false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe '#manual?' do
|
|
113
|
+
let(:snap) { double }
|
|
114
|
+
subject(:s) do
|
|
115
|
+
AWS.stub!
|
|
116
|
+
s = Automan::RDS::Snapshot.new
|
|
117
|
+
s.logger = Logger.new('/dev/null')
|
|
118
|
+
s
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'returns true if type is "manual"' do
|
|
122
|
+
snap.stub(:snapshot_type).and_return('manual')
|
|
123
|
+
s.manual?(snap).should be_true
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'returns false if type is foo' do
|
|
127
|
+
snap.stub(:snapshot_type).and_return('foo')
|
|
128
|
+
s.manual?(snap).should be_false
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
describe '#prunable_snapshots' do
|
|
133
|
+
let(:snap) { double }
|
|
134
|
+
subject(:s) do
|
|
135
|
+
AWS.stub!
|
|
136
|
+
s = Automan::RDS::Snapshot.new
|
|
137
|
+
s.logger = Logger.new('/dev/null')
|
|
138
|
+
s.stub(:get_all_snapshots).and_return( [ snap ] )
|
|
139
|
+
s
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'includes snapshots which can be pruned' do
|
|
143
|
+
s.stub(:can_prune?).and_return(true)
|
|
144
|
+
s.prunable_snapshots.should include(snap)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'excludes snapshots which should not be pruned' do
|
|
148
|
+
s.stub(:can_prune?).and_return(false)
|
|
149
|
+
s.prunable_snapshots.should_not include(snap)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|