ruby-atmos 0.6.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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, aoid, options = {})
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
- # this means we're creating a new object,
115
- # not representing an existing one
116
- if (@aoid.nil?)
117
- response = @request.do(:create_object, options)
118
- @aoid = response.id
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
- @system_metadata = Atmos::Metadata.new(self, Atmos::Metadata::SYSTEM)
122
-
123
- # These guys need the object to have a valid id, so we do it after the create
124
- @user_acl = Atmos::ACL.new(self, Atmos::ACL::USER)
125
- @group_acl = Atmos::ACL.new(self, Atmos::ACL::GROUP)
126
- @metadata = Atmos::Metadata.new(self, Atmos::Metadata::NON_LISTABLE)
127
- @listable_metadata = Atmos::Metadata.new(self, Atmos::Metadata::LISTABLE)
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 = REST[actionname]
26
- uri = (options[:id].nil?) ? action[:uri] : action[:uri].sub(/:id/, options[:id])
27
- url = URI::join(@baseurl.to_s, uri.to_s)
28
- verb = action[: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
- :uri => '/rest/objects?listabletags',
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
- :uri => '/rest/objects/:id?acl',
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
- :uri => '/rest/objects/:id?acl',
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
- :uri => '/rest/objects/:id?metadata/user',
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 => :post,
26
- :uri => '/rest/objects/:id?metadata/user',
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
- :uri => '/rest/objects/:id?metadata/user',
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
- :uri => '/rest/objects/:id',
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
- :uri => '/rest/objects/:id',
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
- :uri => '/rest/objects/:id',
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
- :uri => '/rest/service',
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
- :uri => '/rest/objects',
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
- :uri => '/rest/objects',
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
- :uri => '/rest/objects/:id',
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
- :uri => '/rest/objects/:id?metadata/system',
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
- :uri => '/rest/objects/:id?metadata/user',
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
- :uri => '/rest/objects/:id?metadata/tags',
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
- :uri => '/rest/objects/:id?acl',
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 => :delete,
123
- :uri => '/rest/objects/:id?versions',
124
- :http_response => 204,
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 => :post,
131
- :uri => '/rest/objects/:id?versions' ,
132
- :http_response => 201,
133
+ :create_version => { :verb => :post,
134
+ :object_uri => '/rest/objects/:id?versions' ,
135
+ :http_response => 201,
133
136
  },
134
- :list_versions => { :verb => :get, :uri => '/rest/objects/:id?versions', :http_response => 200},
135
- :restore_version => { :verb => :put, :uri => '/rest/objects/:id?versions', :http_response => 200},
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 currently uses only the Atmos object interface,
9
- # not the namespace interface. Proxy support has been tested
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, nil, options)
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
- def get(id)
147
- Atmos::Object.new(self, id)
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
@@ -1,7 +1,7 @@
1
1
  module Atmos
2
2
 
3
3
  # Version number of this library.
4
- VERSION = "0.6.0"
4
+ VERSION = "1.0.1"
5
5
 
6
6
  # Array of version strings this library has been tested against.
7
7
  SUPPORTED_VERSIONS = ["1.2.7", "1.3.4", "1.4.0", "1.4.1", "2.0.1"].freeze
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'
@@ -22,7 +22,7 @@ class AtmosObjectCreateTest < Test::Unit::TestCase
22
22
  $stdout.flush
23
23
  end
24
24
 
25
- def test_create_empty_object
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
@@ -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
@@ -3,24 +3,24 @@ require 'test/unit'
3
3
  require 'atmos'
4
4
 
5
5
 
6
- class AtmosObjectCreateTest < Test::Unit::TestCase
6
+ class AtmosObjectReadTest < Test::Unit::TestCase
7
7
 
8
- def initialize(id)
9
- super(id)
10
- @cleanup = []
11
- end
8
+ def initialize(id)
9
+ super(id)
10
+ @cleanup = []
11
+ end
12
12
 
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
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
- # something is very strange here
35
- # the object shouldn't end up empty, but it does
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
 
@@ -3,7 +3,7 @@ require 'test/unit'
3
3
  require 'atmos'
4
4
 
5
5
 
6
- class AtmosObjectCreateTest < Test::Unit::TestCase
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 AtmosUtilTest < Test::Unit::TestCase
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: 7
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 6
9
- - 0
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: 2011-12-14 00:00:00 -08:00
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