rudy 0.4.0 → 0.6.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.
Files changed (135) hide show
  1. data/CHANGES.txt +54 -30
  2. data/README.rdoc +100 -12
  3. data/Rakefile +103 -8
  4. data/Rudyfile +119 -0
  5. data/bin/ird +175 -0
  6. data/bin/rudy +259 -156
  7. data/bin/rudy-ec2 +228 -95
  8. data/bin/rudy-s3 +76 -0
  9. data/bin/rudy-sdb +67 -0
  10. data/lib/annoy.rb +270 -0
  11. data/lib/console.rb +30 -9
  12. data/lib/escape.rb +305 -0
  13. data/lib/rudy.rb +151 -182
  14. data/lib/rudy/aws.rb +56 -49
  15. data/lib/rudy/aws/ec2.rb +47 -292
  16. data/lib/rudy/aws/ec2/address.rb +157 -0
  17. data/lib/rudy/aws/ec2/group.rb +301 -0
  18. data/lib/rudy/aws/ec2/image.rb +168 -0
  19. data/lib/rudy/aws/ec2/instance.rb +434 -0
  20. data/lib/rudy/aws/ec2/keypair.rb +104 -0
  21. data/lib/rudy/aws/ec2/snapshot.rb +98 -0
  22. data/lib/rudy/aws/ec2/volume.rb +230 -0
  23. data/lib/rudy/aws/ec2/zone.rb +77 -0
  24. data/lib/rudy/aws/s3.rb +54 -0
  25. data/lib/rudy/aws/sdb.rb +298 -0
  26. data/lib/rudy/aws/sdb/error.rb +46 -0
  27. data/lib/rudy/{metadata/backup.rb → backup.rb} +26 -51
  28. data/lib/rudy/cli.rb +157 -0
  29. data/lib/rudy/cli/aws/ec2/addresses.rb +105 -0
  30. data/lib/rudy/cli/aws/ec2/candy.rb +208 -0
  31. data/lib/rudy/cli/aws/ec2/groups.rb +121 -0
  32. data/lib/rudy/cli/aws/ec2/images.rb +196 -0
  33. data/lib/rudy/cli/aws/ec2/instances.rb +194 -0
  34. data/lib/rudy/cli/aws/ec2/keypairs.rb +53 -0
  35. data/lib/rudy/cli/aws/ec2/snapshots.rb +49 -0
  36. data/lib/rudy/cli/aws/ec2/volumes.rb +104 -0
  37. data/lib/rudy/cli/aws/ec2/zones.rb +22 -0
  38. data/lib/rudy/cli/aws/s3/buckets.rb +50 -0
  39. data/lib/rudy/cli/aws/s3/store.rb +22 -0
  40. data/lib/rudy/cli/aws/sdb/domains.rb +41 -0
  41. data/lib/rudy/cli/candy.rb +8 -0
  42. data/lib/rudy/{command → cli}/config.rb +34 -24
  43. data/lib/rudy/cli/disks.rb +35 -0
  44. data/lib/rudy/cli/machines.rb +94 -0
  45. data/lib/rudy/cli/routines.rb +57 -0
  46. data/lib/rudy/config.rb +77 -72
  47. data/lib/rudy/config/objects.rb +29 -0
  48. data/lib/rudy/disks.rb +248 -0
  49. data/lib/rudy/global.rb +121 -0
  50. data/lib/rudy/huxtable.rb +340 -0
  51. data/lib/rudy/machines.rb +245 -0
  52. data/lib/rudy/metadata.rb +123 -13
  53. data/lib/rudy/routines.rb +47 -0
  54. data/lib/rudy/routines/helpers/diskhelper.rb +101 -0
  55. data/lib/rudy/routines/helpers/scripthelper.rb +91 -0
  56. data/lib/rudy/routines/release.rb +34 -0
  57. data/lib/rudy/routines/shutdown.rb +57 -0
  58. data/lib/rudy/routines/startup.rb +58 -0
  59. data/lib/rudy/scm/svn.rb +1 -1
  60. data/lib/rudy/utils.rb +322 -4
  61. data/lib/storable.rb +26 -17
  62. data/lib/sysinfo.rb +274 -0
  63. data/lib/tryouts.rb +6 -13
  64. data/rudy.gemspec +128 -42
  65. data/support/randomize-root-password +45 -0
  66. data/support/rudy-ec2-startup +9 -9
  67. data/support/update-ec2-ami-tools +20 -0
  68. data/test/05_config/00_setup_test.rb +20 -0
  69. data/test/05_config/30_machines_test.rb +69 -0
  70. data/test/20_sdb/00_setup_test.rb +16 -0
  71. data/test/20_sdb/10_domains_test.rb +115 -0
  72. data/test/25_ec2/00_setup_test.rb +29 -0
  73. data/test/25_ec2/10_keypairs_test.rb +41 -0
  74. data/test/25_ec2/20_groups_test.rb +131 -0
  75. data/test/25_ec2/30_addresses_test.rb +38 -0
  76. data/test/25_ec2/40_volumes_test.rb +49 -0
  77. data/test/25_ec2/50_snapshots_test.rb +74 -0
  78. data/test/26_ec2_instances/00_setup_test.rb +28 -0
  79. data/test/26_ec2_instances/10_instances_test.rb +83 -0
  80. data/test/26_ec2_instances/50_images_test.rb +13 -0
  81. data/test/30_sdb_metadata/00_setup_test.rb +21 -0
  82. data/test/30_sdb_metadata/10_disks_test.rb +109 -0
  83. data/test/30_sdb_metadata/20_backups_test.rb +102 -0
  84. data/test/coverage.txt +51 -0
  85. data/test/helper.rb +36 -0
  86. data/vendor/highline-1.5.1/CHANGELOG +222 -0
  87. data/vendor/highline-1.5.1/INSTALL +35 -0
  88. data/vendor/highline-1.5.1/LICENSE +7 -0
  89. data/vendor/highline-1.5.1/README +63 -0
  90. data/vendor/highline-1.5.1/Rakefile +82 -0
  91. data/vendor/highline-1.5.1/TODO +6 -0
  92. data/vendor/highline-1.5.1/examples/ansi_colors.rb +38 -0
  93. data/vendor/highline-1.5.1/examples/asking_for_arrays.rb +18 -0
  94. data/vendor/highline-1.5.1/examples/basic_usage.rb +75 -0
  95. data/vendor/highline-1.5.1/examples/color_scheme.rb +32 -0
  96. data/vendor/highline-1.5.1/examples/limit.rb +12 -0
  97. data/vendor/highline-1.5.1/examples/menus.rb +65 -0
  98. data/vendor/highline-1.5.1/examples/overwrite.rb +19 -0
  99. data/vendor/highline-1.5.1/examples/page_and_wrap.rb +322 -0
  100. data/vendor/highline-1.5.1/examples/password.rb +7 -0
  101. data/vendor/highline-1.5.1/examples/trapping_eof.rb +22 -0
  102. data/vendor/highline-1.5.1/examples/using_readline.rb +17 -0
  103. data/vendor/highline-1.5.1/lib/highline.rb +758 -0
  104. data/vendor/highline-1.5.1/lib/highline/color_scheme.rb +120 -0
  105. data/vendor/highline-1.5.1/lib/highline/compatibility.rb +17 -0
  106. data/vendor/highline-1.5.1/lib/highline/import.rb +43 -0
  107. data/vendor/highline-1.5.1/lib/highline/menu.rb +395 -0
  108. data/vendor/highline-1.5.1/lib/highline/question.rb +463 -0
  109. data/vendor/highline-1.5.1/lib/highline/system_extensions.rb +193 -0
  110. data/vendor/highline-1.5.1/setup.rb +1360 -0
  111. data/vendor/highline-1.5.1/test/tc_color_scheme.rb +56 -0
  112. data/vendor/highline-1.5.1/test/tc_highline.rb +823 -0
  113. data/vendor/highline-1.5.1/test/tc_import.rb +54 -0
  114. data/vendor/highline-1.5.1/test/tc_menu.rb +429 -0
  115. data/vendor/highline-1.5.1/test/ts_all.rb +15 -0
  116. metadata +141 -38
  117. data/lib/aws_sdb.rb +0 -3
  118. data/lib/aws_sdb/error.rb +0 -42
  119. data/lib/aws_sdb/service.rb +0 -215
  120. data/lib/rudy/aws/simpledb.rb +0 -53
  121. data/lib/rudy/command/addresses.rb +0 -46
  122. data/lib/rudy/command/backups.rb +0 -175
  123. data/lib/rudy/command/base.rb +0 -841
  124. data/lib/rudy/command/deploy.rb +0 -12
  125. data/lib/rudy/command/disks.rb +0 -213
  126. data/lib/rudy/command/environment.rb +0 -73
  127. data/lib/rudy/command/groups.rb +0 -61
  128. data/lib/rudy/command/images.rb +0 -91
  129. data/lib/rudy/command/instances.rb +0 -85
  130. data/lib/rudy/command/machines.rb +0 -161
  131. data/lib/rudy/command/metadata.rb +0 -41
  132. data/lib/rudy/command/release.rb +0 -174
  133. data/lib/rudy/command/volumes.rb +0 -66
  134. data/lib/rudy/metadata/disk.rb +0 -138
  135. data/tryouts/console_tryout.rb +0 -91
@@ -0,0 +1,98 @@
1
+
2
+
3
+ module Rudy::AWS
4
+ module EC2
5
+ class Snapshot < Storable
6
+ @@sformat = "%s <- %10s; %s"
7
+
8
+ field :awsid
9
+ field :progress
10
+ field :created
11
+ field :volid
12
+ field :status
13
+
14
+ def liner_note
15
+ t = Time.parse(@created)
16
+ info = t.strftime("%Y-%m-%d %H:%M:%S")
17
+ "%s (%s)" % [(self.awsid || '').bright, info]
18
+ end
19
+
20
+ def to_s(with_title=false)
21
+ @@sformat % [liner_note, @volid, @status]
22
+ end
23
+
24
+ def completed?
25
+ self.status && self.status == 'completed'
26
+ end
27
+
28
+ end
29
+
30
+ class Snapshots
31
+ include Rudy::AWS::ObjectBase
32
+ include Rudy::AWS::EC2::Base
33
+
34
+ def list(snap_id=[])
35
+ snapshots = list_as_hash(snap_id)
36
+ if snapshots
37
+ snapshots = snapshots.values.sort { |a,b| a.created <=> b.created }
38
+ end
39
+ snapshots
40
+ end
41
+ def list_as_hash(snap_id=[])
42
+ snap_id = [snap_id].flatten.compact
43
+ slist = @ec2.describe_snapshots(:snapshot_id => snap_id)
44
+ return unless slist['snapshotSet'].is_a?(Hash)
45
+ snapshots = {}
46
+ slist['snapshotSet']['item'].each do |snap|
47
+ kp = self.class.from_hash(snap)
48
+ snapshots[kp.awsid] = kp
49
+ end
50
+ snapshots
51
+ end
52
+
53
+ def create(vol_id)
54
+ vol_id = (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
55
+ shash = @ec2.create_snapshot(:volume_id => vol_id)
56
+ snap = Snapshots.from_hash(shash)
57
+ snap
58
+ end
59
+
60
+ def destroy(snap_id)
61
+ ret = @ec2.delete_snapshot(:snapshot_id => snap_id)
62
+ (ret && ret['return'] == 'true')
63
+ end
64
+
65
+
66
+ def Snapshots.from_hash(h)
67
+ #snapshotSet:
68
+ # item:
69
+ # - snapshotId: snap-5493653d
70
+ # volumeId: vol-0836d761
71
+ # status: completed
72
+ # startTime: "2009-03-29T20:15:10.000Z"
73
+ # progress: 100%
74
+ vol = Rudy::AWS::EC2::Snapshot.new
75
+ vol.awsid = h['snapshotId']
76
+ vol.volid = h['volumeId']
77
+ vol.created = h['startTime']
78
+ vol.progress = h['progress']
79
+ vol.status = h['status']
80
+ vol
81
+ end
82
+
83
+ def any?
84
+ !(list_as_hash || {}).empty?
85
+ end
86
+
87
+ def get(snap_id)
88
+ list(snap_id).first || nil
89
+ end
90
+
91
+ def exists?(id)
92
+ !get(snap_id).nil?
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,230 @@
1
+
2
+
3
+ module Rudy::AWS
4
+ module EC2
5
+ class Volume < Storable
6
+ @@sformat = "%s %10s;%4sGB; %s " # cram the terabyte
7
+
8
+ field :awsid
9
+ field :status
10
+ field :size
11
+ field :snapid
12
+ field :zone
13
+ field :create_time
14
+ field :attach_time
15
+ field :instid
16
+ field :device
17
+
18
+ def liner_note
19
+ info = attached? ? "attached to #{@instid}" : @status
20
+ "%s (%s)" % [(self.awsid || '').bright, info]
21
+ end
22
+
23
+ def to_s(with_title=false)
24
+ line = @@sformat % [liner_note, @zone, @size, @device]
25
+ line << " <- %s" % [@snapid] if @snapid
26
+ line
27
+ end
28
+
29
+ def inspect
30
+ lines = [liner_note]
31
+ field_names.each do |n|
32
+ lines << sprintf(" %12s: %s", n, self.send(n)) if self.send(n)
33
+ end
34
+ lines.join($/)
35
+ end
36
+
37
+ # Alias for status
38
+ def state
39
+ status
40
+ end
41
+
42
+ def available?; (status && status == "available"); end
43
+ def creating?; (status && status == "creating"); end
44
+ def deleting?; (status && status == "deleting"); end
45
+ def attached?; (status && status == "attached"); end
46
+ def in_use?; (status && status == "in-use"); end
47
+
48
+ end
49
+
50
+
51
+ class Volumes
52
+ include Rudy::AWS::ObjectBase
53
+ include Rudy::AWS::EC2::Base
54
+
55
+ unless defined?(KNOWN_STATES)
56
+ KNOWN_STATES = [:available, :creating, :deleting, :attached, :detaching].freeze
57
+ end
58
+
59
+ # * +size+ the number of GB
60
+ def create(size, zone, snapid=nil)
61
+ opts = {
62
+ :availability_zone => zone.to_s,
63
+ :size => (size || 1).to_s
64
+ }
65
+
66
+ opts[:snapshot_id] = snapid if snapid
67
+
68
+ # "status"=>"creating",
69
+ # "size"=>"1",
70
+ # "snapshotId"=>nil,
71
+ # "requestId"=>"d42ff744-48b5-4f47-a3f0-7aba57a13eb9",
72
+ # "availabilityZone"=>"us-east-1b",
73
+ # "createTime"=>"2009-03-17T20:10:48.000Z",
74
+ # "volumeId"=>"vol-48826421"
75
+ vol = execute_request({}) { @ec2.create_volume(opts) }
76
+
77
+ # TODO: use a waiter?
78
+ #Rudy.waiter(1, 30) do
79
+ # ret = @@ec2.volumes.available?(volume.awsid)
80
+ #end
81
+
82
+ reqid = vol['requestId']
83
+ Volumes.from_hash(vol) || nil
84
+ end
85
+
86
+ def destroy(vol_id)
87
+ vol_id = Volumes.get_vol_id(vol_id)
88
+ raise VolumeNotAvailable, vol_id unless available?(vol_id)
89
+ ret = execute_request({}) { @ec2.delete_volume(:volume_id => vol_id) }
90
+ (ret['return'] == 'true')
91
+ end
92
+
93
+ def attach(vol_id, inst_id, device)
94
+ vol_id = Volumes.get_vol_id(vol_id)
95
+ inst_id = inst_id.is_a?(Rudy::AWS::EC2::Instance) ? inst_id.awsid : inst_id
96
+ raise NoVolumeID unless vol_id
97
+ raise VolumeAlreadyAttached, vol_id if attached?(vol_id)
98
+ raise NoInstanceID unless inst_id
99
+ raise NoDevice unless device
100
+
101
+ opts = {
102
+ :volume_id => vol_id,
103
+ :instance_id => inst_id,
104
+ :device => device
105
+ }
106
+ ret = execute_request(false) { @ec2.attach_volume(opts) }
107
+ (ret['status'] == 'attaching')
108
+ end
109
+
110
+ def detach(vol_id)
111
+ vol_id = Volumes.get_vol_id(vol_id)
112
+ raise NoVolumeID unless vol_id
113
+ raise VolumeNotAttached, vol_id unless attached?(vol_id)
114
+ ret = execute_request({}) { @ec2.detach_volume(:volume_id => vol_id) }
115
+ (ret['status'] == 'detaching')
116
+ end
117
+
118
+
119
+ def list(state=nil, vol_id=[])
120
+ list_as_hash(state, vol_id).values
121
+ end
122
+
123
+ def list_as_hash(state=nil, vol_id=[])
124
+ state &&= state.to_sym
125
+ state = nil if state == :any
126
+ # A nil state is fine, but we don't want an unknown one!
127
+ raise UnknownState, state if state && !Volumes.known_state?(state)
128
+
129
+ opts = {
130
+ :volume_id => vol_id ? [vol_id].flatten : []
131
+ }
132
+
133
+ vlist = execute_request({}) { @ec2.describe_volumes(opts) }
134
+
135
+ volumes = {}
136
+ return volumes unless vlist['volumeSet'].is_a?(Hash)
137
+ vlist['volumeSet']['item'].each do |vol|
138
+ v = Volumes.from_hash(vol)
139
+ next if state && v.state != state.to_s
140
+ volumes[v.awsid] = v
141
+ end
142
+ volumes
143
+ end
144
+
145
+ def any?(state=nil,vol_id=[])
146
+ !list(state, vol_id).nil?
147
+ end
148
+
149
+ def exists?(vol_id)
150
+ vol_id = Volumes.get_vol_id(vol_id)
151
+ vol = get(vol_id)
152
+ !vol.nil?
153
+ end
154
+
155
+ def get(vol_id)
156
+ vol_id = Volumes.get_vol_id(vol_id)
157
+ list(:any, vol_id).first || nil
158
+ end
159
+
160
+ # deleting?, available?, etc...
161
+ %w[deleting available attached in-use].each do |state|
162
+ define_method("#{state.tr('-', '_')}?") do |vol_id|
163
+ vol_id = Volumes.get_vol_id(vol_id)
164
+ return false unless vol_id
165
+ vol = get(vol_id)
166
+ (vol && vol.status == state)
167
+ end
168
+ end
169
+
170
+ # Creates a Rudy::AWS::EC2::Volume object from:
171
+ #
172
+ # volumeSet:
173
+ # item:
174
+ # - status: available
175
+ # size: "1"
176
+ # snapshotId:
177
+ # availabilityZone: us-east-1b
178
+ # attachmentSet:
179
+ # createTime: "2009-03-17T20:10:48.000Z"
180
+ # volumeId: vol-48826421
181
+ # attachmentSet:
182
+ # item:
183
+ # - attachTime: "2009-03-17T21:49:54.000Z"
184
+ # status: attached
185
+ # device: /dev/sdh
186
+ # instanceId: i-956af3fc
187
+ # volumeId: vol-48826421
188
+ #
189
+ # requestId: 8fc30e5b-a9c3-4fe0-a979-0f71e639a7c7
190
+ #
191
+ def self.from_hash(h)
192
+ vol = Rudy::AWS::EC2::Volume.new
193
+ vol.status = h['status']
194
+ vol.size = h['size']
195
+ vol.snapid = h['snapshotId']
196
+ vol.zone = h['availabilityZone']
197
+ vol.awsid = h['volumeId']
198
+ vol.create_time = h['createTime']
199
+ if h['attachmentSet'].is_a?(Hash)
200
+ item = h['attachmentSet']['item'].first
201
+ vol.status = item['status'] # Overwrite "available status". Possibly a bad idea.
202
+ vol.device = item['device']
203
+ vol.attach_time = item['attachTime']
204
+ vol.instid = item['instanceId']
205
+ end
206
+ vol
207
+ end
208
+
209
+ # * +vol_id+ is a String or Rudy::AWS::EC2::Volume
210
+ # Returns the volume ID
211
+ def self.get_vol_id(vol_id)
212
+ (vol_id.is_a?(Rudy::AWS::EC2::Volume)) ? vol_id.awsid : vol_id
213
+ end
214
+
215
+ # Is +state+ a known EC2 volume state? See: KNOWN_STATES
216
+ def self.known_state?(state)
217
+ return false unless state
218
+ state &&= state.to_sym
219
+ KNOWN_STATES.member?(state)
220
+ end
221
+
222
+ end
223
+ end
224
+ end
225
+
226
+
227
+ class Rudy::AWS::EC2::Volumes
228
+
229
+
230
+ end
@@ -0,0 +1,77 @@
1
+
2
+ module Rudy::AWS
3
+ module EC2
4
+
5
+ class Zone < Storable
6
+
7
+ field :name
8
+ field :region
9
+ field :state
10
+
11
+ def liner_note
12
+ "%-10s %9s %s" % [self.name, self.region, self.state]
13
+ end
14
+
15
+ def to_s(titles=false)
16
+ str = titles ? "%-20s %s#{$/}" % ['name', 'region', 'state'] : ""
17
+ str << liner_note
18
+ end
19
+
20
+ end
21
+
22
+ class Zones
23
+ include Rudy::AWS::ObjectBase
24
+ include Rudy::AWS::EC2::Base
25
+
26
+ def list(*names)
27
+ zones = list_as_hash(names)
28
+ zones &&= zones.values
29
+ zones
30
+ end
31
+
32
+ def list_as_hash(*names)
33
+ names = names.flatten
34
+ zlist = @ec2.describe_availability_zones(:zone_name => names)
35
+ return unless zlist['availabilityZoneInfo'].is_a?(Hash)
36
+ zones = {}
37
+ zlist['availabilityZoneInfo']['item'].each do |zhash|
38
+ zon = Zones.from_hash(zhash)
39
+ zones[zon.name] = zon
40
+ end
41
+ zones
42
+ end
43
+
44
+ def self.from_hash(h)
45
+ zone = Rudy::AWS::EC2::Zone.new
46
+ zone.name = h['zoneName']
47
+ zone.region = h['regionName']
48
+ zone.state = h['zoneState']
49
+ zone
50
+ end
51
+
52
+ def any?
53
+ zones = list || []
54
+ !zones.empty?
55
+ end
56
+
57
+ def get(name)
58
+ zones = list(name) || []
59
+ return if zones.empty?
60
+ zones.first
61
+ end
62
+
63
+ def zone?(name)
64
+ begin
65
+ kp = get(name)
66
+ kp.is_a?(Rudy::AWS::EC2::Zone)
67
+ rescue => ex
68
+ false
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+
@@ -1,3 +1,57 @@
1
1
 
2
2
  module Rudy::AWS
3
+ class S3
4
+
5
+ def initialize(access_key=nil, secret_key=nil, region=nil, debug=nil)
6
+
7
+ url ||= 'http://sdb.amazonaws.com'
8
+ # There is a bug with passing :server to EC2::Base.new so
9
+ # we'll use the environment variable for now.
10
+ #if region && Rudy::AWS.valid_region?(region)
11
+ # "#{region}.sdb.amazonaws.com"
12
+ #end
13
+
14
+ @access_key_id = access_key || ENV['AWS_ACCESS_KEY'] || ENV['AMAZON_ACCESS_KEY_ID']
15
+ @secret_access_key = secret_key || ENV['AWS_SECRET_KEY'] || ENV['AMAZON_SECRET_ACCESS_KEY']
16
+ @base_url = url
17
+ @debug = debug || StringIO.new
18
+
19
+
20
+ AWS::S3::Base.establish_connection!(
21
+ :access_key_id => @access_key_id,
22
+ :secret_access_key => @secret_access_key
23
+ )
24
+
25
+ end
26
+
27
+ def list_buckets
28
+ ::AWS::S3::Service.buckets
29
+ end
30
+
31
+ def create_bucket(name)
32
+ ::AWS::S3::Bucket.create(name)
33
+ end
34
+
35
+ def destroy_bucket(name)
36
+ ::AWS::S3::Bucket.delete(name)
37
+ end
38
+
39
+ def find_bucket(name)
40
+ ::AWS::S3::Bucket.delete(name)
41
+ end
42
+
43
+ def list_bucket_objects(name)
44
+ ::AWS::S3::Bucket.objects(name)
45
+ end
46
+
47
+ #def store(path, bucket)
48
+ # fname = File.basename(path)
49
+ # S3Object.store(fname, open(path), bucket)
50
+ #end
51
+
52
+ def bucket_exists?(name)
53
+ b = find_bucket(name)
54
+ !b.nil?
55
+ end
56
+ end
3
57
  end