fog 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/bin/fog +3 -1
- data/fog.gemspec +2 -2
- data/lib/fog/aws/models/s3/objects.rb +4 -0
- data/lib/fog/aws/requests/s3/copy_object.rb +1 -1
- data/lib/fog/aws/requests/s3/delete_object.rb +1 -1
- data/lib/fog/aws/requests/s3/get_object.rb +16 -1
- data/lib/fog/aws/requests/s3/head_object.rb +1 -1
- data/lib/fog/aws/requests/s3/put_object.rb +1 -1
- data/lib/fog/aws/s3.rb +40 -18
- data/lib/fog/connection.rb +40 -33
- data/spec/aws/models/s3/objects_spec.rb +12 -0
- data/spec/aws/requests/s3/get_bucket_spec.rb +6 -0
- data/spec/aws/requests/s3/get_object_spec.rb +6 -0
- data/spec/spec_helper.rb +2 -1
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.13
|
data/bin/fog
CHANGED
@@ -3,7 +3,9 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'fog')
|
|
3
3
|
require 'irb'
|
4
4
|
require 'yaml'
|
5
5
|
|
6
|
-
if File.exists?(File.expand_path(
|
6
|
+
if ARGV[0] && File.exists?(File.expand_path(ARGV[0]))
|
7
|
+
@credentials = YAML.load(File.open(File.expand_path(ARGV[0])).read)
|
8
|
+
elsif File.exists?(File.expand_path('~/.fog'))
|
7
9
|
@credentials = YAML.load(File.open(File.expand_path('~/.fog')).read)
|
8
10
|
end
|
9
11
|
|
data/fog.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{fog}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.13"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wesley Beary"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-05}
|
13
13
|
s.default_executable = %q{fog}
|
14
14
|
s.description = %q{brings clouds to you}
|
15
15
|
s.email = %q{me@geemus.com}
|
@@ -39,11 +39,26 @@ unless Fog.mocking?
|
|
39
39
|
:headers => headers,
|
40
40
|
:host => "#{bucket_name}.#{@host}",
|
41
41
|
:method => 'GET',
|
42
|
-
:path => object_name,
|
42
|
+
:path => CGI.escape(object_name),
|
43
43
|
:block => block
|
44
44
|
})
|
45
45
|
end
|
46
46
|
|
47
|
+
def get_object_url(bucket_name, object_name, expires)
|
48
|
+
unless bucket_name
|
49
|
+
raise ArgumentError.new('bucket_name is required')
|
50
|
+
end
|
51
|
+
unless object_name
|
52
|
+
raise ArgumentError.new('object_name is required')
|
53
|
+
end
|
54
|
+
url({
|
55
|
+
:headers => {},
|
56
|
+
:host => "#{bucket_name}.#{@host}",
|
57
|
+
:method => 'GET',
|
58
|
+
:path => object_name
|
59
|
+
}, expires)
|
60
|
+
end
|
61
|
+
|
47
62
|
end
|
48
63
|
end
|
49
64
|
end
|
data/lib/fog/aws/s3.rb
CHANGED
@@ -55,7 +55,7 @@ module Fog
|
|
55
55
|
# :aws_secret_access_key in order to create a connection
|
56
56
|
#
|
57
57
|
# ==== Examples
|
58
|
-
#
|
58
|
+
# s3 = S3.new(
|
59
59
|
# :aws_access_key_id => your_aws_access_key_id,
|
60
60
|
# :aws_secret_access_key => your_aws_secret_access_key
|
61
61
|
# )
|
@@ -100,7 +100,33 @@ module Fog
|
|
100
100
|
|
101
101
|
def request(params)
|
102
102
|
params[:headers]['Date'] = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000")
|
103
|
+
params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature(params)}"
|
103
104
|
|
105
|
+
response = @connection.request({
|
106
|
+
:block => params[:block],
|
107
|
+
:body => params[:body],
|
108
|
+
:expects => params[:expects],
|
109
|
+
:headers => params[:headers],
|
110
|
+
:host => params[:host],
|
111
|
+
:method => params[:method],
|
112
|
+
:parser => params[:parser],
|
113
|
+
:path => params[:path],
|
114
|
+
:query => params[:query]
|
115
|
+
})
|
116
|
+
|
117
|
+
response
|
118
|
+
end
|
119
|
+
|
120
|
+
def url(params, expires)
|
121
|
+
params[:headers]['Date'] = expires.to_i
|
122
|
+
query = [params[:query]].compact
|
123
|
+
query << "AWSAccessKeyId=#{@aws_access_key_id}"
|
124
|
+
query << "Signature=#{CGI.escape(signature(params))}"
|
125
|
+
query << "Expires=#{params[:headers]['Date']}"
|
126
|
+
"http://#{params[:host]}/#{params[:path]}?#{query.join('&')}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def signature(params)
|
104
130
|
string_to_sign =
|
105
131
|
<<-DATA
|
106
132
|
#{params[:method]}
|
@@ -121,9 +147,20 @@ DATA
|
|
121
147
|
end
|
122
148
|
string_to_sign << "#{canonical_amz_headers}"
|
123
149
|
|
124
|
-
canonical_resource = "/"
|
125
150
|
subdomain = params[:host].split(".#{@host}").first
|
126
|
-
unless subdomain
|
151
|
+
unless subdomain =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\.\d{1,3}){3}$))(?:[a-z0-9]|\.(?![\.\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
152
|
+
puts("[WARN] fog: the specified s3 bucket name(#{subdomain}) is not a valid dns name. See: http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?Introduction.html")
|
153
|
+
params[:host] = params[:host].split("#{subdomain}.")[-1]
|
154
|
+
if params[:path]
|
155
|
+
params[:path] = "#{subdomain}/#{params[:path]}"
|
156
|
+
else
|
157
|
+
params[:path] = "#{subdomain}"
|
158
|
+
end
|
159
|
+
subdomain = nil
|
160
|
+
end
|
161
|
+
|
162
|
+
canonical_resource = "/"
|
163
|
+
unless subdomain.nil? || subdomain == @host
|
127
164
|
canonical_resource << "#{CGI.escape(subdomain).downcase}/"
|
128
165
|
end
|
129
166
|
canonical_resource << "#{params[:path]}"
|
@@ -134,21 +171,6 @@ DATA
|
|
134
171
|
|
135
172
|
hmac = @hmac.update(string_to_sign)
|
136
173
|
signature = Base64.encode64(hmac.digest).chomp!
|
137
|
-
params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
|
138
|
-
|
139
|
-
response = @connection.request({
|
140
|
-
:block => params[:block],
|
141
|
-
:body => params[:body],
|
142
|
-
:expects => params[:expects],
|
143
|
-
:headers => params[:headers],
|
144
|
-
:host => params[:host],
|
145
|
-
:method => params[:method],
|
146
|
-
:parser => params[:parser],
|
147
|
-
:path => params[:path],
|
148
|
-
:query => params[:query]
|
149
|
-
})
|
150
|
-
|
151
|
-
response
|
152
174
|
end
|
153
175
|
|
154
176
|
end
|
data/lib/fog/connection.rb
CHANGED
@@ -17,13 +17,20 @@ unless Fog.mocking?
|
|
17
17
|
|
18
18
|
def initialize(url)
|
19
19
|
@uri = URI.parse(url)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@connection
|
25
|
-
|
26
|
-
@connection.
|
20
|
+
end
|
21
|
+
|
22
|
+
def connection
|
23
|
+
if @connection && !@connection.closed?
|
24
|
+
@connection
|
25
|
+
else
|
26
|
+
@connection = TCPSocket.open(@uri.host, @uri.port)
|
27
|
+
if @uri.scheme == 'https'
|
28
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
29
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
30
|
+
@connection = OpenSSL::SSL::SSLSocket.new(@connection, @ssl_context)
|
31
|
+
@connection.sync_close = true
|
32
|
+
@connection.connect
|
33
|
+
end
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -45,7 +52,7 @@ unless Fog.mocking?
|
|
45
52
|
request << "#{key}: #{value}\r\n"
|
46
53
|
end
|
47
54
|
request << "\r\n"
|
48
|
-
|
55
|
+
connection.write(request)
|
49
56
|
|
50
57
|
if params[:body]
|
51
58
|
if params[:body].is_a?(String)
|
@@ -54,18 +61,18 @@ unless Fog.mocking?
|
|
54
61
|
body = params[:body]
|
55
62
|
end
|
56
63
|
while chunk = body.read(CHUNK_SIZE)
|
57
|
-
|
64
|
+
connection.write(chunk)
|
58
65
|
end
|
59
66
|
end
|
60
67
|
|
61
68
|
response = Fog::Response.new
|
62
69
|
response.request = params
|
63
|
-
response.status =
|
70
|
+
response.status = connection.readline[9..11].to_i
|
64
71
|
if params[:expects] && params[:expects] != response.status
|
65
72
|
error = true
|
66
73
|
end
|
67
74
|
while true
|
68
|
-
data =
|
75
|
+
data = connection.readline.chomp!
|
69
76
|
if data == ""
|
70
77
|
break
|
71
78
|
end
|
@@ -73,35 +80,35 @@ unless Fog.mocking?
|
|
73
80
|
response.headers[capitalize(header[0])] = header[1]
|
74
81
|
end
|
75
82
|
|
76
|
-
|
77
|
-
if error
|
78
|
-
|
79
|
-
|
80
|
-
|
83
|
+
unless params[:method] == 'HEAD'
|
84
|
+
if error || params[:parser]
|
85
|
+
if error
|
86
|
+
parser = Fog::Errors::Parser.new
|
87
|
+
elsif params[:parser]
|
88
|
+
parser = params[:parser]
|
89
|
+
end
|
90
|
+
body = Nokogiri::XML::SAX::PushParser.new(parser)
|
91
|
+
elsif params[:block]
|
92
|
+
body = nil
|
93
|
+
else
|
94
|
+
body = ''
|
81
95
|
end
|
82
|
-
body = Nokogiri::XML::SAX::PushParser.new(parser)
|
83
|
-
elsif params[:block]
|
84
|
-
body = nil
|
85
|
-
else
|
86
|
-
body = ''
|
87
|
-
end
|
88
96
|
|
89
|
-
unless params[:method] == 'HEAD'
|
90
97
|
if response.headers['Content-Length']
|
91
98
|
if error || !params[:block]
|
92
|
-
body <<
|
99
|
+
body << connection.read(response.headers['Content-Length'].to_i)
|
93
100
|
else
|
94
101
|
remaining = response.headers['Content-Length'].to_i
|
95
102
|
while remaining > 0
|
96
|
-
params[:block].call(
|
103
|
+
params[:block].call(connection.read([CHUNK_SIZE, remaining].min))
|
97
104
|
remaining -= CHUNK_SIZE;
|
98
105
|
end
|
99
106
|
end
|
100
107
|
elsif response.headers['Transfer-Encoding'] == 'chunked'
|
101
108
|
while true
|
102
109
|
# 2 == "/r/n".length
|
103
|
-
chunk_size =
|
104
|
-
chunk =
|
110
|
+
chunk_size = connection.readline.chomp!.to_i(16) + 2
|
111
|
+
chunk = connection.read(chunk_size)[0...-2]
|
105
112
|
if error || !params[:block]
|
106
113
|
body << chunk
|
107
114
|
else
|
@@ -112,13 +119,13 @@ unless Fog.mocking?
|
|
112
119
|
end
|
113
120
|
end
|
114
121
|
end
|
115
|
-
end
|
116
122
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
123
|
+
if parser
|
124
|
+
body.finish
|
125
|
+
response.body = parser.response
|
126
|
+
else
|
127
|
+
response.body = body
|
128
|
+
end
|
122
129
|
end
|
123
130
|
|
124
131
|
if error
|
@@ -90,6 +90,18 @@ describe 'Fog::AWS::S3::Objects' do
|
|
90
90
|
|
91
91
|
end
|
92
92
|
|
93
|
+
describe "#get_url" do
|
94
|
+
|
95
|
+
it "should return a signed expiring url" do
|
96
|
+
file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r')
|
97
|
+
object = @bucket.objects.create(:key => 'fogobjectname', :body => file)
|
98
|
+
url = @bucket.objects.get_url('fogobjectname', Time.now + 60 * 10)
|
99
|
+
open(url).read.should == File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r').read
|
100
|
+
object.destroy
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
93
105
|
describe "#head" do
|
94
106
|
|
95
107
|
it "should return a Fog::AWS::S3::Object with metadata" do
|
@@ -106,5 +106,11 @@ describe 'S3.get_bucket' do
|
|
106
106
|
}.should raise_error(Fog::Errors::NotFound)
|
107
107
|
end
|
108
108
|
|
109
|
+
it 'should request non-subdomain buckets and raise a NotFound error' do
|
110
|
+
lambda {
|
111
|
+
s3.get_bucket('A-invalid--name')
|
112
|
+
}.should raise_error(Fog::Errors::NotFound)
|
113
|
+
end
|
114
|
+
|
109
115
|
end
|
110
116
|
end
|
@@ -35,6 +35,12 @@ describe 'S3.get_object' do
|
|
35
35
|
data.should == file.read
|
36
36
|
end
|
37
37
|
|
38
|
+
it 'should return a signed expiring url' do
|
39
|
+
url = s3.get_object_url('foggetobject', 'fog_get_object', Time.now + 60 * 10)
|
40
|
+
file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r')
|
41
|
+
open(url).read.should == file.read
|
42
|
+
end
|
43
|
+
|
38
44
|
end
|
39
45
|
describe 'failure' do
|
40
46
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec'
|
2
|
+
require 'open-uri'
|
2
3
|
|
3
4
|
current_directory = File.dirname(__FILE__)
|
4
5
|
require "#{current_directory}/../lib/fog"
|
@@ -6,7 +7,7 @@ require "#{current_directory}/../lib/fog"
|
|
6
7
|
|
7
8
|
def credentials
|
8
9
|
@credentials ||= begin
|
9
|
-
credentials_path =
|
10
|
+
credentials_path = File.expand_path('~/.fog')
|
10
11
|
credentials_data = File.open(credentials_path).read
|
11
12
|
YAML.load(credentials_data)
|
12
13
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wesley Beary
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-05 00:00:00 -07:00
|
13
13
|
default_executable: fog
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|