ruby-atmos 0.6.0 → 1.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.
- data/Rakefile +1 -1
- data/lib/atmos.rb +35 -0
- data/lib/atmos/object.rb +88 -30
- data/lib/atmos/request.rb +41 -8
- data/lib/atmos/response.rb +116 -0
- data/lib/atmos/rest.rb +37 -146
- data/lib/atmos/store.rb +17 -6
- data/lib/atmos/version.rb +1 -1
- data/test/suite.rb +2 -0
- data/test/test_object_create.rb +16 -1
- data/test/test_object_get.rb +55 -0
- data/test/test_object_misc.rb +1 -1
- data/test/test_object_read.rb +21 -37
- data/test/test_object_update.rb +2 -1
- data/test/{request_test.rb → test_request.rb} +1 -2
- data/test/test_store.rb +1 -1
- metadata +11 -7
data/Rakefile
CHANGED
@@ -45,7 +45,7 @@ spec = Gem::Specification.new do |s|
|
|
45
45
|
s.extra_rdoc_files = %w(README COPYING)
|
46
46
|
s.homepage = 'http://www.emc.com/atmos'
|
47
47
|
#s.rubyforge_project = 'atmos'
|
48
|
-
s.files = FileList['Rakefile', 'lib/*/*.rb']
|
48
|
+
s.files = FileList['Rakefile', 'lib/atmos.rb', 'lib/*/*.rb']
|
49
49
|
s.test_files = Dir['test/**/*']
|
50
50
|
s.require_path = 'lib'
|
51
51
|
s.add_dependency('log4r', '>=1.1.9')
|
data/lib/atmos.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'log4r'
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'net/https'
|
6
|
+
require 'time'
|
7
|
+
require 'base64'
|
8
|
+
require 'hmac-sha1'
|
9
|
+
|
10
|
+
|
11
|
+
Log4r::Logger.new('atmos')
|
12
|
+
Log4r::Logger.get('atmos').add(Log4r::StderrOutputter.new 'console')
|
13
|
+
Log4r::Logger.get('atmos').level = Log4r::FATAL
|
14
|
+
#Log4r::Logger.get('atmos').level = Log4r::WARN
|
15
|
+
#Log4r::Logger.get('atmos').level = Log4r::INFO
|
16
|
+
#Log4r::Logger.get('atmos').level = Log4r::DEBUG
|
17
|
+
|
18
|
+
|
19
|
+
$:.unshift(File.dirname(__FILE__))
|
20
|
+
require 'atmos/parser'
|
21
|
+
require 'atmos/version'
|
22
|
+
require 'atmos/util'
|
23
|
+
require 'atmos/exceptions'
|
24
|
+
require 'atmos/request'
|
25
|
+
require 'atmos/response'
|
26
|
+
require 'atmos/rest'
|
27
|
+
require 'atmos/store'
|
28
|
+
require 'atmos/attributes'
|
29
|
+
require 'atmos/object'
|
30
|
+
|
31
|
+
#
|
32
|
+
# This sets the default, but it can still be overridden by setting the parser elsewhere.
|
33
|
+
#
|
34
|
+
Atmos::Parser::parser = (File.exists?(File.join(File.dirname(__FILE__), 'atmos/parser/rexml.rb'))) ? Atmos::Parser::REXML : Atmos::Parser::NOKOGIRI
|
35
|
+
Atmos::LOG.warn("parser: #{Atmos::Parser::parser}")
|
data/lib/atmos/object.rb
CHANGED
@@ -75,23 +75,13 @@ module Atmos
|
|
75
75
|
class Object
|
76
76
|
attr_reader :aoid, :request, :user # :nodoc:
|
77
77
|
|
78
|
-
# Hash-like object containing user access control properties of the object.
|
79
|
-
attr_reader :user_acl
|
80
|
-
|
81
|
-
# Hash-like object containing group access control properties of the object.
|
82
|
-
attr_reader :group_acl
|
83
|
-
|
84
|
-
# Hash-like object containing non-listable metadata associated with the object.
|
85
|
-
attr_reader :metadata
|
86
|
-
|
87
|
-
# Hash-like object containing listable metadata associated with the object.
|
88
|
-
attr_reader :listable_metadata
|
89
|
-
|
90
|
-
# Hash-like object containing read-only system metadata associated with the object.
|
91
|
-
attr_reader :system_metadata
|
92
|
-
|
93
78
|
@request = nil
|
94
79
|
@checksum = nil
|
80
|
+
@user_acl = nil
|
81
|
+
@group_acl = nil
|
82
|
+
@metadata = nil
|
83
|
+
@system_metadata = nil
|
84
|
+
@listable_metadata = nil
|
95
85
|
|
96
86
|
|
97
87
|
#
|
@@ -99,9 +89,10 @@ module Atmos
|
|
99
89
|
# with an Atmos::Store object:
|
100
90
|
#
|
101
91
|
# obj = store.create
|
102
|
-
# obj = store.get(obj_id)
|
92
|
+
# obj = store.get(:id => obj_id)
|
93
|
+
# obj = store.get(:namespace => obj_id)
|
103
94
|
#
|
104
|
-
def initialize(store,
|
95
|
+
def initialize(store, action, options = {})
|
105
96
|
Atmos::LOG.debug("obj.new options: #{options.inspect}")
|
106
97
|
validate_options(options)
|
107
98
|
|
@@ -111,20 +102,85 @@ module Atmos
|
|
111
102
|
@request = Atmos::Request.new(:store => @store)
|
112
103
|
@user = @store.user
|
113
104
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
105
|
+
if (action == :create)
|
106
|
+
|
107
|
+
if (options[:id])
|
108
|
+
raise Atmos::Exceptions::ArgumentException, "You can't specify an id on object creation."
|
109
|
+
end
|
110
|
+
|
111
|
+
Atmos::LOG.debug("Object.new: creating new object")
|
112
|
+
response = @request.do(:create_object, options)
|
113
|
+
@aoid = response.id
|
114
|
+
|
115
|
+
elsif (action == :get)
|
116
|
+
|
117
|
+
Atmos::LOG.debug("Retrieving object: id: #{options[:id]}; namespace: #{options[:namespace]}")
|
118
|
+
response = @request.do(:get_object_info, options)
|
119
|
+
@aoid = Atmos::Parser::response_get_string(response.http_response, '//xmlns:objectId')
|
120
|
+
Atmos::LOG.debug("Retrieved object id for namespace: #{@aoid}")
|
119
121
|
end
|
120
122
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Lazy evaluation of hash-like object containing user access control properties of the object.
|
127
|
+
#
|
128
|
+
def user_acl
|
129
|
+
#Atmos::LOG.warn("user_acl: #{@user_acl.inspect}")
|
130
|
+
if (@user_acl.nil?)
|
131
|
+
@user_acl = Atmos::ACL.new(self, Atmos::ACL::USER)
|
132
|
+
#Atmos::LOG.warn("user_acl: #{@user_acl.inspect}")
|
133
|
+
end
|
134
|
+
|
135
|
+
@user_acl
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
#
|
140
|
+
# Lazy evaluation of hash-like object containing group access control properties of the object.
|
141
|
+
#
|
142
|
+
def group_acl
|
143
|
+
if (@group_acl.nil?)
|
144
|
+
@group_acl = Atmos::ACL.new(self, Atmos::ACL::GROUP)
|
145
|
+
end
|
146
|
+
|
147
|
+
@group_acl
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
#
|
152
|
+
# Lazy evaluation of hash-like object containing non-listable properties of the object.
|
153
|
+
#
|
154
|
+
def metadata
|
155
|
+
if (@metadata.nil?)
|
156
|
+
@metadata = Atmos::Metadata.new(self, Atmos::Metadata::NON_LISTABLE)
|
157
|
+
end
|
158
|
+
|
159
|
+
@metadata
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
#
|
164
|
+
# Lazy evaluation of hash-like object containing listable metadata properties of the object.
|
165
|
+
#
|
166
|
+
def listable_metadata
|
167
|
+
if (@listable_metadata.nil?)
|
168
|
+
@listable_metadata = Atmos::Metadata.new(self, Atmos::Metadata::LISTABLE)
|
169
|
+
end
|
170
|
+
|
171
|
+
@listable_metadata
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
#
|
176
|
+
# Lazy evaluation of Hash-like object containing read-only system metadata associated with the object.
|
177
|
+
#
|
178
|
+
def system_metadata
|
179
|
+
if (@system_metadata.nil?)
|
180
|
+
@system_metadata = Atmos::Metadata.new(self, Atmos::Metadata::SYSTEM)
|
181
|
+
end
|
182
|
+
|
183
|
+
@system_metadata
|
128
184
|
end
|
129
185
|
|
130
186
|
|
@@ -230,7 +286,7 @@ module Atmos
|
|
230
286
|
def validate_options(options)
|
231
287
|
invalid_values = []
|
232
288
|
|
233
|
-
valid_options = [:checksum, :user_acl, :group_acl, :metadata, :listable_metadata, :mimetype, :length, :data].freeze
|
289
|
+
valid_options = [:checksum, :user_acl, :group_acl, :metadata, :listable_metadata, :mimetype, :length, :data, :namespace, :id].freeze
|
234
290
|
invalid_options = options.keys - valid_options
|
235
291
|
raise Atmos::Exceptions::ArgumentException, "Unrecognized options: #{invalid_options.inspect}" if (!invalid_options.empty?)
|
236
292
|
|
@@ -248,6 +304,8 @@ module Atmos
|
|
248
304
|
invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Hash))
|
249
305
|
when :mimetype
|
250
306
|
invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(String))
|
307
|
+
when :namespace
|
308
|
+
invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(String))
|
251
309
|
when :length
|
252
310
|
invalid_values.push(k) if (options[k].nil? || !options[k].kind_of?(Integer))
|
253
311
|
when :data
|
data/lib/atmos/request.rb
CHANGED
@@ -14,6 +14,43 @@ module Atmos
|
|
14
14
|
@http = options[:store].http
|
15
15
|
@tag = options[:default_tag]
|
16
16
|
end
|
17
|
+
|
18
|
+
|
19
|
+
#
|
20
|
+
# If there is no namespace option provided, we're using the object interface,
|
21
|
+
# otherwise we're using the namespace interface. Determine which it is, so we know
|
22
|
+
# which URI to use.
|
23
|
+
#
|
24
|
+
# If aoid is nil, we're creating a new object regardless.
|
25
|
+
#
|
26
|
+
def get_uri(action, options)
|
27
|
+
|
28
|
+
if (options.has_key?(:namespace) && action.has_key?(:namespace_uri))
|
29
|
+
|
30
|
+
# only two actions need namespace, so if the action doesn't have the
|
31
|
+
# :namespace_url key, we use the regular :object_url.
|
32
|
+
ns = (options[:namespace].start_with?('/')) ? options[:namespace][1..-1] : options[:namespace]
|
33
|
+
uri = action[:namespace_uri].sub(/:namespace/, ns)
|
34
|
+
|
35
|
+
else
|
36
|
+
|
37
|
+
uri = action[:object_uri]
|
38
|
+
|
39
|
+
# only need the following check/substution if the uri requires an id
|
40
|
+
if (!uri.index(':id').nil?)
|
41
|
+
|
42
|
+
if (options.has_key?(:id) && !options[:id].nil?)
|
43
|
+
uri = uri.sub(/:id/, options[:id])
|
44
|
+
else
|
45
|
+
raise Atmos::Exceptions::ArgumentException, "An id is required for this action. This is an internal error."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Atmos::LOG.info("uri: #{uri}")
|
51
|
+
uri
|
52
|
+
end
|
53
|
+
|
17
54
|
|
18
55
|
|
19
56
|
def do(actionname, options = {}, &block)
|
@@ -22,14 +59,10 @@ module Atmos
|
|
22
59
|
raise Atmos::Exceptions::InternalLibraryException,
|
23
60
|
"Invalid REST action: #{actionname}" if (REST[actionname].nil?)
|
24
61
|
|
25
|
-
action
|
26
|
-
uri
|
27
|
-
url
|
28
|
-
verb
|
29
|
-
|
30
|
-
if (options[:id].nil? && !action[:uri].index(':id').nil?)
|
31
|
-
raise Atmos::Exceptions::ArgumentException, "An id is required for this action (#{actionname})."
|
32
|
-
end
|
62
|
+
action = REST[actionname]
|
63
|
+
uri = get_uri(action, options)
|
64
|
+
url = URI::join(@baseurl.to_s, uri.to_s)
|
65
|
+
verb = action[:verb]
|
33
66
|
|
34
67
|
request = (verbs.include?(verb)) ? Net::HTTP.const_get(verb.to_s.capitalize).new(uri) : nil
|
35
68
|
raise Atmos::Exceptions::AtmosException,
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Atmos
|
2
|
+
class Response # :nodoc:
|
3
|
+
attr_reader :http_response
|
4
|
+
|
5
|
+
def initialize(response, action)
|
6
|
+
@http_response = response
|
7
|
+
@action = action
|
8
|
+
|
9
|
+
if (response.kind_of?(Net::HTTPServerError))
|
10
|
+
raise Atmos::Exceptions::ServerException, response.message
|
11
|
+
elsif (response.kind_of?(Net::HTTPClientError))
|
12
|
+
|
13
|
+
raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
|
14
|
+
|
15
|
+
Atmos::LOG.debug("#{response.class}")
|
16
|
+
Atmos::LOG.debug("#{response.body}")
|
17
|
+
|
18
|
+
Atmos::Parser::response_check_action_error(@action, response)
|
19
|
+
|
20
|
+
elsif (!response.kind_of?(Net::HTTPSuccess))
|
21
|
+
raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(header)
|
27
|
+
if (!self[header].nil?)
|
28
|
+
self[header].split(',').each do |elt|
|
29
|
+
if (!elt.index('=').nil?)
|
30
|
+
key,val = elt.split('=')
|
31
|
+
yield key.strip, val.strip
|
32
|
+
else
|
33
|
+
yield elt.strip
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
@http_response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(sym, *args)
|
44
|
+
Atmos::LOG.info("method missing: #{sym}")
|
45
|
+
header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
|
46
|
+
if (REST[@action][:return_headers].include?(header))
|
47
|
+
Atmos::LOG.info("header: #{header}")
|
48
|
+
rv = Atmos::Util.header2hash(header, @http_response[header])
|
49
|
+
Atmos::LOG.debug("rv: #{rv.inspect}")
|
50
|
+
return rv
|
51
|
+
else
|
52
|
+
return super.method_missing(sym, *args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def delta
|
57
|
+
if (REST[@action][:return_headers].include?('x-emc-delta'))
|
58
|
+
@http_response['x-emc-delta']
|
59
|
+
else
|
60
|
+
raise "A #{@action} request doesn't return a delta."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def policy
|
65
|
+
if (REST[@action][:return_headers].include?('x-emc-policy'))
|
66
|
+
@http_response['x-emc-policy']
|
67
|
+
else
|
68
|
+
raise "A #{@action} request doesn't return a policy description."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def id
|
73
|
+
if (REST[@action][:return_headers].include?('location'))
|
74
|
+
@http_response['location'][@http_response['location'].rindex('/')+1..-1]
|
75
|
+
else
|
76
|
+
raise "A #{@action} request doesn't return an id."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def headers
|
81
|
+
headers = {}
|
82
|
+
@http_response.each do |header, value|
|
83
|
+
headers[header] = value
|
84
|
+
end
|
85
|
+
headers
|
86
|
+
end
|
87
|
+
|
88
|
+
def [](header)
|
89
|
+
@http_response[header]
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
# while (response.kind_of?(Net::HTTPRedirection))
|
100
|
+
|
101
|
+
#From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
|
102
|
+
# rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
|
103
|
+
|
104
|
+
# puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
|
105
|
+
|
106
|
+
# if rurl.start_with?('/')
|
107
|
+
# puts("Got a relative redirect url: #{options[:url]}")
|
108
|
+
# options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
|
109
|
+
# end
|
110
|
+
|
111
|
+
# options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
|
112
|
+
# response = self.generic_request(options)
|
113
|
+
# end
|
114
|
+
# raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
|
115
|
+
|
116
|
+
end
|
data/lib/atmos/rest.rb
CHANGED
@@ -1,62 +1,62 @@
|
|
1
1
|
module Atmos
|
2
2
|
REST = {
|
3
3
|
:listable_tags => { :verb => :get,
|
4
|
-
:
|
4
|
+
:object_uri => '/rest/objects?listabletags',
|
5
5
|
:required_headers => ['x-emc-tags'],
|
6
6
|
:optional_headers => ['x-emc-token'],
|
7
7
|
:http_response => 200,
|
8
8
|
:return_headers => ['x-emc-listable-tags'],
|
9
9
|
},
|
10
10
|
:set_group_acl => { :verb => :post,
|
11
|
-
:
|
11
|
+
:object_uri => '/rest/objects/:id?acl',
|
12
12
|
:required_headers => ['x-emc-groupacl'],
|
13
13
|
:http_response => 200,
|
14
14
|
},
|
15
15
|
:set_user_acl => { :verb => :post,
|
16
|
-
:
|
16
|
+
:object_uri => '/rest/objects/:id?acl',
|
17
17
|
:required_headers => ['x-emc-useracl'],
|
18
18
|
:http_response => 200,
|
19
19
|
},
|
20
20
|
:set_metadata => { :verb => :post,
|
21
|
-
:
|
21
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
22
22
|
:http_response => 200,
|
23
23
|
:required_headers => ['x-emc-meta'],
|
24
24
|
},
|
25
|
-
:set_listable_metadata => { :verb
|
26
|
-
:
|
25
|
+
:set_listable_metadata => { :verb => :post,
|
26
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
27
27
|
:http_response => 200,
|
28
28
|
:required_headers => ['x-emc-listable-meta'],
|
29
29
|
},
|
30
30
|
:delete_metadata => { :verb => :delete,
|
31
|
-
:
|
31
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
32
32
|
:http_response => 204,
|
33
33
|
:required_headers => ['x-emc-tags'],
|
34
34
|
},
|
35
35
|
:read_object => { :verb => :get,
|
36
|
-
:
|
36
|
+
:object_uri => '/rest/objects/:id',
|
37
37
|
:http_response => 200,
|
38
38
|
:optional_headers => ['Range'],
|
39
39
|
},
|
40
40
|
:update_object => { :verb => :put,
|
41
|
-
:
|
41
|
+
:object_uri => '/rest/objects/:id',
|
42
42
|
:http_response => 200,
|
43
43
|
:required_headers => ['Content-Type', 'Content-Length'],
|
44
44
|
:optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
|
45
45
|
},
|
46
46
|
:trunc_object => { :verb => :put,
|
47
|
-
:
|
47
|
+
:object_uri => '/rest/objects/:id',
|
48
48
|
:http_response => 200,
|
49
49
|
:required_headers => ['Content-Length'],
|
50
50
|
:optional_headers => ['x-emc-useracl', 'x-emc-groupacl', 'Range'],
|
51
51
|
},
|
52
52
|
:get_service_info => { :verb => :get,
|
53
|
-
:
|
53
|
+
:object_uri => '/rest/service',
|
54
54
|
:response => :xml,
|
55
55
|
:http_response => 200,
|
56
56
|
},
|
57
57
|
|
58
58
|
:list_objects => { :verb => :get,
|
59
|
-
:
|
59
|
+
:object_uri => '/rest/objects',
|
60
60
|
:response => :xml,
|
61
61
|
:http_response => 200,
|
62
62
|
:required_headers => ['x-emc-tags', 'Content-Type'],
|
@@ -66,7 +66,8 @@ module Atmos
|
|
66
66
|
},
|
67
67
|
|
68
68
|
:create_object => { :verb => :post,
|
69
|
-
:
|
69
|
+
:namespace_uri => '/rest/namespace/:namespace',
|
70
|
+
:object_uri => '/rest/objects',
|
70
71
|
:http_response => 201,
|
71
72
|
:optional_headers => ['x-emc-wschecksum', 'x-emc-useracl', 'x-emc-groupacl', 'x-emc-meta', 'x-emc-listable-meta'],
|
72
73
|
:required_headers => ['Content-Type', 'Content-Length'],
|
@@ -78,8 +79,14 @@ module Atmos
|
|
78
79
|
'x-emc-listable-meta' => :listable_metadata},
|
79
80
|
},
|
80
81
|
|
82
|
+
:get_object_info => { :verb => :get,
|
83
|
+
:namespace_uri => '/rest/namespace/:namespace?info',
|
84
|
+
:object_uri => '/rest/objects/:id?info',
|
85
|
+
:http_response => 200,
|
86
|
+
:return_headers => ['x-emc-policy'],
|
87
|
+
},
|
81
88
|
:delete_object => { :verb => :delete,
|
82
|
-
:
|
89
|
+
:object_uri => '/rest/objects/:id',
|
83
90
|
:http_response => 204,
|
84
91
|
:required_headers => ['Content-Type'],
|
85
92
|
:return_headers => ['x-emc-delta', 'x-emc-policy'],
|
@@ -88,27 +95,27 @@ module Atmos
|
|
88
95
|
},
|
89
96
|
|
90
97
|
:list_system_metadata=> { :verb => :get,
|
91
|
-
:
|
98
|
+
:object_uri => '/rest/objects/:id?metadata/system',
|
92
99
|
:http_response => 200,
|
93
100
|
:return_headers => ['x-emc-meta'],
|
94
101
|
:errors => { '1003' => { :class => Atmos::Exceptions::NoSuchObjectException ,
|
95
102
|
:message => "Object not found to delete." } },
|
96
103
|
},
|
97
104
|
:list_metadata => { :verb => :get,
|
98
|
-
:
|
105
|
+
:object_uri => '/rest/objects/:id?metadata/user',
|
99
106
|
:http_response => 200,
|
100
107
|
:required_headers => ['Content-Type'],
|
101
108
|
:return_headers => ['x-emc-meta', 'x-emc-listable-meta'],
|
102
109
|
},
|
103
110
|
|
104
111
|
:list_tags => { :verb => :get,
|
105
|
-
:
|
112
|
+
:object_uri => '/rest/objects/:id?metadata/tags',
|
106
113
|
:http_response => 200,
|
107
114
|
:return_headers => ['x-emc-tags', 'x-emc-listable-tags'],
|
108
115
|
},
|
109
116
|
|
110
117
|
:list_acl => { :verb => :get,
|
111
|
-
:
|
118
|
+
:object_uri => '/rest/objects/:id?acl',
|
112
119
|
:http_response => 200,
|
113
120
|
:return_headers => ['x-emc-useracl', 'x-emc-groupacl'],
|
114
121
|
},
|
@@ -119,136 +126,20 @@ module Atmos
|
|
119
126
|
|
120
127
|
|
121
128
|
|
122
|
-
:delete_version => { :verb
|
123
|
-
:
|
124
|
-
:http_response
|
125
|
-
},
|
126
|
-
:get_object_info => { :verb => :get,
|
127
|
-
:uri => '/rest/objects/:id?info',
|
128
|
-
:http_response => 200,
|
129
|
+
:delete_version => { :verb => :delete,
|
130
|
+
:object_uri => '/rest/objects/:id?versions',
|
131
|
+
:http_response => 204,
|
129
132
|
},
|
130
|
-
:create_version => { :verb
|
131
|
-
:
|
132
|
-
:http_response
|
133
|
+
:create_version => { :verb => :post,
|
134
|
+
:object_uri => '/rest/objects/:id?versions' ,
|
135
|
+
:http_response => 201,
|
133
136
|
},
|
134
|
-
:list_versions => { :verb
|
135
|
-
|
137
|
+
:list_versions => { :verb => :get,
|
138
|
+
:object_uri => '/rest/objects/:id?versions',
|
139
|
+
:http_response => 200},
|
140
|
+
:restore_version => { :verb => :put,
|
141
|
+
:object_uri => '/rest/objects/:id?versions',
|
142
|
+
:http_response => 200},
|
136
143
|
} # :nodoc:
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
class Response # :nodoc:
|
141
|
-
attr_reader :http_response
|
142
|
-
|
143
|
-
def initialize(response, action)
|
144
|
-
@http_response = response
|
145
|
-
@action = action
|
146
|
-
|
147
|
-
if (response.kind_of?(Net::HTTPServerError))
|
148
|
-
raise Atmos::Exceptions::ServerException, response.message
|
149
|
-
elsif (response.kind_of?(Net::HTTPClientError))
|
150
|
-
|
151
|
-
raise Atmos::Exceptions::AtmosException, "Atmos got a bad request. This is probably a problem in this library." if (response.kind_of?(Net::HTTPBadRequest))
|
152
|
-
|
153
|
-
Atmos::LOG.debug("#{response.class}")
|
154
|
-
Atmos::LOG.debug("#{response.body}")
|
155
|
-
|
156
|
-
Atmos::Parser::response_check_action_error(@action, response)
|
157
|
-
|
158
|
-
elsif (!response.kind_of?(Net::HTTPSuccess))
|
159
|
-
raise Atmos::Exceptions::NotImplementedException, "This library doesn't handle these kinds of responses: #{response}"
|
160
|
-
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
def each(header)
|
165
|
-
if (!self[header].nil?)
|
166
|
-
self[header].split(',').each do |elt|
|
167
|
-
if (!elt.index('=').nil?)
|
168
|
-
key,val = elt.split('=')
|
169
|
-
yield key.strip, val.strip
|
170
|
-
else
|
171
|
-
yield elt.strip
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def body
|
178
|
-
@http_response.body
|
179
|
-
end
|
180
|
-
|
181
|
-
def method_missing(sym, *args)
|
182
|
-
Atmos::LOG.info("method missing: #{sym}")
|
183
|
-
header = "x-emc-#{sym.id2name.sub(/_/, '-')}"
|
184
|
-
if (REST[@action][:return_headers].include?(header))
|
185
|
-
Atmos::LOG.info("header: #{header}")
|
186
|
-
rv = Atmos::Util.header2hash(header, @http_response[header])
|
187
|
-
Atmos::LOG.debug("rv: #{rv.inspect}")
|
188
|
-
return rv
|
189
|
-
else
|
190
|
-
return super.method_missing(sym, *args)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def delta
|
195
|
-
if (REST[@action][:return_headers].include?('x-emc-delta'))
|
196
|
-
@http_response['x-emc-delta']
|
197
|
-
else
|
198
|
-
raise "A #{@action} request doesn't return a delta."
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def policy
|
203
|
-
if (REST[@action][:return_headers].include?('x-emc-policy'))
|
204
|
-
@http_response['x-emc-policy']
|
205
|
-
else
|
206
|
-
raise "A #{@action} request doesn't return a policy description."
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def id
|
211
|
-
if (REST[@action][:return_headers].include?('location'))
|
212
|
-
@http_response['location'][@http_response['location'].rindex('/')+1..-1]
|
213
|
-
else
|
214
|
-
raise "A #{@action} request doesn't return an id."
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
def headers
|
219
|
-
headers = {}
|
220
|
-
@http_response.each do |header, value|
|
221
|
-
headers[header] = value
|
222
|
-
end
|
223
|
-
headers
|
224
|
-
end
|
225
|
-
|
226
|
-
def [](header)
|
227
|
-
@http_response[header]
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
# while (response.kind_of?(Net::HTTPRedirection))
|
238
|
-
|
239
|
-
#From http://railstips.org/blog/archives/2009/03/04/following-redirects-with-nethttp/
|
240
|
-
# rurl = (response['location'].nil?) ? response.body.match(/<a href=\"([^>]+)\">/i)[1] : response['location']
|
241
|
-
|
242
|
-
# puts("Got a redirect \nfrom: #{options[:url]}\n to: #{rurl}")
|
243
|
-
|
244
|
-
# if rurl.start_with?('/')
|
245
|
-
# puts("Got a relative redirect url: #{options[:url]}")
|
246
|
-
# options[:url] = URI.parse("#{url.scheme}://#{url.host}#{redirect_url}")
|
247
|
-
# end
|
248
|
-
|
249
|
-
# options[:redirects] = (options[:redirects].nil?) ? 0 : options[:redirects] += 1
|
250
|
-
# response = self.generic_request(options)
|
251
|
-
# end
|
252
|
-
# raise "Too many redirects (#{options[:redirects]}): #{url}" if (!options[:redirects].nil? && (options[:redirects] > 3))
|
253
144
|
|
254
145
|
end
|
data/lib/atmos/store.rb
CHANGED
@@ -5,9 +5,8 @@ module Atmos
|
|
5
5
|
|
6
6
|
#
|
7
7
|
# Create and validate a connection to an Atmos server.
|
8
|
-
# This API
|
9
|
-
#
|
10
|
-
# against squid 2.7.STABLE9 on ubuntu 10.10.
|
8
|
+
# This API supports both the object interface and the namespace interface.
|
9
|
+
# Proxy support has been tested against squid 2.7.STABLE9 on ubuntu 10.10.
|
11
10
|
#
|
12
11
|
# If you have used Atmos Online this example will look familiar:
|
13
12
|
#
|
@@ -130,9 +129,10 @@ module Atmos
|
|
130
129
|
# * <tt>:mimetype</tt> - defaults to application/octet-stream
|
131
130
|
# * <tt>:data</tt> - a String or an IO stream
|
132
131
|
# * <tt>:length</tt> - a number (requires <tt>:data</tt>)
|
132
|
+
# * <tt>:namespace</tt> - name for namespace interface instead of object interface
|
133
133
|
#
|
134
134
|
def create(options = {})
|
135
|
-
Atmos::Object.new(self,
|
135
|
+
Atmos::Object.new(self, :create, options)
|
136
136
|
end
|
137
137
|
|
138
138
|
|
@@ -143,8 +143,19 @@ module Atmos
|
|
143
143
|
# The blob data is not loaded until
|
144
144
|
# accessed, so it can be progressively downloaded.
|
145
145
|
#
|
146
|
-
|
147
|
-
|
146
|
+
# Mutually exclusive, one required:
|
147
|
+
# * <tt>:id</tt> - the id of the object to retrieve
|
148
|
+
# * <tt>:namespace</tt> - the namespace key of the object to retrieve
|
149
|
+
def get(options)
|
150
|
+
|
151
|
+
# can't both be set, can't both be empty
|
152
|
+
if (!options[:id].nil? && !options[:namespace].nil?)
|
153
|
+
raise Atmos::Exceptions::ArgumentException, "One of :id, :namespace is required, but not both."
|
154
|
+
elsif (options[:id].nil? && options[:namespace].nil?)
|
155
|
+
raise Atmos::Exceptions::ArgumentException, "One of :id, :namespace is required."
|
156
|
+
end
|
157
|
+
|
158
|
+
Atmos::Object.new(self, :get, options)
|
148
159
|
end
|
149
160
|
|
150
161
|
|
data/lib/atmos/version.rb
CHANGED
data/test/suite.rb
CHANGED
@@ -4,6 +4,8 @@ require File.dirname(__FILE__) + '/test_util'
|
|
4
4
|
require File.dirname(__FILE__) + '/test_acl'
|
5
5
|
require File.dirname(__FILE__) + '/test_metadata'
|
6
6
|
require File.dirname(__FILE__) + '/test_object_create'
|
7
|
+
require File.dirname(__FILE__) + '/test_object_get'
|
8
|
+
require File.dirname(__FILE__) + '/test_object_read'
|
7
9
|
require File.dirname(__FILE__) + '/test_object_update'
|
8
10
|
require File.dirname(__FILE__) + '/test_object_misc'
|
9
11
|
require File.dirname(__FILE__) + '/test_store'
|
data/test/test_object_create.rb
CHANGED
@@ -22,7 +22,7 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
22
22
|
$stdout.flush
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
25
|
+
def test_create_empty_object_via_object_interface
|
26
26
|
obj = @s.create
|
27
27
|
@cleanup.push(obj)
|
28
28
|
|
@@ -33,6 +33,21 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
33
33
|
assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
|
34
34
|
end
|
35
35
|
|
36
|
+
def test_create_empty_object_via_namespace_interface
|
37
|
+
time = Time.now()
|
38
|
+
time = time.to_i
|
39
|
+
ns = "/foo/bar#{time}"
|
40
|
+
|
41
|
+
obj = @s.create(:namespace => ns)
|
42
|
+
@cleanup.push(obj)
|
43
|
+
|
44
|
+
assert_not_nil(obj)
|
45
|
+
assert_equal({}, obj.metadata)
|
46
|
+
assert_equal({}, obj.listable_metadata)
|
47
|
+
assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
|
48
|
+
assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
|
49
|
+
end
|
50
|
+
|
36
51
|
def test_create_object_with_bad_option
|
37
52
|
assert_raise Atmos::Exceptions::ArgumentException do
|
38
53
|
@cleanup.push(@s.create(:tag => ['testtag']))
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'credentials'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'atmos'
|
4
|
+
|
5
|
+
|
6
|
+
class AtmosObjectGetTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def initialize(id)
|
9
|
+
super(id)
|
10
|
+
@cleanup = []
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
@s = Atmos::Test::Util.static_store
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@cleanup.each do |obj|
|
21
|
+
obj.delete
|
22
|
+
end
|
23
|
+
$stdout.flush
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_get_object_via_object_interface
|
27
|
+
obj = @s.create
|
28
|
+
@cleanup.push(obj)
|
29
|
+
|
30
|
+
obj = @s.get(:id => obj.aoid)
|
31
|
+
assert_not_nil(obj)
|
32
|
+
assert_equal({}, obj.metadata)
|
33
|
+
assert_equal({}, obj.listable_metadata)
|
34
|
+
assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
|
35
|
+
assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_get_object_via_namespace_interface
|
39
|
+
time = Time.now()
|
40
|
+
time = time.to_i#{}"%.6f" % a.to_f #=> "1195480202.282373"
|
41
|
+
|
42
|
+
ns = "/foo/bar#{time}"
|
43
|
+
obj = @s.create(:namespace => ns)
|
44
|
+
obj = @s.get(:namespace => ns)
|
45
|
+
|
46
|
+
assert_not_nil(obj)
|
47
|
+
assert_equal({}, obj.metadata)
|
48
|
+
assert_equal({}, obj.listable_metadata)
|
49
|
+
assert_equal({Atmos::Test::Util.user => :full}, obj.user_acl)
|
50
|
+
assert_equal({Atmos::Test::Util.group => :none}, obj.group_acl)
|
51
|
+
|
52
|
+
obj.delete()
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/test/test_object_misc.rb
CHANGED
@@ -40,7 +40,7 @@ class AtmosObjectMiscTest < Test::Unit::TestCase
|
|
40
40
|
def test_delete_nonexisting_object
|
41
41
|
first = @s.create
|
42
42
|
assert_not_nil(first)
|
43
|
-
second = @s.get(first.aoid)
|
43
|
+
second = @s.get(:id => first.aoid)
|
44
44
|
assert_not_nil(second)
|
45
45
|
first.delete
|
46
46
|
assert_raise Atmos::Exceptions::NoSuchObjectException do
|
data/test/test_object_read.rb
CHANGED
@@ -3,24 +3,24 @@ require 'test/unit'
|
|
3
3
|
require 'atmos'
|
4
4
|
|
5
5
|
|
6
|
-
class
|
6
|
+
class AtmosObjectReadTest < Test::Unit::TestCase
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def initialize(id)
|
9
|
+
super(id)
|
10
|
+
@cleanup = []
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
def setup
|
14
|
+
@s = Atmos::Test::Util.static_store
|
15
|
+
@md = {'non' => 'listable', 'user' => 'metadata', 'a' => 'b'}
|
16
|
+
@lmd = {'x' => 'a', 'y' => 'b', 'z' => 'c'}
|
17
|
+
data = open(Atmos::Test::Util.small_binary_filename)
|
18
|
+
obj = @s.create(:metadata => @md,
|
19
|
+
:listable_metadata => @lmd,
|
20
|
+
:data => File.new(Atmos::Test::Util.small_binary_filename, "rb"))
|
21
|
+
@cleanup.push(obj)
|
22
|
+
@aoid = obj.aoid
|
23
|
+
end
|
24
24
|
|
25
25
|
|
26
26
|
def teardown
|
@@ -31,14 +31,10 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
31
31
|
|
32
32
|
|
33
33
|
def test_read_partial_data_as_stream
|
34
|
-
|
35
|
-
|
36
|
-
# it doesn't end up empty when this file is run
|
37
|
-
# as a standalone test
|
34
|
+
obj = @s.get(:id => @aoid)
|
35
|
+
|
38
36
|
file = open(Atmos::Test::Util.small_binary_filename)
|
39
37
|
contents = file.read
|
40
|
-
obj = @s.get(@aoid)
|
41
|
-
obj.update(contents)
|
42
38
|
assert_equal(contents, obj.data)
|
43
39
|
|
44
40
|
f = File.new(Atmos::Test::Util.small_binary_filename, "rb")
|
@@ -57,13 +53,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
57
53
|
|
58
54
|
def test_read_data_as_stream
|
59
55
|
|
60
|
-
# something is very strange here
|
61
|
-
# the object shouldn't end up empty, but it does
|
62
|
-
# it doesn't end up empty when this file is run
|
63
|
-
# as a standalone test
|
64
56
|
file = open(Atmos::Test::Util.small_binary_filename)
|
65
57
|
contents = file.read
|
66
|
-
obj = @s.get(@aoid)
|
58
|
+
obj = @s.get(:id => @aoid)
|
67
59
|
obj.update(contents)
|
68
60
|
assert_equal(contents, obj.data)
|
69
61
|
|
@@ -77,13 +69,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
77
69
|
|
78
70
|
def test_read_partial_data_as_string
|
79
71
|
|
80
|
-
# something is very strange here
|
81
|
-
# the object shouldn't end up empty, but it does
|
82
|
-
# it doesn't end up empty when this file is run
|
83
|
-
# as a standalone test
|
84
72
|
file = open(Atmos::Test::Util.small_binary_filename)
|
85
73
|
contents = file.read
|
86
|
-
obj = @s.get(@aoid)
|
74
|
+
obj = @s.get(:id => @aoid)
|
87
75
|
obj.update(contents)
|
88
76
|
assert_equal(contents, obj.data)
|
89
77
|
|
@@ -97,13 +85,9 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
97
85
|
end
|
98
86
|
|
99
87
|
def test_read_data_as_string
|
100
|
-
# something is very strange here
|
101
|
-
# the object shouldn't end up empty, but it does
|
102
|
-
# it doesn't end up empty when this file is run
|
103
|
-
# as a standalone test
|
104
88
|
file = open(Atmos::Test::Util.small_binary_filename)
|
105
89
|
contents = file.read
|
106
|
-
obj = @s.get(@aoid)
|
90
|
+
obj = @s.get(:id => @aoid)
|
107
91
|
obj.update(contents)
|
108
92
|
assert_equal(contents, obj.data)
|
109
93
|
|
data/test/test_object_update.rb
CHANGED
@@ -3,7 +3,7 @@ require 'test/unit'
|
|
3
3
|
require 'atmos'
|
4
4
|
|
5
5
|
|
6
|
-
class
|
6
|
+
class AtmosObjectUpdateTest < Test::Unit::TestCase
|
7
7
|
|
8
8
|
def initialize(id)
|
9
9
|
super(id)
|
@@ -14,6 +14,7 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
|
|
14
14
|
@s = Atmos::Test::Util.static_store
|
15
15
|
@obj = @s.create
|
16
16
|
@cleanup.push(@obj)
|
17
|
+
#print "test_object_update.setup(): obj: #{@obj}; objid: #{@obj.aoid}\n"
|
17
18
|
end
|
18
19
|
|
19
20
|
|
@@ -3,7 +3,7 @@ require 'test/unit'
|
|
3
3
|
require File.dirname(__FILE__) + '/../lib/atmos'
|
4
4
|
|
5
5
|
|
6
|
-
class
|
6
|
+
class AtmosRequestTest < Test::Unit::TestCase
|
7
7
|
|
8
8
|
def dont_test_signature_calculation
|
9
9
|
headers = {}
|
@@ -44,7 +44,6 @@ class AtmosUtilTest < Test::Unit::TestCase
|
|
44
44
|
:secret => 'p97mki4AkiAyVi+/7QpdxFAU/jY=',
|
45
45
|
:unsupported => true
|
46
46
|
)
|
47
|
-
print "#{store.server_version}\n"
|
48
47
|
|
49
48
|
end
|
50
49
|
end
|
data/test/test_store.rb
CHANGED
@@ -112,11 +112,11 @@ class AtmosStoreTest < Test::Unit::TestCase
|
|
112
112
|
|
113
113
|
for i in (1...10)
|
114
114
|
obj = @s.create(:listable_metadata => {tag => true})
|
115
|
+
@cleanup.push(obj)
|
115
116
|
end
|
116
117
|
|
117
118
|
i = 0
|
118
119
|
@s.each_object_with_listable_tag(tag) do |obj|
|
119
|
-
@cleanup.push(obj)
|
120
120
|
i += 1
|
121
121
|
end
|
122
122
|
assert_equal(9, i)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-atmos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 0.6.0
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Fleur Dragan
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-03 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -77,11 +77,13 @@ extra_rdoc_files:
|
|
77
77
|
- COPYING
|
78
78
|
files:
|
79
79
|
- Rakefile
|
80
|
+
- lib/atmos.rb
|
80
81
|
- lib/atmos/attributes.rb
|
81
82
|
- lib/atmos/exceptions.rb
|
82
83
|
- lib/atmos/object.rb
|
83
84
|
- lib/atmos/parser.rb
|
84
85
|
- lib/atmos/request.rb
|
86
|
+
- lib/atmos/response.rb
|
85
87
|
- lib/atmos/rest.rb
|
86
88
|
- lib/atmos/store.rb
|
87
89
|
- lib/atmos/util.rb
|
@@ -91,16 +93,17 @@ files:
|
|
91
93
|
- test/files/dragaf-tiny-from-vsphere.ova
|
92
94
|
- test/files/SmallImageForTest.iso
|
93
95
|
- test/files/something.txt
|
94
|
-
- test/request_test.rb
|
95
96
|
- test/suite.rb
|
96
97
|
- test/suite_noproxy.rb
|
97
98
|
- test/suite_proxy.rb
|
98
99
|
- test/test_acl.rb
|
99
100
|
- test/test_metadata.rb
|
100
101
|
- test/test_object_create.rb
|
102
|
+
- test/test_object_get.rb
|
101
103
|
- test/test_object_misc.rb
|
102
104
|
- test/test_object_read.rb
|
103
105
|
- test/test_object_update.rb
|
106
|
+
- test/test_request.rb
|
104
107
|
- test/test_store.rb
|
105
108
|
- test/test_util.rb
|
106
109
|
- README
|
@@ -150,15 +153,16 @@ test_files:
|
|
150
153
|
- test/files/dragaf-tiny-from-vsphere.ova
|
151
154
|
- test/files/SmallImageForTest.iso
|
152
155
|
- test/files/something.txt
|
153
|
-
- test/request_test.rb
|
154
156
|
- test/suite.rb
|
155
157
|
- test/suite_noproxy.rb
|
156
158
|
- test/suite_proxy.rb
|
157
159
|
- test/test_acl.rb
|
158
160
|
- test/test_metadata.rb
|
159
161
|
- test/test_object_create.rb
|
162
|
+
- test/test_object_get.rb
|
160
163
|
- test/test_object_misc.rb
|
161
164
|
- test/test_object_read.rb
|
162
165
|
- test/test_object_update.rb
|
166
|
+
- test/test_request.rb
|
163
167
|
- test/test_store.rb
|
164
168
|
- test/test_util.rb
|