raca 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aaa0a7402a954f81c2f50f2baf971bd5fe2393b3
4
- data.tar.gz: 5e047547e7fec0af4e9ef1f0874706b3c6a2decb
3
+ metadata.gz: e941f8aeb1ac9f4a298aa8bf39d9de49cfeeced0
4
+ data.tar.gz: cbbb18973fd14aed6dbf03d277229a47668a3019
5
5
  SHA512:
6
- metadata.gz: b0dd7c796af6df4919a712d2a41396f18b5801598202493125f65c6299576d3101c6ce826bf523878c61e42ec761152cada2bd802b4bef0ecc356313c0c9921d
7
- data.tar.gz: 64c50f121fa1e365242faef9d7688f826562f6fa6b28b8363fd23307dfb44ab57d61eb7a00d92ebb2065a1e2fc3e40e0875b58ea92981398cb8baa9ab2cea3d0
6
+ metadata.gz: ff7704021fa4e079271978663d322553e99bcfc7fad5036a5f62381c71d77150df410f42d436c700dbcf7f7f430d4ffba3f35eb606aea90895b17ccf9f4aa9c4
7
+ data.tar.gz: 07b80eb013f6e8ab98db0b502b382169746d82dd7d4d5981f1f5384a5241e36867e8e1aad4faeec2376c43a7050dfeb403cde1e22110ae7d5a3177d92cb1bc31
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ v0.4.0 (25th August 2014)
2
+ * Added Raca::Container#temp_upload_url
3
+ * Renamed Raca::Container#expiring_url to Raca::Container#temp_url
4
+ * the old method still exists for now but is deprecated
5
+ * Expand Raca::Container#metadata to include custom metadata
6
+ * Added raca::Container#set_metadata
7
+ * For non regioned APIs (like Cloud DNS) the region argument to Raca::Account#public_endpoint
8
+ can be left off
9
+ * Added read access to user accounts via Raca::Account#users
10
+
1
11
  v0.3.3 (26th April 2014)
2
12
  * Added a User-Agent header to all requests
3
13
  * Remove automatic retries after a rackspace timeout
data/README.markdown CHANGED
@@ -112,7 +112,7 @@ is the temp URL key that can be set using Raca::Containers#set_temp_url_key
112
112
  ord_containers = account.containers(:ord)
113
113
  ord_containers.set_temp_url_key("secret")
114
114
  dir = ord_containers.get("container_name")
115
- puts dir.expiring_url("remote_key.txt", "secret", Time.now.to_i + 60)
115
+ puts dir.temp_url("remote_key.txt", "secret", Time.now.to_i + 60)
116
116
 
117
117
  ### Cloud Servers
118
118
 
@@ -133,6 +133,21 @@ You can use the collection to create a brand new server:
133
133
 
134
134
  a_server = ord_servers.create("server_name", "1Gb", "Ubuntu 10.04 LTS")
135
135
 
136
+ ### Users
137
+
138
+ Using an existing Raca::Account object, retrieve a collection of Users like so:
139
+
140
+ users = account.users
141
+
142
+ You can retrieve an existing user from the collection:
143
+
144
+ a_user = users.get("username")
145
+
146
+ You can display details for each user with the details method:
147
+
148
+ a_user = users.get("username")
149
+ a_user.details
150
+
136
151
  ## General API principles
137
152
 
138
153
  Methods that make calls to an API should never return a raw HTTP response
data/lib/raca.rb CHANGED
@@ -6,4 +6,6 @@ require 'raca/containers'
6
6
  require 'raca/http_client'
7
7
  require 'raca/server'
8
8
  require 'raca/servers'
9
+ require 'raca/user'
10
+ require 'raca/users'
9
11
  require 'raca/util'
data/lib/raca/account.rb CHANGED
@@ -8,6 +8,7 @@ module Raca
8
8
  # the supported rackspace APIs.
9
9
  #
10
10
  class Account
11
+ IDENTITY_URL = "https://identity.api.rackspacecloud.com/v2.0/"
11
12
 
12
13
  def initialize(username, key, cache = nil)
13
14
  @username, @key, @cache = username, key, cache
@@ -37,11 +38,28 @@ module Raca
37
38
  # account = Raca::Account.new("username", "secret")
38
39
  # puts account.public_endpoint("cloudServers", :syd)
39
40
  #
40
- def public_endpoint(service_name, region)
41
- region = region.to_s.upcase
41
+ # Some service APIs are not regioned. In those cases, the region code can be
42
+ # left off:
43
+ #
44
+ # account = Raca::Account.new("username", "secret")
45
+ # puts account.public_endpoint("cloudDNS")
46
+ #
47
+ def public_endpoint(service_name, region = nil)
48
+ return IDENTITY_URL if service_name == "identity"
49
+
42
50
  endpoints = service_endpoints(service_name)
43
- regional_endpoint = endpoints.detect { |e| e["region"] == region } || {}
44
- regional_endpoint["publicURL"]
51
+ if endpoints.size > 1 && region
52
+ region = region.to_s.upcase
53
+ endpoints = endpoints.select { |e| e["region"] == region } || {}
54
+ elsif endpoints.size > 1 && region.nil?
55
+ raise ArgumentError, "The requested service exists in multiple regions, please specify a region code"
56
+ end
57
+
58
+ if endpoints.size == 0
59
+ raise ArgumentError, "No matching services found"
60
+ else
61
+ endpoints.first["publicURL"]
62
+ end
45
63
  end
46
64
 
47
65
  # Return the names of the available services. As rackspace add new services and
@@ -80,13 +98,23 @@ module Raca
80
98
  Raca::Servers.new(self, region)
81
99
  end
82
100
 
101
+ # Return a Raca::Users object. Use this to query and manage the users associated
102
+ # with the current account.
103
+ #
104
+ # account = Raca::Account.new("username", "secret")
105
+ # puts account.users
106
+ #
107
+ def users
108
+ Raca::Users.new(self)
109
+ end
110
+
83
111
  # Raca classes use this method to occasionally re-authenticate with the rackspace
84
112
  # servers. You can probably ignore it.
85
113
  #
86
114
  def refresh_cache
87
115
  # Raca::HttpClient depends on Raca::Account, so we intentionally don't use it here
88
116
  # to avoid a circular dependency
89
- Net::HTTP.new('identity.api.rackspacecloud.com', 443).tap {|http|
117
+ Net::HTTP.new(identity_host, 443).tap {|http|
90
118
  http.use_ssl = true
91
119
  }.start {|http|
92
120
  payload = {
@@ -98,7 +126,7 @@ module Raca
98
126
  }
99
127
  }
100
128
  response = http.post(
101
- '/v2.0/tokens',
129
+ tokens_path,
102
130
  JSON.dump(payload),
103
131
  {'Content-Type' => 'application/json'},
104
132
  )
@@ -116,8 +144,24 @@ module Raca
116
144
  Raca::HttpClient.new(self, hostname)
117
145
  end
118
146
 
147
+ def inspect
148
+ "#<Raca::Account:#{__id__} username=#{@username}>"
149
+ end
150
+
119
151
  private
120
152
 
153
+ def identity_host
154
+ URI.parse(IDENTITY_URL).host
155
+ end
156
+
157
+ def identity_path
158
+ URI.parse(IDENTITY_URL).path
159
+ end
160
+
161
+ def tokens_path
162
+ File.join(identity_path, "tokens")
163
+ end
164
+
121
165
  def raise_on_error(response)
122
166
  error_klass = case response.code.to_i
123
167
  when 400 then BadRequestError
@@ -90,14 +90,14 @@ module Raca
90
90
  def download(key, filepath)
91
91
  log "downloading #{key} from #{container_path}"
92
92
  object_path = File.join(container_path, Raca::Util.url_encode(key))
93
- response = storage_client.get(object_path) do |response|
93
+ outer_response = storage_client.get(object_path) do |response|
94
94
  File.open(filepath, 'wb') do |io|
95
95
  response.read_body do |chunk|
96
96
  io.write(chunk)
97
97
  end
98
98
  end
99
99
  end
100
- response["Content-Length"].to_i
100
+ outer_response["Content-Length"].to_i
101
101
  end
102
102
 
103
103
  # Return an array of files in the container.
@@ -151,12 +151,34 @@ module Raca
151
151
  def metadata
152
152
  log "retrieving container metadata from #{container_path}"
153
153
  response = storage_client.head(container_path)
154
+ custom = {}
155
+ response.each_capitalized_name { |name|
156
+ custom[name] = response[name] if name[/\AX-Container-Meta-/]
157
+ }
154
158
  {
155
159
  :objects => response["X-Container-Object-Count"].to_i,
156
- :bytes => response["X-Container-Bytes-Used"].to_i
160
+ :bytes => response["X-Container-Bytes-Used"].to_i,
161
+ :custom => custom,
157
162
  }
158
163
  end
159
164
 
165
+ # Set metadata headers on the container
166
+ #
167
+ # headers = { "X-Container-Meta-Access-Control-Allow-Origin" => "*" }
168
+ # container.set_metadata(headers)
169
+ #
170
+ # Note: Rackspace requires some headers to begin with 'X-Container-Meta-' or other prefixes, e.g. when setting
171
+ # 'Access-Control-Allow-Origin', it needs to be set as 'X-Container-Meta-Access-Control-Allow-Origin'.
172
+ # See: http://docs.rackspace.com/files/api/v1/cf-devguide/content/CORS_Container_Header-d1e1300.html
173
+ # http://docs.rackspace.com/files/api/v1/cf-devguide/content/
174
+ # POST_updateacontainermeta_v1__account___container__containerServicesOperations_d1e000.html
175
+ #
176
+ def set_metadata(headers)
177
+ log "setting headers for container #{container_path}"
178
+ response = storage_client.post(container_path, '', headers)
179
+ (200..299).cover?(response.code.to_i)
180
+ end
181
+
160
182
  # Return the key details for CDN access to this container. Can be called
161
183
  # on non CDN enabled containers, but the details won't make much sense.
162
184
  #
@@ -186,13 +208,37 @@ module Raca
186
208
  (200..299).cover?(response.code.to_i)
187
209
  end
188
210
 
189
- # Generate a expiring URL for a file that is otherwise private. useful for providing temporary
190
- # access to files.
211
+ # Generate an expiring URL for downloading a file that is otherwise private.
212
+ # Useful for providing temporary access to files.
213
+ #
214
+ def temp_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
215
+ private_url("GET", object_key, temp_url_key, expires_at)
216
+ end
217
+
218
+ # DEPRECATED: use temp_url instead, this will be removed in version 1.0
191
219
  #
192
220
  def expiring_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
193
- digest = OpenSSL::Digest::Digest.new('sha1')
221
+ temp_url(object_key, temp_url_key, expires_at)
222
+ end
223
+
224
+ # Generate a temporary URL for uploading a file to a private container. Anyone
225
+ # can perform a PUT request to the URL returned from this method and an object
226
+ # will be created in the container.
227
+ #
228
+ def temp_upload_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
229
+ private_url("PUT", object_key, temp_url_key, expires_at)
230
+ end
231
+
232
+ def inspect
233
+ "#<Raca::Container:#{__id__} region=#{@region} container_name=#{@container_name}>"
234
+ end
235
+
236
+ private
237
+
238
+ def private_url(method, object_key, temp_url_key, expires_at)
239
+ raise ArgumentError, "method must be GET or PUT" unless %w{GET PUT}.include?(method)
240
+ digest = OpenSSL::Digest.new('sha1')
194
241
 
195
- method = 'GET'
196
242
  expires = expires_at.to_i
197
243
  path = File.join(container_path, object_key)
198
244
  encoded_path = File.join(container_path, Raca::Util.url_encode(object_key))
@@ -204,8 +250,6 @@ module Raca
204
250
  "https://#{storage_host}#{encoded_path}?temp_url_sig=#{hmac.hexdigest}&temp_url_expires=#{expires}"
205
251
  end
206
252
 
207
- private
208
-
209
253
  # build the request path for listing the contents of a container
210
254
  #
211
255
  def list_request_path(marker, prefix, details, limit)
@@ -45,6 +45,10 @@ module Raca
45
45
  (200..299).cover?(response.code.to_i)
46
46
  end
47
47
 
48
+ def inspect
49
+ "#<Raca::Containers:#{__id__} region=#{@region}>"
50
+ end
51
+
48
52
  private
49
53
 
50
54
  def storage_host
@@ -65,6 +65,10 @@ module Raca
65
65
  cloud_request(request)
66
66
  end
67
67
 
68
+ def inspect
69
+ "#<Raca::HttpClient:#{__id__}>"
70
+ end
71
+
68
72
  private
69
73
 
70
74
  def build_streaming_put_request(path, io, byte_count, headers)
@@ -91,7 +95,7 @@ module Raca
91
95
  def cloud_request(request, &block)
92
96
  cloud_http do |http|
93
97
  request['X-Auth-Token'] = @account.auth_token
94
- request['User-Agent'] = "raca 0.3.3 (http://rubygems.org/gems/raca)"
98
+ request['User-Agent'] = "raca 0.4.0 (http://rubygems.org/gems/raca)"
95
99
  http.request(request, &block)
96
100
  end
97
101
  end
data/lib/raca/server.rb CHANGED
@@ -53,6 +53,10 @@ module Raca
53
53
  JSON.parse(data)['server']
54
54
  end
55
55
 
56
+ def inspect
57
+ "#<Raca::Server:#{__id__} region=#{@region} server_id=#{@server_id}>"
58
+ end
59
+
56
60
  private
57
61
 
58
62
  def json_headers
data/lib/raca/servers.rb CHANGED
@@ -61,6 +61,10 @@ module Raca
61
61
  Raca::Server.new(@account, @region, data['id'])
62
62
  end
63
63
 
64
+ def inspect
65
+ "#<Raca::Servers:#{__id__} region=#{@region}>"
66
+ end
67
+
64
68
  private
65
69
 
66
70
  def json_headers
data/lib/raca/user.rb ADDED
@@ -0,0 +1,50 @@
1
+ module Raca
2
+ # Represents a single user within the current account.
3
+ #
4
+ # You probably don't want to instantiate this directly,
5
+ # see Raca::Account#users
6
+ #
7
+ class User
8
+ attr_reader :username
9
+
10
+ def initialize(account, username, opts = {})
11
+ @account, @username = account, username
12
+ @identity_url = @account.public_endpoint("identity")
13
+ @logger = opts[:logger]
14
+ @logger ||= Rails.logger if defined?(Rails)
15
+ end
16
+
17
+ def details
18
+ response = identity_client.get(user_path)
19
+ JSON.load(response.body)["user"]
20
+ end
21
+
22
+ def inspect
23
+ "#<Raca::User:#{__id__} @username=#{@username}>"
24
+ end
25
+
26
+ private
27
+
28
+ def identity_host
29
+ URI.parse(@identity_url).host
30
+ end
31
+
32
+ def identity_path
33
+ URI.parse(@identity_url).path
34
+ end
35
+
36
+ def user_path
37
+ @user_path ||= File.join(identity_path, "users") + "?name=" + Raca::Util.url_encode(@username)
38
+ end
39
+
40
+ def identity_client
41
+ @identity_client ||= @account.http_client(identity_host)
42
+ end
43
+
44
+ def log(msg)
45
+ if @logger.respond_to?(:debug)
46
+ @logger.debug msg
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/raca/users.rb ADDED
@@ -0,0 +1,59 @@
1
+ module Raca
2
+ # Represents a collection of users associated with a rackspace account
3
+ #
4
+ # You probably don't want to instantiate this directly,
5
+ # see Raca::Account#users
6
+ #
7
+ class Users
8
+ def initialize(account, opts = {})
9
+ @account = account
10
+ @identity_url = @account.public_endpoint("identity")
11
+ @logger = opts[:logger]
12
+ @logger ||= Rails.logger if defined?(Rails)
13
+ end
14
+
15
+ def get(username)
16
+ list.detect { |user| user.username == username }
17
+ end
18
+
19
+ def inspect
20
+ "#<Raca::Users:#{__id__}>"
21
+ end
22
+
23
+ private
24
+
25
+ # TODO should this (or something like it) be part of the public API?
26
+ def list
27
+ log "retrieving users list from #{users_path}"
28
+ response = identity_client.get(users_path)
29
+ records = JSON.load(response.body)["users"]
30
+ records.map { |record|
31
+ record["username"]
32
+ }.map { |username|
33
+ Raca::User.new(@account, username)
34
+ }
35
+ end
36
+
37
+ def identity_host
38
+ URI.parse(@identity_url).host
39
+ end
40
+
41
+ def identity_path
42
+ URI.parse(@identity_url).path
43
+ end
44
+
45
+ def users_path
46
+ File.join(identity_path, "users")
47
+ end
48
+
49
+ def identity_client
50
+ @identity_client ||= @account.http_client(identity_host)
51
+ end
52
+
53
+ def log(msg)
54
+ if @logger.respond_to?(:debug)
55
+ @logger.debug msg
56
+ end
57
+ end
58
+ end
59
+ end
data/lib/raca/util.rb CHANGED
@@ -3,8 +3,8 @@ module Raca
3
3
  class Util
4
4
  # CGI.escape, but without special treatment on spaces
5
5
  def self.url_encode(str)
6
- str.gsub(%r{([^a-zA-Z0-9_./-]+)}) do |match|
7
- '%' + match.unpack('H2*' * match.bytesize).join('%').upcase
6
+ str.gsub(%r{([^a-zA-Z0-9_./-])}) do |match|
7
+ '%' + match.unpack('H*').first.scan(/../).join("%").upcase
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Healy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-26 00:00:00.000000000 Z
11
+ date: 2014-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '3.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: webmock
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -99,6 +99,8 @@ files:
99
99
  - lib/raca/http_client.rb
100
100
  - lib/raca/server.rb
101
101
  - lib/raca/servers.rb
102
+ - lib/raca/user.rb
103
+ - lib/raca/users.rb
102
104
  - lib/raca/util.rb
103
105
  homepage: http://github.com/conversation/raca
104
106
  licenses:
@@ -128,3 +130,4 @@ signing_key:
128
130
  specification_version: 4
129
131
  summary: A simple wrapper for the Rackspace Cloud API with no dependencies
130
132
  test_files: []
133
+ has_rdoc: true