heirloom 0.9.0 → 0.10.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.
- data/.rvmrc +1 -1
- data/CHANGELOG +7 -0
- data/README.md +1 -11
- data/lib/heirloom.rb +2 -0
- data/lib/heirloom/acl/s3.rb +8 -9
- data/lib/heirloom/archive/authorizer.rb +3 -1
- data/lib/heirloom/archive/destroyer.rb +3 -3
- data/lib/heirloom/archive/downloader.rb +4 -4
- data/lib/heirloom/archive/reader.rb +15 -5
- data/lib/heirloom/archive/uploader.rb +13 -9
- data/lib/heirloom/archive/writer.rb +2 -0
- data/lib/heirloom/cipher.rb +2 -0
- data/lib/heirloom/cipher/data.rb +37 -19
- data/lib/heirloom/cipher/file.rb +23 -22
- data/lib/heirloom/cipher/shared.rb +19 -0
- data/lib/heirloom/cli/upload.rb +2 -1
- data/lib/heirloom/directory/directory.rb +12 -1
- data/lib/heirloom/exceptions.rb +11 -0
- data/lib/heirloom/uploader/s3.rb +34 -23
- data/lib/heirloom/utils.rb +1 -0
- data/lib/heirloom/utils/file.rb +30 -0
- data/lib/heirloom/version.rb +1 -1
- data/spec/acl/s3_spec.rb +1 -1
- data/spec/archive/authorizer_spec.rb +25 -13
- data/spec/archive/destroyer_spec.rb +1 -1
- data/spec/archive/downloader_spec.rb +4 -4
- data/spec/archive/reader_spec.rb +19 -5
- data/spec/archive/uploader_spec.rb +47 -20
- data/spec/cipher/data_spec.rb +52 -35
- data/spec/cipher/file_spec.rb +23 -9
- data/spec/cli/upload_spec.rb +1 -0
- data/spec/directory/directory_spec.rb +36 -10
- data/spec/uploader/s3_spec.rb +64 -0
- data/spec/utils/file_spec.rb +30 -0
- metadata +22 -14
data/lib/heirloom/uploader/s3.rb
CHANGED
@@ -9,48 +9,59 @@ module Heirloom
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def upload_file(args)
|
12
|
-
bucket
|
13
|
-
file
|
14
|
-
id
|
15
|
-
key_name
|
16
|
-
key_folder
|
17
|
-
name
|
12
|
+
bucket = args[:bucket]
|
13
|
+
file = args[:file]
|
14
|
+
id = args[:id]
|
15
|
+
key_name = args[:key_name]
|
16
|
+
key_folder = args[:key_folder]
|
17
|
+
name = args[:name]
|
18
18
|
public_readable = args[:public_readable]
|
19
19
|
|
20
|
+
body = File.open file
|
20
21
|
s3_bucket = s3.get_bucket bucket
|
21
22
|
|
22
23
|
@logger.info "Uploading s3://#{bucket}/#{key_folder}/#{key_name}"
|
23
24
|
|
24
25
|
s3_bucket.files.create :key => "#{key_folder}/#{key_name}",
|
25
|
-
:body =>
|
26
|
+
:body => body,
|
26
27
|
:public => public_readable
|
28
|
+
@logger.warn "File is readable by entire Internet." if public_readable
|
27
29
|
|
28
|
-
|
30
|
+
body.close
|
29
31
|
end
|
30
32
|
|
31
33
|
def add_endpoint_attributes(args)
|
32
|
-
bucket
|
33
|
-
id
|
34
|
-
name
|
35
|
-
|
34
|
+
bucket = args[:bucket]
|
35
|
+
id = args[:id]
|
36
|
+
name = args[:name]
|
37
|
+
key_name = args[:key_name]
|
38
|
+
|
39
|
+
domain = "heirloom_#{name}"
|
36
40
|
key_folder = name
|
37
|
-
|
41
|
+
endpoint = endpoints[@region]
|
38
42
|
|
39
|
-
|
40
|
-
http_endpoint = "http://#{endpoints[@region]}/#{bucket}/#{key_folder}/#{key_name}"
|
41
|
-
https_endpoint = "https://#{endpoints[@region]}/#{bucket}/#{key_folder}/#{key_name}"
|
43
|
+
path = "#{bucket}/#{key_folder}/#{key_name}"
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
+
end_point_attributes(path).each_pair do |key, value|
|
46
|
+
add_endpoint_attribute domain, id, key, value
|
47
|
+
end
|
48
|
+
end
|
45
49
|
|
46
|
-
|
47
|
-
@logger.debug "Adding attribute #{http_endpoint}."
|
50
|
+
private
|
48
51
|
|
49
|
-
|
50
|
-
|
52
|
+
def end_point_attributes(path)
|
53
|
+
{
|
54
|
+
"#{@region}-s3-url" => "s3://#{path}",
|
55
|
+
"#{@region}-http-url" => "http://#{endpoints[@region]}/#{path}",
|
56
|
+
"#{@region}-https-url" => "https://#{endpoints[@region]}/#{path}"
|
57
|
+
}
|
51
58
|
end
|
52
59
|
|
53
|
-
|
60
|
+
def add_endpoint_attribute(domain, id, key, value)
|
61
|
+
sdb.put_attributes domain, id, { key => value }
|
62
|
+
@logger.debug "Adding tag #{key}."
|
63
|
+
@logger.debug "Adding tag #{value}."
|
64
|
+
end
|
54
65
|
|
55
66
|
def endpoints
|
56
67
|
{
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'heirloom/utils/file'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Heirloom
|
2
|
+
module Utils
|
3
|
+
module File
|
4
|
+
|
5
|
+
def which(cmd)
|
6
|
+
exts = pathext ? pathext.split(';') : ['']
|
7
|
+
path.split(path_separator).each do |path|
|
8
|
+
exts.each { |ext|
|
9
|
+
exe = "#{path}/#{cmd}#{ext}"
|
10
|
+
return exe if ::File.executable? exe
|
11
|
+
}
|
12
|
+
end
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def path_separator
|
17
|
+
::File::PATH_SEPARATOR
|
18
|
+
end
|
19
|
+
|
20
|
+
def path
|
21
|
+
ENV['PATH']
|
22
|
+
end
|
23
|
+
|
24
|
+
def pathext
|
25
|
+
ENV['PATHEXT']
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/heirloom/version.rb
CHANGED
data/spec/acl/s3_spec.rb
CHANGED
@@ -29,7 +29,7 @@ describe Heirloom do
|
|
29
29
|
with("bucket", "key-folder/key.tar.gz", {"Owner"=>{"DisplayName"=>"Brett", "ID"=>"123"}, "AccessControlList"=>[{"Grantee"=>{"EmailAddress"=>"acct1@test.com"}, "Permission"=>"READ"}, {"Grantee"=>{"EmailAddress"=>"acct2@test.com"}, "Permission"=>"READ"}, {"Grantee"=>{"DisplayName"=>"Brett", "ID"=>"123"}, "Permission"=>"FULL_CONTROL"}]})
|
30
30
|
|
31
31
|
@s3.allow_read_access_from_accounts :bucket => 'bucket',
|
32
|
-
:key_name => 'key',
|
32
|
+
:key_name => 'key.tar.gz',
|
33
33
|
:key_folder => 'key-folder',
|
34
34
|
:accounts => ['acct1@test.com', 'acct2@test.com']
|
35
35
|
end
|
@@ -3,28 +3,40 @@ require 'spec_helper'
|
|
3
3
|
describe Heirloom do
|
4
4
|
|
5
5
|
before do
|
6
|
-
@config_mock =
|
7
|
-
@logger_mock =
|
6
|
+
@config_mock = mock 'config'
|
7
|
+
@logger_mock = mock 'logger'
|
8
8
|
@logger_mock.stub :info => true, :debug => true
|
9
9
|
@config_mock.should_receive(:logger).and_return(@logger_mock)
|
10
10
|
@authorizer = Heirloom::Authorizer.new :config => @config_mock,
|
11
11
|
:name => 'tim',
|
12
|
-
:id => '123'
|
12
|
+
:id => '123.tar.gz'
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should authorize access to an archive in all regions" do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
and_return(reader)
|
21
|
-
reader.should_receive(:get_bucket).exactly(2).times.
|
16
|
+
s3_acl_mock = mock 's3 acl'
|
17
|
+
reader_mock = mock 'reader mock'
|
18
|
+
reader_mock.stub :key_name => '123.tar.gz'
|
19
|
+
reader_mock.should_receive(:get_bucket).exactly(2).times.
|
22
20
|
and_return('the-bucket')
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
|
22
|
+
accounts = [ "test@a.com", "a@test.com", "test@test.co", "test@test.co.uk" ]
|
23
|
+
|
24
|
+
Heirloom::Reader.should_receive(:new).
|
25
|
+
with(:config => @config_mock,
|
26
|
+
:name => 'tim',
|
27
|
+
:id => '123.tar.gz').
|
28
|
+
and_return reader_mock
|
29
|
+
Heirloom::ACL::S3.should_receive(:new).
|
30
|
+
with(:config => @config_mock,
|
31
|
+
:region => 'us-west-1').
|
32
|
+
and_return s3_acl_mock
|
33
|
+
Heirloom::ACL::S3.should_receive(:new).
|
34
|
+
with(:config => @config_mock,
|
35
|
+
:region => 'us-west-2').
|
36
|
+
and_return s3_acl_mock
|
37
|
+
s3_acl_mock.should_receive(:allow_read_access_from_accounts).
|
26
38
|
exactly(2).times.
|
27
|
-
with(:key_name => '123',
|
39
|
+
with(:key_name => '123.tar.gz',
|
28
40
|
:key_folder => 'tim',
|
29
41
|
:bucket => 'the-bucket',
|
30
42
|
:accounts => accounts)
|
@@ -17,7 +17,7 @@ describe Heirloom do
|
|
17
17
|
@reader_mock.should_receive(:get_bucket).
|
18
18
|
with(:region => 'us-west-1').
|
19
19
|
and_return 'bucket-us-west-1'
|
20
|
-
|
20
|
+
@reader_mock.stub :key_name => '123.tar.gz'
|
21
21
|
|
22
22
|
@s3_destroyer_mock = mock 's3 destroyer'
|
23
23
|
Heirloom::Destroyer::S3.should_receive(:new).
|
@@ -85,7 +85,7 @@ describe Heirloom do
|
|
85
85
|
before do
|
86
86
|
@s3_downloader_mock.should_receive(:download_file).
|
87
87
|
with(:bucket => 'bucket-us-west-1',
|
88
|
-
:key => 'tim/123.tar.gz').
|
88
|
+
:key => 'tim/123.tar.gz.gpg').
|
89
89
|
and_return 'encrypted_data'
|
90
90
|
Heirloom::Cipher::Data.should_receive(:new).
|
91
91
|
with(:config => @config_mock).
|
@@ -136,10 +136,10 @@ describe Heirloom do
|
|
136
136
|
end
|
137
137
|
|
138
138
|
it "should return false if the decrypt_data returns false" do
|
139
|
-
@downloader.download(:region
|
139
|
+
@downloader.download(:region => 'us-west-1',
|
140
140
|
:bucket_prefix => 'bucket',
|
141
|
-
:extract
|
142
|
-
:secret
|
141
|
+
:extract => false,
|
142
|
+
:secret => 'badsecret').should be_false
|
143
143
|
end
|
144
144
|
|
145
145
|
end
|
data/spec/archive/reader_spec.rb
CHANGED
@@ -52,7 +52,7 @@ describe Heirloom do
|
|
52
52
|
with("select * from `heirloom_tim` where itemName() = '123'").
|
53
53
|
and_return( { '123' =>
|
54
54
|
{ 'us-west-1-s3-url' =>
|
55
|
-
[ 's3://the-bucket/the-
|
55
|
+
[ 's3://the-bucket/the-name/123.tar.gz' ]
|
56
56
|
}
|
57
57
|
} )
|
58
58
|
@reader.get_bucket(:region => 'us-west-1').should == 'the-bucket'
|
@@ -80,10 +80,24 @@ describe Heirloom do
|
|
80
80
|
with("select * from `heirloom_tim` where itemName() = '123'").
|
81
81
|
and_return( { '123' =>
|
82
82
|
{ 'us-west-1-s3-url' =>
|
83
|
-
['s3://the-url/the-bucket/
|
83
|
+
['s3://the-url/the-bucket/123.tar.gz']
|
84
84
|
}
|
85
85
|
} )
|
86
|
-
@reader.get_key(:region => 'us-west-1').should == 'the-bucket/
|
86
|
+
@reader.get_key(:region => 'us-west-1').should == 'the-bucket/123.tar.gz'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return the encrypted key name" do
|
90
|
+
@sdb_mock.should_receive(:select).
|
91
|
+
with("select * from `heirloom_tim` where itemName() = '123'").
|
92
|
+
and_return( { '123' => { 'encrypted' => [ 'true' ] } } )
|
93
|
+
@reader.key_name.should == '123.tar.gz.gpg'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should return the unencrypted key name" do
|
97
|
+
@sdb_mock.should_receive(:select).
|
98
|
+
with("select * from `heirloom_tim` where itemName() = '123'").
|
99
|
+
and_return( { '123' => { 'encrypted' => [ 'false' ] } } )
|
100
|
+
@reader.key_name.should == '123.tar.gz'
|
87
101
|
end
|
88
102
|
|
89
103
|
it "should return the regions the archive has been uploaded to" do
|
@@ -92,11 +106,11 @@ describe Heirloom do
|
|
92
106
|
with("select * from `heirloom_tim` where itemName() = '123'").
|
93
107
|
and_return( { '123' =>
|
94
108
|
{ 'us-west-1-s3-url' =>
|
95
|
-
['s3://the-url-us-west-1/the-bucket/
|
109
|
+
['s3://the-url-us-west-1/the-bucket/123.tar.gz'],
|
96
110
|
'build_by' =>
|
97
111
|
['user'],
|
98
112
|
'us-east-1-s3-url' =>
|
99
|
-
['s3://the-url-us-east-1/the-bucket/
|
113
|
+
['s3://the-url-us-east-1/the-bucket/123.tar.gz']
|
100
114
|
}
|
101
115
|
} )
|
102
116
|
@reader.regions.should == ['us-west-1', 'us-east-1']
|
@@ -3,38 +3,65 @@ require 'spec_helper'
|
|
3
3
|
describe Heirloom do
|
4
4
|
|
5
5
|
before do
|
6
|
-
@config_mock =
|
7
|
-
@
|
8
|
-
@config_mock.
|
6
|
+
@config_mock = mock 'config'
|
7
|
+
@logger_stub = stub 'logger', :info => true
|
8
|
+
@config_mock.stub :logger => @logger_stub
|
9
9
|
@uploader = Heirloom::Uploader.new :config => @config_mock,
|
10
10
|
:name => 'tim',
|
11
11
|
:id => '123'
|
12
|
+
@s3_mock = mock 's3'
|
12
13
|
end
|
13
14
|
|
14
15
|
it "should upload a new archive" do
|
15
|
-
s3_mock = mock 's3'
|
16
16
|
Heirloom::Uploader::S3.should_receive(:new).
|
17
17
|
with(:config => @config_mock,
|
18
|
-
:logger => @
|
18
|
+
:logger => @logger_stub,
|
19
19
|
:region => 'us-west-1').
|
20
|
-
and_return s3_mock
|
21
|
-
s3_mock.should_receive(:upload_file).
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
s3_mock.should_receive(:add_endpoint_attributes).
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
20
|
+
and_return @s3_mock
|
21
|
+
@s3_mock.should_receive(:upload_file).
|
22
|
+
with(:bucket => 'prefix-us-west-1',
|
23
|
+
:file => '/tmp/file',
|
24
|
+
:id => '123',
|
25
|
+
:key_folder => 'tim',
|
26
|
+
:key_name => "123.tar.gz",
|
27
|
+
:name => 'tim',
|
28
|
+
:public_readable => true)
|
29
|
+
@s3_mock.should_receive(:add_endpoint_attributes).
|
30
|
+
with(:bucket => 'prefix-us-west-1',
|
31
|
+
:id => '123',
|
32
|
+
:key_name => '123.tar.gz',
|
33
|
+
:name => 'tim')
|
34
34
|
@uploader.upload :file => '/tmp/file',
|
35
35
|
:bucket_prefix => 'prefix',
|
36
36
|
:regions => ['us-west-1'],
|
37
|
-
:public_readable => true
|
37
|
+
:public_readable => true,
|
38
|
+
:secret => nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should upload a new archive with .gpg if secret provided" do
|
42
|
+
Heirloom::Uploader::S3.should_receive(:new).
|
43
|
+
with(:config => @config_mock,
|
44
|
+
:logger => @logger_stub,
|
45
|
+
:region => 'us-west-1').
|
46
|
+
and_return @s3_mock
|
47
|
+
@s3_mock.should_receive(:upload_file).
|
48
|
+
with(:bucket => 'prefix-us-west-1',
|
49
|
+
:file => '/tmp/file',
|
50
|
+
:id => '123',
|
51
|
+
:key_folder => 'tim',
|
52
|
+
:key_name => "123.tar.gz.gpg",
|
53
|
+
:name => 'tim',
|
54
|
+
:public_readable => true)
|
55
|
+
@s3_mock.should_receive(:add_endpoint_attributes).
|
56
|
+
with(:bucket => 'prefix-us-west-1',
|
57
|
+
:id => '123',
|
58
|
+
:key_name => '123.tar.gz.gpg',
|
59
|
+
:name => 'tim')
|
60
|
+
@uploader.upload :file => '/tmp/file',
|
61
|
+
:bucket_prefix => 'prefix',
|
62
|
+
:regions => ['us-west-1'],
|
63
|
+
:public_readable => true,
|
64
|
+
:secret => 'secret12'
|
38
65
|
end
|
39
66
|
|
40
67
|
end
|
data/spec/cipher/data_spec.rb
CHANGED
@@ -2,54 +2,71 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Heirloom do
|
4
4
|
before do
|
5
|
+
@encrypted_file_mock = mock 'encrypted mock'
|
6
|
+
@decrypted_file_mock = mock 'decrypted mock'
|
7
|
+
@encrypted_file_mock.stub :path => '/tmp/enc'
|
8
|
+
@decrypted_file_mock.stub :path => '/tmp/dec',
|
9
|
+
:read => 'plaintext'
|
5
10
|
@logger_mock = mock 'logger', :info => true
|
6
|
-
@logger_mock.stub :info
|
11
|
+
@logger_mock.stub :info => true,
|
12
|
+
:debug => true
|
7
13
|
@config_mock = mock 'config'
|
8
14
|
@config_mock.stub :logger => @logger_mock
|
9
15
|
@data = Heirloom::Cipher::Data.new :config => @config_mock
|
10
16
|
end
|
11
17
|
|
12
|
-
context "
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
context "when succesful" do
|
19
|
+
context "with secret given" do
|
20
|
+
it "should decrypt the given data" do
|
21
|
+
@data.should_receive(:which).with('gpg').and_return true
|
22
|
+
Tempfile.should_receive(:new).with('archive.tar.gz.gpg').
|
23
|
+
and_return @encrypted_file_mock
|
24
|
+
Tempfile.should_receive(:new).with('archive.tar.gz').
|
25
|
+
and_return @decrypted_file_mock
|
26
|
+
::File.should_receive(:open).
|
27
|
+
with(@encrypted_file_mock,'w')
|
28
|
+
$?.stub :success? => true
|
18
29
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@aes_mock.stub :final => 'final'
|
25
|
-
@data.decrypt_data(:data => 'firstsixteencharcrypteddata',
|
26
|
-
:secret => 'mysecret').
|
27
|
-
should == 'cleartextfinal'
|
30
|
+
command = 'gpg --batch --yes --cipher-algo AES256 --passphrase password --output /tmp/dec /tmp/enc 2>&1'
|
31
|
+
@data.should_receive(:`).with(command).and_return true
|
32
|
+
@data.decrypt_data(:data => 'crypted',
|
33
|
+
:secret => 'password').should == 'plaintext'
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@aes_mock.should_receive(:iv=).with 'firstsixteenchar'
|
36
|
-
@aes_mock.should_receive(:update).with('crypteddata').and_return 'crap'
|
37
|
-
@aes_mock.should_receive(:final).and_raise OpenSSL::Cipher::CipherError
|
38
|
-
@data.decrypt_data(:data => 'firstsixteencharcrypteddata',
|
39
|
-
:secret => 'badsecret').
|
40
|
-
should be_false
|
37
|
+
context "no secret given" do
|
38
|
+
it "should return the data if no secret given" do
|
39
|
+
@data.decrypt_data(:data => 'plaintext',
|
40
|
+
:secret => nil).should == 'plaintext'
|
41
|
+
end
|
41
42
|
end
|
42
|
-
|
43
43
|
end
|
44
44
|
|
45
|
-
context "
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
context "when unsuccesful" do
|
46
|
+
context "with secret given" do
|
47
|
+
it "should return false if gpg not in path" do
|
48
|
+
@logger_mock.should_receive(:error)
|
49
|
+
@data.should_receive(:which).with('gpg').and_return false
|
50
|
+
@data.decrypt_data(:data => 'crypted',
|
51
|
+
:secret => 'password').should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return false if decrypted fails" do
|
55
|
+
@logger_mock.should_receive(:error)
|
56
|
+
@data.should_receive(:which).with('gpg').and_return true
|
57
|
+
Tempfile.should_receive(:new).with('archive.tar.gz.gpg').
|
58
|
+
and_return @encrypted_file_mock
|
59
|
+
Tempfile.should_receive(:new).with('archive.tar.gz').
|
60
|
+
and_return @decrypted_file_mock
|
61
|
+
::File.should_receive(:open).
|
62
|
+
with(@encrypted_file_mock,'w')
|
63
|
+
$?.stub :success? => false
|
49
64
|
|
50
|
-
|
51
|
-
|
52
|
-
|
65
|
+
command = 'gpg --batch --yes --cipher-algo AES256 --passphrase password --output /tmp/dec /tmp/enc 2>&1'
|
66
|
+
@data.should_receive(:`).with(command).and_return true
|
67
|
+
@data.decrypt_data(:data => 'crypted',
|
68
|
+
:secret => 'password').should be_false
|
69
|
+
end
|
53
70
|
end
|
54
71
|
end
|
55
72
|
|