raca 0.3.3 → 0.4.0

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.
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