fog 0.3.1 → 0.3.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.
- data/Gemfile.lock +1 -1
- data/README.rdoc +1 -0
- data/fog.gemspec +44 -1
- data/lib/fog.rb +2 -1
- data/lib/fog/bin.rb +2 -0
- data/lib/fog/google.rb +22 -0
- data/lib/fog/google/bin.rb +20 -0
- data/lib/fog/google/models/storage/directories.rb +43 -0
- data/lib/fog/google/models/storage/directory.rb +48 -0
- data/lib/fog/google/models/storage/file.rb +87 -0
- data/lib/fog/google/models/storage/files.rb +94 -0
- data/lib/fog/google/parsers/storage/access_control_list.rb +46 -0
- data/lib/fog/google/parsers/storage/copy_object.rb +22 -0
- data/lib/fog/google/parsers/storage/get_bucket.rb +46 -0
- data/lib/fog/google/parsers/storage/get_bucket_logging.rb +40 -0
- data/lib/fog/google/parsers/storage/get_bucket_object_versions.rb +88 -0
- data/lib/fog/google/parsers/storage/get_bucket_versioning.rb +24 -0
- data/lib/fog/google/parsers/storage/get_request_payment.rb +20 -0
- data/lib/fog/google/parsers/storage/get_service.rb +32 -0
- data/lib/fog/google/requests/storage/copy_object.rb +72 -0
- data/lib/fog/google/requests/storage/delete_bucket.rb +46 -0
- data/lib/fog/google/requests/storage/delete_object.rb +50 -0
- data/lib/fog/google/requests/storage/get_bucket.rb +110 -0
- data/lib/fog/google/requests/storage/get_bucket_acl.rb +55 -0
- data/lib/fog/google/requests/storage/get_object.rb +104 -0
- data/lib/fog/google/requests/storage/get_object_acl.rb +66 -0
- data/lib/fog/google/requests/storage/get_object_torrent.rb +55 -0
- data/lib/fog/google/requests/storage/get_object_url.rb +54 -0
- data/lib/fog/google/requests/storage/get_service.rb +53 -0
- data/lib/fog/google/requests/storage/head_object.rb +64 -0
- data/lib/fog/google/requests/storage/put_bucket.rb +68 -0
- data/lib/fog/google/requests/storage/put_bucket_acl.rb +80 -0
- data/lib/fog/google/requests/storage/put_object.rb +71 -0
- data/lib/fog/google/requests/storage/put_object_url.rb +54 -0
- data/lib/fog/google/storage.rb +192 -0
- data/spec/google/models/storage/directories_spec.rb +49 -0
- data/spec/google/models/storage/directory_spec.rb +83 -0
- data/spec/google/models/storage/file_spec.rb +121 -0
- data/spec/google/models/storage/files_spec.rb +141 -0
- data/spec/google/requests/storage/copy_object_spec.rb +61 -0
- data/spec/google/requests/storage/delete_bucket_spec.rb +35 -0
- data/spec/google/requests/storage/delete_object_spec.rb +38 -0
- data/spec/google/requests/storage/get_bucket_spec.rb +110 -0
- data/spec/google/requests/storage/get_object_spec.rb +58 -0
- data/spec/google/requests/storage/get_service_spec.rb +32 -0
- data/spec/google/requests/storage/head_object_spec.rb +26 -0
- data/spec/google/requests/storage/put_bucket_spec.rb +21 -0
- data/spec/google/requests/storage/put_object_spec.rb +43 -0
- metadata +45 -2
@@ -0,0 +1,53 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
require 'fog/google/parsers/storage/get_service'
|
7
|
+
|
8
|
+
# List information about Google Storage buckets for authorized user
|
9
|
+
#
|
10
|
+
# ==== Returns
|
11
|
+
# * response<~Excon::Response>:
|
12
|
+
# * body<~Hash>:
|
13
|
+
# * 'Buckets'<~Hash>:
|
14
|
+
# * 'Name'<~String> - Name of bucket
|
15
|
+
# * 'CreationTime'<~Time> - Timestamp of bucket creation
|
16
|
+
# * 'Owner'<~Hash>:
|
17
|
+
# * 'DisplayName'<~String> - Display name of bucket owner
|
18
|
+
# * 'ID'<~String> - Id of bucket owner
|
19
|
+
def get_service
|
20
|
+
request({
|
21
|
+
:expects => 200,
|
22
|
+
:headers => {},
|
23
|
+
:host => @host,
|
24
|
+
:idempotent => true,
|
25
|
+
:method => 'GET',
|
26
|
+
:parser => Fog::Parsers::Google::Storage::GetService.new,
|
27
|
+
:url => @host
|
28
|
+
})
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Mock
|
34
|
+
|
35
|
+
def get_service
|
36
|
+
response = Excon::Response.new
|
37
|
+
response.headers['Status'] = 200
|
38
|
+
buckets = @data[:buckets].values.map do |bucket|
|
39
|
+
bucket.reject do |key, value|
|
40
|
+
!['CreationDate', 'Name'].include?(key)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
response.body = {
|
44
|
+
'Buckets' => buckets,
|
45
|
+
'Owner' => { 'DisplayName' => 'owner', 'ID' => 'some_id'}
|
46
|
+
}
|
47
|
+
response
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Get headers for an object from Google Storage
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * bucket_name<~String> - Name of bucket to read from
|
10
|
+
# * object_name<~String> - Name of object to read
|
11
|
+
# * options<~Hash>:
|
12
|
+
# * 'If-Match'<~String> - Returns object only if its etag matches this value, otherwise returns 412 (Precondition Failed).
|
13
|
+
# * 'If-Modified-Since'<~Time> - Returns object only if it has been modified since this time, otherwise returns 304 (Not Modified).
|
14
|
+
# * 'If-None-Match'<~String> - Returns object only if its etag differs from this value, otherwise returns 304 (Not Modified)
|
15
|
+
# * 'If-Unmodified-Since'<~Time> - Returns object only if it has not been modified since this time, otherwise returns 412 (Precodition Failed).
|
16
|
+
# * 'Range'<~String> - Range of object to download
|
17
|
+
# * 'versionId'<~String> - specify a particular version to retrieve
|
18
|
+
#
|
19
|
+
# ==== Returns
|
20
|
+
# * response<~Excon::Response>:
|
21
|
+
# * body<~String> - Contents of object
|
22
|
+
# * headers<~Hash>:
|
23
|
+
# * 'Content-Length'<~String> - Size of object contents
|
24
|
+
# * 'Content-Type'<~String> - MIME type of object
|
25
|
+
# * 'ETag'<~String> - Etag of object
|
26
|
+
# * 'Last-Modified'<~String> - Last modified timestamp for object
|
27
|
+
def head_object(bucket_name, object_name, options={})
|
28
|
+
unless bucket_name
|
29
|
+
raise ArgumentError.new('bucket_name is required')
|
30
|
+
end
|
31
|
+
unless object_name
|
32
|
+
raise ArgumentError.new('object_name is required')
|
33
|
+
end
|
34
|
+
if version_id = options.delete('versionId')
|
35
|
+
query = {'versionId' => version_id}
|
36
|
+
end
|
37
|
+
headers = {}
|
38
|
+
headers['If-Modified-Since'] = options['If-Modified-Since'].utc.strftime("%a, %d %b %Y %H:%M:%S +0000") if options['If-Modified-Since']
|
39
|
+
headers['If-Unmodified-Since'] = options['If-Unmodified-Since'].utc.strftime("%a, %d %b %Y %H:%M:%S +0000") if options['If-Modified-Since']
|
40
|
+
headers.merge!(options)
|
41
|
+
request({
|
42
|
+
:expects => 200,
|
43
|
+
:headers => headers,
|
44
|
+
:host => "#{bucket_name}.#{@host}",
|
45
|
+
:method => 'HEAD',
|
46
|
+
:path => CGI.escape(object_name),
|
47
|
+
:query => query
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Mock
|
54
|
+
|
55
|
+
def head_object(bucket_name, object_name, options = {})
|
56
|
+
response = get_object(bucket_name, object_name, options)
|
57
|
+
response.body = nil
|
58
|
+
response
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Create an Google Storage bucket
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * bucket_name<~String> - name of bucket to create
|
10
|
+
# * options<~Hash> - config arguments for bucket. Defaults to {}.
|
11
|
+
# * :location_constraint<~Symbol> - sets the location for the bucket
|
12
|
+
#
|
13
|
+
# ==== Returns
|
14
|
+
# * response<~Excon::Response>:
|
15
|
+
# * status<~Integer> - 200
|
16
|
+
def put_bucket(bucket_name, options = {})
|
17
|
+
if options['LocationConstraint']
|
18
|
+
data =
|
19
|
+
<<-DATA
|
20
|
+
<CreateBucketConfiguration>
|
21
|
+
<LocationConstraint>#{options['LocationConstraint']}</LocationConstraint>
|
22
|
+
</CreateBucketConfiguration>
|
23
|
+
DATA
|
24
|
+
else
|
25
|
+
data = nil
|
26
|
+
end
|
27
|
+
request({
|
28
|
+
:expects => 200,
|
29
|
+
:body => data,
|
30
|
+
:headers => {},
|
31
|
+
:idempotent => true,
|
32
|
+
:host => "#{bucket_name}.#{@host}",
|
33
|
+
:method => 'PUT'
|
34
|
+
})
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class Mock
|
40
|
+
|
41
|
+
def put_bucket(bucket_name, options = {})
|
42
|
+
response = Excon::Response.new
|
43
|
+
response.status = 200
|
44
|
+
bucket = {
|
45
|
+
:objects => {},
|
46
|
+
'Name' => bucket_name,
|
47
|
+
'CreationDate' => Time.now,
|
48
|
+
'Owner' => { 'DisplayName' => 'owner', 'ID' => 'some_id'},
|
49
|
+
'Payer' => 'BucketOwner'
|
50
|
+
}
|
51
|
+
if options['LocationConstraint']
|
52
|
+
bucket['LocationConstraint'] = options['LocationConstraint']
|
53
|
+
else
|
54
|
+
bucket['LocationConstraint'] = ''
|
55
|
+
end
|
56
|
+
if @data[:buckets][bucket_name].nil?
|
57
|
+
@data[:buckets][bucket_name] = bucket
|
58
|
+
else
|
59
|
+
response.status = 409
|
60
|
+
raise(Excon::Errors.status_error({:expects => 200}, response))
|
61
|
+
end
|
62
|
+
response
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Change access control list for an Google Storage bucket
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * bucket_name<~String> - name of bucket to modify
|
10
|
+
# * acl<~Hash>:
|
11
|
+
# * Owner<~Hash>:
|
12
|
+
# * ID<~String>: id of owner
|
13
|
+
# * DisplayName<~String>: display name of owner
|
14
|
+
# * AccessControlList<~Array>:
|
15
|
+
# * Grantee<~Hash>:
|
16
|
+
# * 'DisplayName'<~String> - Display name of grantee
|
17
|
+
# * 'ID'<~String> - Id of grantee
|
18
|
+
# or
|
19
|
+
# * 'EmailAddress'<~String> - Email address of grantee
|
20
|
+
# or
|
21
|
+
# * 'URI'<~String> - URI of group to grant access for
|
22
|
+
# * Permission<~String> - Permission, in [FULL_CONTROL, WRITE, WRITE_ACP, READ, READ_ACP]
|
23
|
+
def put_bucket_acl(bucket_name, acl)
|
24
|
+
data =
|
25
|
+
<<-DATA
|
26
|
+
<AccessControlPolicy>
|
27
|
+
<Owner>
|
28
|
+
<ID>#{acl['Owner']['ID']}</ID>
|
29
|
+
<DisplayName>#{acl['Owner']['DisplayName']}</DisplayName>
|
30
|
+
</Owner>
|
31
|
+
<AccessControlList>
|
32
|
+
DATA
|
33
|
+
|
34
|
+
acl['AccessControlList'].each do |grant|
|
35
|
+
data << " <Grant>"
|
36
|
+
type = case grant['Grantee'].keys.sort
|
37
|
+
when ['DisplayName', 'ID']
|
38
|
+
'CanonicalUser'
|
39
|
+
when ['EmailAddress']
|
40
|
+
'AmazonCustomerByEmail'
|
41
|
+
when ['URI']
|
42
|
+
'Group'
|
43
|
+
end
|
44
|
+
data << " <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"#{type}\">"
|
45
|
+
for key, value in grant['Grantee']
|
46
|
+
data << " <#{key}>#{value}</#{key}>"
|
47
|
+
end
|
48
|
+
data << " </Grantee>"
|
49
|
+
data << " <Permission>#{grant['Permission']}</Permission>"
|
50
|
+
data << " </Grant>"
|
51
|
+
end
|
52
|
+
|
53
|
+
data <<
|
54
|
+
<<-DATA
|
55
|
+
</AccessControlList>
|
56
|
+
</AccessControlPolicy>
|
57
|
+
DATA
|
58
|
+
|
59
|
+
request({
|
60
|
+
:body => data,
|
61
|
+
:expects => 200,
|
62
|
+
:headers => {},
|
63
|
+
:host => "#{bucket_name}.#{@host}",
|
64
|
+
:method => 'PUT',
|
65
|
+
:query => {'acl' => nil}
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
class Mock
|
72
|
+
|
73
|
+
def put_bucket_acl(bucket_name, acl)
|
74
|
+
Fog::Mock.not_implemented
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Create an object in an Google Storage bucket
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * bucket_name<~String> - Name of bucket to create object in
|
10
|
+
# * object_name<~String> - Name of object to create
|
11
|
+
# * data<~File> - File or String to create object from
|
12
|
+
# * options<~Hash>:
|
13
|
+
# * 'Cache-Control'<~String> - Caching behaviour
|
14
|
+
# * 'Content-Disposition'<~String> - Presentational information for the object
|
15
|
+
# * 'Content-Encoding'<~String> - Encoding of object data
|
16
|
+
# * 'Content-Length'<~String> - Size of object in bytes (defaults to object.read.length)
|
17
|
+
# * 'Content-MD5'<~String> - Base64 encoded 128-bit MD5 digest of message (defaults to Base64 encoded MD5 of object.read)
|
18
|
+
# * 'Content-Type'<~String> - Standard MIME type describing contents (defaults to MIME::Types.of.first)
|
19
|
+
# * 'x-goog-acl'<~String> - Permissions, must be in ['private', 'public-read', 'public-read-write', 'authenticated-read']
|
20
|
+
# * "x-goog-meta-#{name}" - Headers to be returned with object, note total size of request without body must be less than 8 KB.
|
21
|
+
#
|
22
|
+
# ==== Returns
|
23
|
+
# * response<~Excon::Response>:
|
24
|
+
# * headers<~Hash>:
|
25
|
+
# * 'ETag'<~String> - etag of new object
|
26
|
+
def put_object(bucket_name, object_name, data, options = {})
|
27
|
+
data = parse_data(data)
|
28
|
+
headers = data[:headers].merge!(options)
|
29
|
+
request({
|
30
|
+
:body => data[:body],
|
31
|
+
:expects => 200,
|
32
|
+
:headers => headers,
|
33
|
+
:host => "#{bucket_name}.#{@host}",
|
34
|
+
:idempotent => true,
|
35
|
+
:method => 'PUT',
|
36
|
+
:path => CGI.escape(object_name)
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Mock
|
43
|
+
|
44
|
+
def put_object(bucket_name, object_name, data, options = {})
|
45
|
+
data = parse_data(data)
|
46
|
+
unless data[:body].is_a?(String)
|
47
|
+
data[:body] = data[:body].read
|
48
|
+
end
|
49
|
+
response = Excon::Response.new
|
50
|
+
if (bucket = @data[:buckets][bucket_name])
|
51
|
+
response.status = 200
|
52
|
+
bucket[:objects][object_name] = {
|
53
|
+
:body => data[:body],
|
54
|
+
'ETag' => Fog::Google::Mock.etag,
|
55
|
+
'Key' => object_name,
|
56
|
+
'LastModified' => Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000"),
|
57
|
+
'Size' => data[:headers]['Content-Length'],
|
58
|
+
'StorageClass' => 'STANDARD'
|
59
|
+
}
|
60
|
+
bucket[:objects][object_name]['Content-Type'] = data[:headers]['Content-Type']
|
61
|
+
else
|
62
|
+
response.status = 404
|
63
|
+
raise(Excon::Errors.status_error({:expects => 200}, response))
|
64
|
+
end
|
65
|
+
response
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Get an expiring object url from Google Storage for putting an object
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * bucket_name<~String> - Name of bucket containing object
|
10
|
+
# * object_name<~String> - Name of object to get expiring url for
|
11
|
+
# * expires<~Time> - An expiry time for this url
|
12
|
+
#
|
13
|
+
# ==== Returns
|
14
|
+
# * response<~Excon::Response>:
|
15
|
+
# * body<~String> - url for object
|
16
|
+
#
|
17
|
+
def put_object_url(bucket_name, object_name, expires)
|
18
|
+
unless bucket_name
|
19
|
+
raise ArgumentError.new('bucket_name is required')
|
20
|
+
end
|
21
|
+
unless object_name
|
22
|
+
raise ArgumentError.new('object_name is required')
|
23
|
+
end
|
24
|
+
url({
|
25
|
+
:headers => {},
|
26
|
+
:host => "#{bucket_name}.#{@host}",
|
27
|
+
:method => 'PUT',
|
28
|
+
:path => CGI.escape(object_name)
|
29
|
+
}, expires)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class Mock
|
35
|
+
|
36
|
+
def put_object_url(bucket_name, object_name, expires)
|
37
|
+
unless bucket_name
|
38
|
+
raise ArgumentError.new('bucket_name is required')
|
39
|
+
end
|
40
|
+
unless object_name
|
41
|
+
raise ArgumentError.new('object_name is required')
|
42
|
+
end
|
43
|
+
url({
|
44
|
+
:headers => {},
|
45
|
+
:host => "#{bucket_name}.#{@host}",
|
46
|
+
:method => 'PUT',
|
47
|
+
:path => CGI.escape(object_name)
|
48
|
+
}, expires)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Fog
|
2
|
+
module Google
|
3
|
+
class Storage < Fog::Service
|
4
|
+
|
5
|
+
requires :google_storage_access_key_id, :google_storage_secret_access_key
|
6
|
+
|
7
|
+
model_path 'fog/google/models/storage'
|
8
|
+
collection :directories
|
9
|
+
model :directory
|
10
|
+
collection :files
|
11
|
+
model :file
|
12
|
+
|
13
|
+
request_path 'fog/google/requests/storage'
|
14
|
+
request :copy_object
|
15
|
+
request :delete_bucket
|
16
|
+
request :delete_object
|
17
|
+
request :get_bucket
|
18
|
+
request :get_bucket_acl
|
19
|
+
request :get_object
|
20
|
+
request :get_object_acl
|
21
|
+
request :get_object_torrent
|
22
|
+
request :get_object_url
|
23
|
+
request :get_service
|
24
|
+
request :head_object
|
25
|
+
request :put_bucket
|
26
|
+
request :put_bucket_acl
|
27
|
+
request :put_object
|
28
|
+
request :put_object_url
|
29
|
+
|
30
|
+
module Utils
|
31
|
+
|
32
|
+
def parse_data(data)
|
33
|
+
metadata = {
|
34
|
+
:body => nil,
|
35
|
+
:headers => {}
|
36
|
+
}
|
37
|
+
|
38
|
+
if data.is_a?(String)
|
39
|
+
metadata[:body] = data
|
40
|
+
metadata[:headers]['Content-Length'] = metadata[:body].size.to_s
|
41
|
+
else
|
42
|
+
filename = ::File.basename(data.path)
|
43
|
+
unless (mime_types = MIME::Types.of(filename)).empty?
|
44
|
+
metadata[:headers]['Content-Type'] = mime_types.first.content_type
|
45
|
+
end
|
46
|
+
metadata[:body] = data
|
47
|
+
metadata[:headers]['Content-Length'] = ::File.size(data.path).to_s
|
48
|
+
end
|
49
|
+
# metadata[:headers]['Content-MD5'] = Base64.encode64(Digest::MD5.digest(metadata[:body])).strip
|
50
|
+
metadata
|
51
|
+
end
|
52
|
+
|
53
|
+
def url(params, expires)
|
54
|
+
params[:headers]['Date'] = expires.to_i
|
55
|
+
query = [params[:query]].compact
|
56
|
+
query << "GoogleAccessKeyId=#{@google_storage_access_key_id}"
|
57
|
+
query << "Signature=#{CGI.escape(signature(params))}"
|
58
|
+
query << "Expires=#{params[:headers]['Date']}"
|
59
|
+
"http://#{params[:host]}/#{params[:path]}?#{query.join('&')}"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class Mock
|
65
|
+
include Utils
|
66
|
+
|
67
|
+
def self.data
|
68
|
+
@data ||= Hash.new do |hash, key|
|
69
|
+
hash[key] = {
|
70
|
+
:buckets => {}
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.reset_data(keys=data.keys)
|
76
|
+
for key in [*keys]
|
77
|
+
data.delete(key)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(options={})
|
82
|
+
@google_storage_access_key_id = options[:google_storage_access_key_id]
|
83
|
+
@data = self.class.data[@google_storage_access_key_id]
|
84
|
+
end
|
85
|
+
|
86
|
+
def signature(params)
|
87
|
+
"foo"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Real
|
92
|
+
include Utils
|
93
|
+
extend Fog::Deprecation
|
94
|
+
deprecate(:reset, :reload)
|
95
|
+
|
96
|
+
# Initialize connection to Google Storage
|
97
|
+
#
|
98
|
+
# ==== Notes
|
99
|
+
# options parameter must include values for :google_storage_access_key_id and
|
100
|
+
# :google_storage_secret_access_key in order to create a connection
|
101
|
+
#
|
102
|
+
# ==== Examples
|
103
|
+
# google_storage = Storage.new(
|
104
|
+
# :google_storage_access_key_id => your_google_storage_access_key_id,
|
105
|
+
# :google_storage_secret_access_key => your_google_storage_secret_access_key
|
106
|
+
# )
|
107
|
+
#
|
108
|
+
# ==== Parameters
|
109
|
+
# * options<~Hash> - config arguments for connection. Defaults to {}.
|
110
|
+
#
|
111
|
+
# ==== Returns
|
112
|
+
# * Storage object with connection to google.
|
113
|
+
def initialize(options={})
|
114
|
+
@google_storage_access_key_id = options[:google_storage_access_key_id]
|
115
|
+
@google_storage_secret_access_key = options[:google_storage_secret_access_key]
|
116
|
+
@hmac = Fog::HMAC.new('sha1', @google_storage_secret_access_key)
|
117
|
+
@host = options[:host] || 'commondatastorage.googleapis.com'
|
118
|
+
@port = options[:port] || 443
|
119
|
+
@scheme = options[:scheme] || 'https'
|
120
|
+
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", options[:persistent] || true)
|
121
|
+
end
|
122
|
+
|
123
|
+
def reload
|
124
|
+
@connection.reset
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def request(params, &block)
|
130
|
+
params[:headers]['Date'] = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000")
|
131
|
+
params[:headers]['Authorization'] = "GOOG1 #{@google_storage_access_key_id}:#{signature(params)}"
|
132
|
+
|
133
|
+
response = @connection.request(params, &block)
|
134
|
+
|
135
|
+
response
|
136
|
+
end
|
137
|
+
|
138
|
+
def signature(params)
|
139
|
+
string_to_sign =
|
140
|
+
<<-DATA
|
141
|
+
#{params[:method]}
|
142
|
+
#{params[:headers]['Content-MD5']}
|
143
|
+
#{params[:headers]['Content-Type']}
|
144
|
+
#{params[:headers]['Date']}
|
145
|
+
DATA
|
146
|
+
|
147
|
+
google_headers, canonical_google_headers = {}, ''
|
148
|
+
for key, value in params[:headers]
|
149
|
+
if key[0..6] == 'x-goog-'
|
150
|
+
google_headers[key] = value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
google_headers = google_headers.sort {|x, y| x[0] <=> y[0]}
|
155
|
+
for key, value in google_headers
|
156
|
+
canonical_google_headers << "#{key}:#{value}\n"
|
157
|
+
end
|
158
|
+
string_to_sign << "#{canonical_google_headers}"
|
159
|
+
|
160
|
+
subdomain = params[:host].split(".#{@host}").first
|
161
|
+
unless subdomain =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}$))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
162
|
+
Formatador.display_line("[yellow][WARN] fog: the specified google storage bucket name(#{subdomain}) is not a valid dns name. See: http://code.google.com/apis/storage/docs/developer-guide.html#naming[/]")
|
163
|
+
params[:host] = params[:host].split("#{subdomain}.")[-1]
|
164
|
+
if params[:path]
|
165
|
+
params[:path] = "#{subdomain}/#{params[:path]}"
|
166
|
+
else
|
167
|
+
params[:path] = "#{subdomain}"
|
168
|
+
end
|
169
|
+
subdomain = nil
|
170
|
+
end
|
171
|
+
|
172
|
+
canonical_resource = "/"
|
173
|
+
unless subdomain.nil? || subdomain == @host
|
174
|
+
canonical_resource << "#{CGI.escape(subdomain).downcase}/"
|
175
|
+
end
|
176
|
+
canonical_resource << "#{params[:path]}"
|
177
|
+
canonical_resource << '?'
|
178
|
+
for key in (params[:query] || {}).keys
|
179
|
+
if ['acl', 'location', 'logging', 'requestPayment', 'torrent', 'versions', 'versioning'].include?(key)
|
180
|
+
canonical_resource << "#{key}&"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
canonical_resource.chop!
|
184
|
+
string_to_sign << "#{canonical_resource}"
|
185
|
+
|
186
|
+
signed_string = @hmac.sign(string_to_sign)
|
187
|
+
signature = Base64.encode64(signed_string).chomp!
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|