fog 0.0.12 → 0.0.13
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/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
|