bucket_brigade 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8cdb69753762ea4144d0ac29a7debeff8fcefe8d
4
+ data.tar.gz: 9dfc9ad55eba0aa9e4b923949e477023a13b7c49
5
+ SHA512:
6
+ metadata.gz: 31a691b784f454d86ee13e308622cb51ebaff8bc3a9a3c714fa94745025e4ba71738ce82513a36f9ba6a9e2b9a7bd5252408ff1e9a09e77a02b0dfd71bc56009
7
+ data.tar.gz: a1f16f71752d1488930d0308fcb3865791a87f3dd59799051431ba5171795f2ed2902bca6b26ce6530e80461374f6d411442a06cc527bfddbe7884f23a1b51f6
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require rspec/legacy_formatters
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ This gem makes it easy to use a cache hierarchy of S3 buckets. Slower, more durable
2
+ layers can be on Amazon S3. Faster local layers can use another S3-compatible service
3
+ like Riak CS.
4
+
5
+ Usage:
6
+
7
+ ```ruby
8
+ require 'bucket_brigade/bucket_config
9
+
10
+ bucket_config1 = BucketBrigade::BucketConfig.new(
11
+ 'http://192.168.37.73:8080',
12
+ 'access_key_id',
13
+ 'secret_access_key',
14
+ 'test-bucket'
15
+ )
16
+
17
+ bucket_config2 = BucketBrigade::BucketConfig.new(
18
+ 'https://s3.amazonaws.com'
19
+ 'access_key_id',
20
+ 'secret_access_key',
21
+ 'test-bucket'
22
+ )
23
+
24
+
25
+ bucket_list = BucketBrigade::BucketList.new(
26
+ bucket_config1,
27
+ bucket_config2,
28
+ )
29
+
30
+ open('/tmp/file') do |f|
31
+ bucket_list.put('test-key', f)
32
+ end
33
+
34
+ puts bucket_list.get('test-key')
35
+ ```
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'bucket_brigade'
5
+ s.version = '0.0.1'
6
+ s.summary = 'S3 bucket cache hierarchy'
7
+ s.description = 'S3 bucket cache hierarchy'
8
+ s.author = 'Pivotal Labs'
9
+ s.homepage = 'https://github.com/pivotal-cf-experimental/bucket_brigade'
10
+ s.license = 'Apache 2.0'
11
+ s.email = 'support@cloudfoundry.com'
12
+ s.files = `git ls-files`.split("\n")
13
+
14
+ s.add_development_dependency 'rspec', '~> 3.0.0'
15
+ s.add_development_dependency 'rspec-legacy_formatters', '~> 1.0.0'
16
+ s.add_dependency 'aws-sdk', '~> 1.49.0'
17
+ end
@@ -0,0 +1 @@
1
+ module BucketBrigade; end
@@ -0,0 +1,51 @@
1
+ require 'bucket_brigade'
2
+
3
+ require 'aws-sdk'
4
+
5
+ require 'tempfile'
6
+
7
+ class BucketBrigade::Bucket
8
+ def initialize(bucket_config)
9
+ @s3_bucket = AWS::S3.new(
10
+ s3_endpoint: bucket_config.host,
11
+ s3_port: bucket_config.port,
12
+ access_key_id: bucket_config.access_key_id,
13
+ secret_access_key: bucket_config.secret_access_key,
14
+ use_ssl: bucket_config.use_ssl,
15
+ s3_force_path_style: true
16
+ ).buckets[bucket_config.bucket]
17
+ end
18
+
19
+ def has_key?(key)
20
+ object(key).exists?
21
+ end
22
+
23
+ def get(key)
24
+ has_key?(key) ? object(key) : nil
25
+ end
26
+
27
+ def put(key, io)
28
+ object(key).write(io)
29
+ end
30
+
31
+ def delete(key)
32
+ object(key).delete
33
+ end
34
+
35
+ def object(key)
36
+ @s3_bucket.objects[key]
37
+ end
38
+
39
+ def copy(key, other)
40
+ temp = Tempfile.new('bucket-copy')
41
+ object(key).read do |chunk|
42
+ temp.write(chunk)
43
+ end
44
+
45
+ temp.rewind
46
+
47
+ other.object(key).write(temp)
48
+
49
+ temp.close!
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ require 'bucket_brigade'
2
+
3
+ require 'uri'
4
+
5
+ class BucketBrigade::BucketConfig
6
+ attr_reader :endpoint
7
+ attr_reader :access_key_id
8
+ attr_reader :secret_access_key
9
+ attr_reader :bucket
10
+ attr_reader :host
11
+ attr_reader :port
12
+ attr_reader :use_ssl
13
+
14
+ def initialize(endpoint, access_key_id, secret_access_key, bucket)
15
+ @endpoint = endpoint
16
+ @access_key_id = access_key_id
17
+ @secret_access_key = secret_access_key
18
+ @bucket = bucket
19
+
20
+ uri = URI(@endpoint)
21
+ @host = uri.host
22
+ @port = uri.port
23
+ @use_ssl = uri.scheme == 'https'
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ require 'bucket_brigade'
2
+ require 'bucket_brigade/bucket'
3
+
4
+ require 'aws-sdk'
5
+
6
+ class BucketBrigade::BucketList
7
+ def initialize(*bucket_configs)
8
+ @buckets = bucket_configs.map do |bucket_config|
9
+ BucketBrigade::Bucket.new(bucket_config)
10
+ end
11
+ end
12
+
13
+ def get(key)
14
+ @buckets.each_with_index do |bucket, i|
15
+ if bucket.has_key?(key)
16
+ if i > 0
17
+ prev_bucket = @buckets[i - 1]
18
+ bucket.copy(key, prev_bucket)
19
+ return prev_bucket.get(key)
20
+ end
21
+ return bucket.get(key)
22
+ end
23
+ end
24
+ nil
25
+ end
26
+
27
+ def put(key, io)
28
+ @buckets.last.put(key, io)
29
+ # invalidate all the way down
30
+ @buckets.first(@buckets.size - 1).each do |bucket|
31
+ bucket.delete(key)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ require 'bucket_brigade/bucket_config'
2
+ require 'bucket_brigade/bucket_list'
3
+
4
+ describe 'put and get data from the cache' do
5
+
6
+ it 'puts and gets a file from the cache' do
7
+ bucket_config1 = BucketBrigade::BucketConfig.new(
8
+ ENV.fetch('LOCAL_S3_ENDPOINT'),
9
+ ENV.fetch('LOCAL_S3_ID'),
10
+ ENV.fetch('LOCAL_S3_SECRET'),
11
+ 'test'
12
+ )
13
+
14
+ bucket_config2 = BucketBrigade::BucketConfig.new(
15
+ ENV.fetch('LOCAL_S3_ENDPOINT'),
16
+ ENV.fetch('LOCAL_S3_ID'),
17
+ ENV.fetch('LOCAL_S3_SECRET'),
18
+ 'test2'
19
+ )
20
+
21
+ bucket_config3 = BucketBrigade::BucketConfig.new(
22
+ 'http://s3.amazonaws.com',
23
+ ENV.fetch('S3_ID'),
24
+ ENV.fetch('S3_SECRET'),
25
+ 'bucket-brigade-test'
26
+ )
27
+
28
+ bucket_list = BucketBrigade::BucketList.new(
29
+ bucket_config1,
30
+ bucket_config2,
31
+ bucket_config3
32
+ )
33
+
34
+ io = StringIO.new
35
+ io.write('test 1 2 3')
36
+ io.rewind
37
+
38
+ bucket_list.put('test', io)
39
+
40
+ expect(bucket_list.get('test').read).to eq('test 1 2 3')
41
+ end
42
+ end
@@ -0,0 +1,72 @@
1
+ require 'bucket_brigade/bucket_config'
2
+
3
+ describe BucketBrigade::BucketConfig do
4
+ let(:endpoint) { 'http://host.com:1234' }
5
+ let(:access_key_id) { 'test-access-key-id' }
6
+ let(:secret_access_key) { 'secret-access-key' }
7
+ let(:bucket) { 'bucket' }
8
+
9
+ subject(:bucket_config) do
10
+ described_class.new(endpoint, access_key_id, secret_access_key, bucket)
11
+ end
12
+
13
+ describe '#endpoint' do
14
+ it 'returns the endpoint' do
15
+ expect(bucket_config.endpoint).to eq(endpoint)
16
+ end
17
+ end
18
+
19
+ describe '#access_key_id' do
20
+ it 'returns the access_key_id' do
21
+ expect(bucket_config.access_key_id).to eq(access_key_id)
22
+ end
23
+ end
24
+
25
+ describe '#secret_access_key' do
26
+ it 'returns the secret_access_key' do
27
+ expect(bucket_config.secret_access_key).to eq(secret_access_key)
28
+ end
29
+ end
30
+
31
+ describe '#bucket' do
32
+ it 'returns the bucket' do
33
+ expect(bucket_config.bucket).to eq(bucket)
34
+ end
35
+ end
36
+
37
+ describe '#port' do
38
+ context 'when the port is provided' do
39
+ it 'returns the port' do
40
+ expect(bucket_config.port).to eq(1234)
41
+ end
42
+ end
43
+
44
+ context 'when the port is not provided' do
45
+ let(:endpoint) { 'http://host.com' }
46
+ it 'returns port 80' do
47
+ expect(bucket_config.port).to eq(80)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#host' do
53
+ it 'returns the host' do
54
+ expect(bucket_config.host).to eq('host.com')
55
+ end
56
+ end
57
+
58
+ describe '#use_ssl' do
59
+ context 'when the endpoint is https' do
60
+ let(:endpoint) { 'https://host.com/' }
61
+ it 'returns true' do
62
+ expect(bucket_config.use_ssl).to eq(true)
63
+ end
64
+ end
65
+
66
+ context 'when the endpoint is not https' do
67
+ it 'returns false' do
68
+ expect(bucket_config.use_ssl).to eq(false)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,150 @@
1
+ require 'bucket_brigade/bucket_config'
2
+ require 'bucket_brigade/bucket_list'
3
+
4
+ describe BucketBrigade::BucketList do
5
+
6
+ let(:bucket_configs) {
7
+ [
8
+ BucketBrigade::BucketConfig.new(
9
+ 'http://test-endpoint-1:1234',
10
+ 'test-access-key-id-1',
11
+ 'test-secret-access-key-1',
12
+ 'test-bucket-1'
13
+ ),
14
+ BucketBrigade::BucketConfig.new(
15
+ 'http://test-endpoint-2:1234',
16
+ 'test-access-key-id-2',
17
+ 'test-secret-access-key-2',
18
+ 'test-bucket-2'
19
+ )
20
+ ]
21
+ }
22
+
23
+ let(:s3_1) { instance_double(AWS::S3) }
24
+ let(:s3_2) { instance_double(AWS::S3) }
25
+
26
+ let(:bucket_collection_1) { instance_double(AWS::S3::BucketCollection) }
27
+ let(:bucket_collection_2) { instance_double(AWS::S3::BucketCollection) }
28
+
29
+ let(:bucket_1) { instance_double(AWS::S3::Bucket) }
30
+ let(:bucket_2) { instance_double(AWS::S3::Bucket) }
31
+
32
+ let(:object_collection_1) { instance_double(AWS::S3::ObjectCollection) }
33
+ let(:object_collection_2) { instance_double(AWS::S3::ObjectCollection) }
34
+
35
+ let(:object_1) { instance_double(AWS::S3::S3Object) }
36
+ let(:object_2) { instance_double(AWS::S3::S3Object) }
37
+
38
+ before do
39
+ allow(AWS::S3).to receive(:new).with(
40
+ s3_endpoint: 'test-endpoint-1',
41
+ s3_port: 1234,
42
+ access_key_id: 'test-access-key-id-1',
43
+ secret_access_key: 'test-secret-access-key-1',
44
+ use_ssl: false,
45
+ s3_force_path_style: true
46
+ ).and_return(s3_1)
47
+ allow(s3_1).to receive(:buckets).and_return(bucket_collection_1)
48
+ allow(bucket_collection_1).to receive(:[]).with('test-bucket-1').and_return(bucket_1)
49
+ allow(bucket_1).to receive(:objects).and_return(object_collection_1)
50
+ allow(object_collection_1).to receive(:[]).with('test-key').and_return(object_1)
51
+
52
+ allow(AWS::S3).to receive(:new).with(
53
+ s3_endpoint: 'test-endpoint-2',
54
+ s3_port: 1234,
55
+ access_key_id: 'test-access-key-id-2',
56
+ secret_access_key: 'test-secret-access-key-2',
57
+ use_ssl: false,
58
+ s3_force_path_style: true
59
+ ).and_return(s3_2)
60
+ allow(s3_2).to receive(:buckets).and_return(bucket_collection_2)
61
+ allow(bucket_collection_2).to receive(:[]).with('test-bucket-2').and_return(bucket_2)
62
+ allow(bucket_2).to receive(:objects).and_return(object_collection_2)
63
+ allow(object_collection_2).to receive(:[]).with('test-key').and_return(object_2)
64
+ end
65
+
66
+ subject(:bucket_list) { described_class.new(*bucket_configs) }
67
+
68
+ describe '#get' do
69
+ context 'when the object is found in the first bucket' do
70
+ before do
71
+ allow(object_1).to receive(:exists?).and_return(true)
72
+ end
73
+
74
+ it 'returns the object from the first bucket' do
75
+ expect(bucket_list.get('test-key')).to eq(object_1)
76
+ end
77
+ end
78
+
79
+ context 'when the object is not found in any buckets' do
80
+ before do
81
+ allow(object_1).to receive(:exists?).and_return(false)
82
+ allow(object_2).to receive(:exists?).and_return(false)
83
+ end
84
+
85
+ it 'returns nil' do
86
+ expect(bucket_list.get('test-key')).to eq(nil)
87
+ end
88
+ end
89
+
90
+ context 'when the object is found in the second bucket' do
91
+ let(:temp) { instance_double(Tempfile) }
92
+
93
+ before do
94
+ allow(object_1).to receive(:exists?).and_return(false)
95
+ allow(object_2).to receive(:exists?).and_return(true)
96
+
97
+ allow(Tempfile).to receive(:new).with('bucket-copy').and_return(temp)
98
+ allow(temp).to receive(:rewind)
99
+ allow(temp).to receive(:close!)
100
+ end
101
+
102
+ it 'writes the object to the first bucket' do
103
+ allow(object_2).to receive(:read)
104
+ expect(object_1).to receive(:write).with(temp)
105
+ bucket_list.get('test-key')
106
+ end
107
+
108
+ it 'returns the object from the first bucket' do
109
+ allow(object_2).to receive(:read)
110
+
111
+ allow(object_1).to receive(:write).with(temp) {
112
+ allow(object_1).to receive(:exists?).and_return(true)
113
+ }
114
+ expect(bucket_list.get('test-key')).to eq(object_1)
115
+ end
116
+
117
+ end
118
+
119
+ context 'when the object is found in both buckets' do
120
+ before do
121
+ allow(object_1).to receive(:exists?).and_return(true)
122
+ allow(object_2).to receive(:exists?).and_return(true)
123
+ end
124
+
125
+ it 'returns the object from the first bucket' do
126
+ expect(bucket_list.get('test-key')).to eq(object_1)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#put' do
132
+ context 'when writing an object' do
133
+ let(:io) { instance_double(File) }
134
+
135
+ it 'puts the object in the last bucket' do
136
+ allow(object_1).to receive(:delete)
137
+
138
+ expect(object_2).to receive(:write).with(io)
139
+ bucket_list.put('test-key', io)
140
+ end
141
+
142
+ it 'delete the object from the first bucket' do
143
+ allow(object_2).to receive(:write).with(io)
144
+
145
+ expect(object_1).to receive(:delete)
146
+ bucket_list.put('test-key', io)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,145 @@
1
+ require 'bucket_brigade/bucket'
2
+ require 'bucket_brigade/bucket_config'
3
+
4
+ require 'aws-sdk'
5
+
6
+ describe BucketBrigade::Bucket do
7
+
8
+ subject(:bucket) { described_class.new(bucket_config) }
9
+
10
+ let(:bucket_config) {
11
+ BucketBrigade::BucketConfig.new(
12
+ 'http://host.com:1234',
13
+ 'test-access-key-id',
14
+ 'test-secret-access-key',
15
+ 'bucket')
16
+ }
17
+
18
+ let(:s3) { instance_double(AWS::S3) }
19
+ let(:bucket_collection) { instance_double(AWS::S3::BucketCollection) }
20
+ let(:s3_bucket) { instance_double(AWS::S3::Bucket) }
21
+ let(:object_collection) { instance_double(AWS::S3::ObjectCollection) }
22
+ let(:object) { instance_double(AWS::S3::S3Object) }
23
+
24
+ before do
25
+ allow(AWS::S3).to receive(:new).with(
26
+ s3_endpoint: 'host.com',
27
+ s3_port: 1234,
28
+ access_key_id: 'test-access-key-id',
29
+ secret_access_key: 'test-secret-access-key',
30
+ use_ssl: false,
31
+ s3_force_path_style: true
32
+ ).and_return(s3)
33
+ allow(s3).to receive(:buckets).and_return(bucket_collection)
34
+ allow(bucket_collection).to receive(:[]).with('bucket').and_return(s3_bucket)
35
+ allow(s3_bucket).to receive(:objects).and_return(object_collection)
36
+ allow(object_collection).to receive(:[]).with('test-key').and_return(object)
37
+ end
38
+
39
+ describe '#has_key' do
40
+ context 'when the object does not exist' do
41
+ before { allow(object).to receive(:exists?).and_return(false) }
42
+
43
+ it 'returns false' do
44
+ expect(bucket).to_not have_key('test-key')
45
+ end
46
+ end
47
+
48
+ context 'when the object exists' do
49
+ before { allow(object).to receive(:exists?).and_return(true) }
50
+
51
+ it 'returns true' do
52
+ expect(bucket).to have_key('test-key')
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#get' do
58
+ context 'when the object does not exist' do
59
+ before { allow(object).to receive(:exists?).and_return(false) }
60
+
61
+ it 'returns nil' do
62
+ expect(bucket.get('test-key')).to be(nil)
63
+ end
64
+ end
65
+
66
+ context 'when the object exists' do
67
+ before { allow(object).to receive(:exists?).and_return(true) }
68
+
69
+ it 'gets an object from the bucket' do
70
+ expect(bucket.get('test-key')).to eq(object)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#put' do
76
+ it 'puts an object in the bucket' do
77
+ io = instance_double(File)
78
+ expect(object).to receive(:write).with(io)
79
+ bucket.put('test-key', io)
80
+ end
81
+ end
82
+
83
+ describe '#delete' do
84
+ it 'delete the object from the bucket' do
85
+ expect(object).to receive(:delete)
86
+ bucket.delete('test-key')
87
+ end
88
+ end
89
+
90
+ describe '#object' do
91
+ it 'gets the object from the bucket' do
92
+ expect(bucket.object('test-key')).to eq(object)
93
+ end
94
+ end
95
+
96
+ describe '#copy' do
97
+ let(:other_bucket_config) {
98
+ BucketBrigade::BucketConfig.new(
99
+ 'http://host2.com:1234',
100
+ 'test-access-key-id',
101
+ 'test-secret-access-key',
102
+ 'bucket')
103
+ }
104
+
105
+ let(:other_bucket) { described_class.new(other_bucket_config) }
106
+
107
+ let(:other_s3) { instance_double(AWS::S3) }
108
+ let(:other_bucket_collection) { instance_double(AWS::S3::BucketCollection) }
109
+ let(:other_s3_bucket) { instance_double(AWS::S3::Bucket) }
110
+ let(:other_object_collection) { instance_double(AWS::S3::ObjectCollection) }
111
+ let(:other_object) { instance_double(AWS::S3::S3Object) }
112
+
113
+ before do
114
+ allow(AWS::S3).to receive(:new).with(
115
+ s3_endpoint: 'host2.com',
116
+ s3_port: 1234,
117
+ access_key_id: 'test-access-key-id',
118
+ secret_access_key: 'test-secret-access-key',
119
+ use_ssl: false,
120
+ s3_force_path_style: true
121
+ ).and_return(other_s3)
122
+
123
+ allow(other_s3).to receive(:buckets).and_return(other_bucket_collection)
124
+ allow(other_bucket_collection).to receive(:[]).with('bucket').and_return(
125
+ other_s3_bucket)
126
+ allow(other_s3_bucket).to receive(:objects).and_return(
127
+ other_object_collection)
128
+ allow(other_object_collection).to receive(:[]).with('test-key').and_return(
129
+ other_object)
130
+ end
131
+
132
+ it 'copies an object from one bucket to another' do
133
+ temp = instance_double(Tempfile)
134
+ allow(Tempfile).to receive(:new).with('bucket-copy').and_return(temp)
135
+ allow(temp).to receive(:write).with('test')
136
+ allow(temp).to receive(:rewind)
137
+ allow(object).to receive(:read).with(no_args).and_yield('test')
138
+
139
+ expect(other_object).to receive(:write).with(temp)
140
+ expect(temp).to receive(:close!)
141
+
142
+ bucket.copy('test-key', other_bucket)
143
+ end
144
+ end
145
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bucket_brigade
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pivotal Labs
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-legacy_formatters
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.49.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.49.0
55
+ description: S3 bucket cache hierarchy
56
+ email: support@cloudfoundry.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - .gitignore
62
+ - .rspec
63
+ - Gemfile
64
+ - README.md
65
+ - bucket_brigade.gemspec
66
+ - lib/bucket_brigade.rb
67
+ - lib/bucket_brigade/bucket.rb
68
+ - lib/bucket_brigade/bucket_config.rb
69
+ - lib/bucket_brigade/bucket_list.rb
70
+ - spec/integration/put_get_spec.rb
71
+ - spec/unit/bucket_brigade/bucket_config_spec.rb
72
+ - spec/unit/bucket_brigade/bucket_list_spec.rb
73
+ - spec/unit/bucket_brigade/bucket_spec.rb
74
+ homepage: https://github.com/pivotal-cf-experimental/bucket_brigade
75
+ licenses:
76
+ - Apache 2.0
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.0.14
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: S3 bucket cache hierarchy
98
+ test_files: []