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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +29 -0
  8. data/Rakefile +6 -0
  9. data/automan.gemspec +30 -0
  10. data/bin/baker +4 -0
  11. data/bin/mover +4 -0
  12. data/bin/scanner +4 -0
  13. data/bin/snapper +4 -0
  14. data/bin/stacker +4 -0
  15. data/bin/stalker +4 -0
  16. data/lib/automan.rb +27 -0
  17. data/lib/automan/base.rb +64 -0
  18. data/lib/automan/beanstalk/application.rb +74 -0
  19. data/lib/automan/beanstalk/configuration.rb +137 -0
  20. data/lib/automan/beanstalk/deployer.rb +193 -0
  21. data/lib/automan/beanstalk/errors.rb +22 -0
  22. data/lib/automan/beanstalk/package.rb +39 -0
  23. data/lib/automan/beanstalk/router.rb +102 -0
  24. data/lib/automan/beanstalk/terminator.rb +60 -0
  25. data/lib/automan/beanstalk/uploader.rb +58 -0
  26. data/lib/automan/beanstalk/version.rb +100 -0
  27. data/lib/automan/chef/uploader.rb +30 -0
  28. data/lib/automan/cli/baker.rb +63 -0
  29. data/lib/automan/cli/base.rb +14 -0
  30. data/lib/automan/cli/mover.rb +47 -0
  31. data/lib/automan/cli/scanner.rb +24 -0
  32. data/lib/automan/cli/snapper.rb +78 -0
  33. data/lib/automan/cli/stacker.rb +106 -0
  34. data/lib/automan/cli/stalker.rb +279 -0
  35. data/lib/automan/cloudformation/errors.rb +40 -0
  36. data/lib/automan/cloudformation/launcher.rb +196 -0
  37. data/lib/automan/cloudformation/replacer.rb +102 -0
  38. data/lib/automan/cloudformation/terminator.rb +61 -0
  39. data/lib/automan/cloudformation/uploader.rb +57 -0
  40. data/lib/automan/ec2/errors.rb +4 -0
  41. data/lib/automan/ec2/image.rb +137 -0
  42. data/lib/automan/ec2/instance.rb +83 -0
  43. data/lib/automan/mixins/aws_caller.rb +115 -0
  44. data/lib/automan/mixins/utils.rb +18 -0
  45. data/lib/automan/rds/errors.rb +7 -0
  46. data/lib/automan/rds/snapshot.rb +244 -0
  47. data/lib/automan/s3/downloader.rb +25 -0
  48. data/lib/automan/s3/uploader.rb +20 -0
  49. data/lib/automan/version.rb +3 -0
  50. data/lib/automan/wait_rescuer.rb +17 -0
  51. data/spec/beanstalk/application_spec.rb +49 -0
  52. data/spec/beanstalk/configuration_spec.rb +98 -0
  53. data/spec/beanstalk/deployer_spec.rb +162 -0
  54. data/spec/beanstalk/package_spec.rb +9 -0
  55. data/spec/beanstalk/router_spec.rb +65 -0
  56. data/spec/beanstalk/terminator_spec.rb +67 -0
  57. data/spec/beanstalk/uploader_spec.rb +53 -0
  58. data/spec/beanstalk/version_spec.rb +60 -0
  59. data/spec/chef/uploader_spec.rb +9 -0
  60. data/spec/cloudformation/launcher_spec.rb +240 -0
  61. data/spec/cloudformation/replacer_spec.rb +58 -0
  62. data/spec/cloudformation/templates/worker_role.json +337 -0
  63. data/spec/cloudformation/terminator_spec.rb +63 -0
  64. data/spec/cloudformation/uploader_spec.rb +50 -0
  65. data/spec/ec2/image_spec.rb +158 -0
  66. data/spec/ec2/instance_spec.rb +57 -0
  67. data/spec/mixins/aws_caller_spec.rb +39 -0
  68. data/spec/mixins/utils_spec.rb +44 -0
  69. data/spec/rds/snapshot_spec.rb +152 -0
  70. 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