cloudservers 0.2.0 → 0.3.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.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require './lib/cloudservers.rb'
3
+ require 'rake/testtask'
3
4
 
4
5
  begin
5
6
  require 'jeweler'
@@ -7,11 +8,17 @@ begin
7
8
  gemspec.name = "cloudservers"
8
9
  gemspec.summary = "Rackspace Cloud Servers Ruby API"
9
10
  gemspec.description = "A Ruby API to version 1.0 of the Rackspace Cloud Servers product."
10
- gemspec.email = "wade.minter@rackspace.com"
11
+ gemspec.email = "minter@lunenburg.org"
11
12
  gemspec.homepage = "http://github.com/rackspace/cloudservers"
12
- gemspec.authors = ["H. Wade Minter","Mike Mayo"]
13
+ gemspec.authors = ["H. Wade Minter","Mike Mayo","Dan Prince"]
13
14
  gemspec.add_dependency 'json'
14
15
  end
15
16
  rescue LoadError
16
17
  puts "Jeweler not available. Install it with: sudo gem install jeweler"
17
18
  end
19
+
20
+ Rake::TestTask.new(:test) do |t|
21
+ t.pattern = 'test/*_test.rb'
22
+ t.verbose = true
23
+ end
24
+ Rake::Task['test'].comment = "Unit"
data/TODO CHANGED
@@ -1,9 +1,3 @@
1
- * There are caching bugs in the API, so that accurate information is not always returned (ie. if you add a server and
2
- then list available servers, the new one will not show up.). That needs to be corrected on the API end before
3
- data will be accurate.
4
-
5
- * Add pagination in object listing.
6
-
7
1
  * Allow some sort of flag to get the stack trace when an exception is thrown.
8
2
 
9
3
  * Shared IP group modification code.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
data/cloudservers.gemspec CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cloudservers}
8
- s.version = "0.2.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["H. Wade Minter", "Mike Mayo"]
12
- s.date = %q{2010-05-06}
11
+ s.authors = ["H. Wade Minter", "Mike Mayo", "Dan Prince"]
12
+ s.date = %q{2010-10-21}
13
13
  s.description = %q{A Ruby API to version 1.0 of the Rackspace Cloud Servers product.}
14
- s.email = %q{wade.minter@rackspace.com}
14
+ s.email = %q{minter@lunenburg.org}
15
15
  s.extra_rdoc_files = [
16
16
  "README.rdoc",
17
17
  "TODO"
@@ -34,15 +34,21 @@ Gem::Specification.new do |s|
34
34
  "lib/cloudservers/server.rb",
35
35
  "lib/cloudservers/shared_ip_group.rb",
36
36
  "test/cloudservers_authentication_test.rb",
37
+ "test/cloudservers_connection_test.rb",
38
+ "test/cloudservers_exception_test.rb",
39
+ "test/cloudservers_servers_test.rb",
37
40
  "test/test_helper.rb"
38
41
  ]
39
42
  s.homepage = %q{http://github.com/rackspace/cloudservers}
40
43
  s.rdoc_options = ["--charset=UTF-8"]
41
44
  s.require_paths = ["lib"]
42
- s.rubygems_version = %q{1.3.6}
45
+ s.rubygems_version = %q{1.3.7}
43
46
  s.summary = %q{Rackspace Cloud Servers Ruby API}
44
47
  s.test_files = [
45
48
  "test/cloudservers_authentication_test.rb",
49
+ "test/cloudservers_connection_test.rb",
50
+ "test/cloudservers_exception_test.rb",
51
+ "test/cloudservers_servers_test.rb",
46
52
  "test/test_helper.rb"
47
53
  ]
48
54
 
@@ -50,7 +56,7 @@ Gem::Specification.new do |s|
50
56
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
57
  s.specification_version = 3
52
58
 
53
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
59
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
60
  s.add_runtime_dependency(%q<json>, [">= 0"])
55
61
  else
56
62
  s.add_dependency(%q<json>, [">= 0"])
@@ -32,8 +32,8 @@ module CloudServers
32
32
  #
33
33
  # cf = CloudServers::Connection.new(:username => 'YOUR_USERNAME', :api_key => 'YOUR_API_KEY')
34
34
  def initialize(options = {:retry_auth => true})
35
- @authuser = options[:username] || (raise Authentication, "Must supply a :username")
36
- @authkey = options[:api_key] || (raise Authentication, "Must supply an :api_key")
35
+ @authuser = options[:username] || (raise Exception::Authentication, "Must supply a :username")
36
+ @authkey = options[:api_key] || (raise Exception::Authentication, "Must supply an :api_key")
37
37
  @retry_auth = options[:retry_auth]
38
38
  @proxy_host = options[:proxy_host]
39
39
  @proxy_port = options[:proxy_port]
@@ -62,9 +62,9 @@ module CloudServers
62
62
  response
63
63
  rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError
64
64
  # Server closed the connection, retry
65
- raise CloudServers::Exception::Connection, "Unable to reconnect to #{server} after #{count} attempts" if attempts >= 5
65
+ raise CloudServers::Exception::Connection, "Unable to reconnect to #{server} after #{attempts} attempts" if attempts >= 5
66
66
  attempts += 1
67
- @http[server].finish
67
+ @http[server].finish if @http[server].started?
68
68
  start_http(server,path,port,scheme,headers)
69
69
  retry
70
70
  rescue CloudServers::Exception::ExpiredAuthToken
@@ -86,11 +86,18 @@ module CloudServers
86
86
 
87
87
  # Returns an array of hashes, one for each server that exists under this account. The hash keys are :name and :id.
88
88
  #
89
+ # You can also provide :limit and :offset parameters to handle pagination.
90
+ #
89
91
  # >> cs.list_servers
90
92
  # => [{:name=>"MyServer", :id=>110917}]
93
+ #
94
+ # >> cs.list_servers(:limit => 2, :offset => 3)
95
+ # => [{:name=>"demo-standingcloud-lts", :id=>168867},
96
+ # {:name=>"demo-aicache1", :id=>187853}]
91
97
  def list_servers(options = {})
92
- url_params = "?limit=#{URI.escape(options[:limit].to_s)}&offset=#{URI.escape(options[:offset].to_s)}" if options[:limit] && options[:offset]
93
- response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/servers",svrmgmtport,svrmgmtscheme)
98
+ anti_cache_param="cacheid=#{Time.now.to_i}"
99
+ path = CloudServers.paginate(options).empty? ? "#{svrmgmtpath}/servers?#{anti_cache_param}" : "#{svrmgmtpath}/servers?#{CloudServers.paginate(options)}&#{anti_cache_param}"
100
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
94
101
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
95
102
  CloudServers.symbolize_keys(JSON.parse(response.body)["servers"])
96
103
  end
@@ -100,10 +107,16 @@ module CloudServers
100
107
  # includes public and private IP addresses, status, hostID, and more. All hash keys are symbols except for the metadata
101
108
  # hash, which are verbatim strings.
102
109
  #
110
+ # You can also provide :limit and :offset parameters to handle pagination.
103
111
  # >> cs.list_servers_detail
104
112
  # => [{:name=>"MyServer", :addresses=>{:public=>["67.23.42.37"], :private=>["10.176.241.237"]}, :metadata=>{"MyData" => "Valid"}, :imageId=>10, :progress=>100, :hostId=>"36143b12e9e48998c2aef79b50e144d2", :flavorId=>1, :id=>110917, :status=>"ACTIVE"}]
105
- def list_servers_detail
106
- response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/servers/detail",svrmgmtport,svrmgmtscheme)
113
+ #
114
+ # >> cs.list_servers_detail(:limit => 2, :offset => 3)
115
+ # => [{:status=>"ACTIVE", :imageId=>10, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-standingcloud-lts", :id=>168867, :flavorId=>1, :hostId=>"xxxxxx"},
116
+ # {:status=>"ACTIVE", :imageId=>8, :progress=>100, :metadata=>{}, :addresses=>{:public=>["x.x.x.x"], :private=>["x.x.x.x"]}, :name=>"demo-aicache1", :id=>187853, :flavorId=>3, :hostId=>"xxxxxx"}]
117
+ def list_servers_detail(options = {})
118
+ path = CloudServers.paginate(options).empty? ? "#{svrmgmtpath}/servers/detail" : "#{svrmgmtpath}/servers/detail?#{CloudServers.paginate(options)}"
119
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
107
120
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
108
121
  CloudServers.symbolize_keys(JSON.parse(response.body)["servers"])
109
122
  end
@@ -149,11 +162,19 @@ module CloudServers
149
162
  # Returns an array of hashes listing available server images that you have access too, including stock Cloud Servers images and
150
163
  # any that you have created. The "id" key in the hash can be used where imageId is required.
151
164
  #
165
+ # You can also provide :limit and :offset parameters to handle pagination.
166
+ #
152
167
  # >> cs.list_images
153
168
  # => [{:name=>"CentOS 5.2", :id=>2, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},
154
169
  # {:name=>"Gentoo 2008.0", :id=>3, :updated=>"2009-07-20T09:16:57-05:00", :status=>"ACTIVE", :created=>"2009-07-20T09:16:57-05:00"},...
155
- def list_images
156
- response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/images/detail",svrmgmtport,svrmgmtscheme)
170
+ #
171
+ # >> cs.list_images(:limit => 3, :offset => 2)
172
+ # => [{:status=>"ACTIVE", :name=>"Fedora 11 (Leonidas)", :updated=>"2009-12-08T13:50:45-06:00", :id=>13},
173
+ # {:status=>"ACTIVE", :name=>"CentOS 5.3", :updated=>"2009-08-26T14:59:52-05:00", :id=>7},
174
+ # {:status=>"ACTIVE", :name=>"CentOS 5.4", :updated=>"2009-12-16T01:02:17-06:00", :id=>187811}]
175
+ def list_images(options = {})
176
+ path = CloudServers.paginate(options).empty? ? "#{svrmgmtpath}/images/detail" : "#{svrmgmtpath}/images/detail?#{CloudServers.paginate(options)}"
177
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
157
178
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
158
179
  CloudServers.symbolize_keys(JSON.parse(response.body)['images'])
159
180
  end
@@ -170,11 +191,19 @@ module CloudServers
170
191
 
171
192
  # Returns an array of hashes listing all available server flavors. The :id key in the hash can be used when flavorId is required.
172
193
  #
194
+ # You can also provide :limit and :offset parameters to handle pagination.
195
+ #
173
196
  # >> cs.list_flavors
174
197
  # => [{:name=>"256 server", :id=>1, :ram=>256, :disk=>10},
175
198
  # {:name=>"512 server", :id=>2, :ram=>512, :disk=>20},...
176
- def list_flavors
177
- response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/flavors/detail",svrmgmtport,svrmgmtscheme)
199
+ #
200
+ # >> cs.list_flavors(:limit => 3, :offset => 2)
201
+ # => [{:ram=>1024, :disk=>40, :name=>"1GB server", :id=>3},
202
+ # {:ram=>2048, :disk=>80, :name=>"2GB server", :id=>4},
203
+ # {:ram=>4096, :disk=>160, :name=>"4GB server", :id=>5}]
204
+ def list_flavors(options = {})
205
+ path = CloudServers.paginate(options).empty? ? "#{svrmgmtpath}/flavors/detail" : "#{svrmgmtpath}/flavors/detail?#{CloudServers.paginate(options)}"
206
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
178
207
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
179
208
  CloudServers.symbolize_keys(JSON.parse(response.body)['flavors'])
180
209
  end
@@ -191,10 +220,13 @@ module CloudServers
191
220
 
192
221
  # Returns an array of hashes for all Shared IP Groups that are available. The :id key can be used to find that specific object.
193
222
  #
223
+ # You can also provide :limit and :offset parameters to handle pagination.
224
+ #
194
225
  # >> cs.list_shared_ip_groups
195
226
  # => [{:name=>"New Group", :id=>127}]
196
- def list_shared_ip_groups
197
- response = csreq("GET",svrmgmthost,"#{svrmgmtpath}/shared_ip_groups/detail",svrmgmtport,svrmgmtscheme)
227
+ def list_shared_ip_groups(options = {})
228
+ path = CloudServers.paginate(options).empty? ? "#{svrmgmtpath}/shared_ip_groups/detail" : "#{svrmgmtpath}/shared_ip_groups/detail?#{CloudServers.paginate(options)}"
229
+ response = csreq("GET",svrmgmthost,path,svrmgmtport,svrmgmtscheme)
198
230
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
199
231
  CloudServers.symbolize_keys(JSON.parse(response.body)['sharedIpGroups'])
200
232
  end
@@ -303,4 +335,4 @@ module CloudServers
303
335
  end
304
336
 
305
337
  end
306
- end
338
+ end
@@ -1,37 +1,50 @@
1
1
  module CloudServers
2
2
  class Exception
3
+
4
+ class CloudServersError < StandardError
5
+
6
+ attr_reader :response_body
7
+ attr_reader :response_code
8
+
9
+ def initialize(message, code, response_body)
10
+ @response_code=code
11
+ @response_body=response_body
12
+ super(message)
13
+ end
14
+
15
+ end
3
16
 
4
- class CloudServersFault < StandardError # :nodoc:
17
+ class CloudServersFault < CloudServersError # :nodoc:
5
18
  end
6
- class ServiceUnavailable < StandardError # :nodoc:
19
+ class ServiceUnavailable < CloudServersError # :nodoc:
7
20
  end
8
- class Unauthorized < StandardError # :nodoc:
21
+ class Unauthorized < CloudServersError # :nodoc:
9
22
  end
10
- class BadRequest < StandardError # :nodoc:
23
+ class BadRequest < CloudServersError # :nodoc:
11
24
  end
12
- class OverLimit < StandardError # :nodoc:
25
+ class OverLimit < CloudServersError # :nodoc:
13
26
  end
14
- class BadMediaType < StandardError # :nodoc:
27
+ class BadMediaType < CloudServersError # :nodoc:
15
28
  end
16
- class BadMethod < StandardError # :nodoc:
29
+ class BadMethod < CloudServersError # :nodoc:
17
30
  end
18
- class ItemNotFound < StandardError # :nodoc:
31
+ class ItemNotFound < CloudServersError # :nodoc:
19
32
  end
20
- class BuildInProgress < StandardError # :nodoc:
33
+ class BuildInProgress < CloudServersError # :nodoc:
21
34
  end
22
- class ServerCapacityUnavailable < StandardError # :nodoc:
35
+ class ServerCapacityUnavailable < CloudServersError # :nodoc:
23
36
  end
24
- class BackupOrResizeInProgress < StandardError # :nodoc:
37
+ class BackupOrResizeInProgress < CloudServersError # :nodoc:
25
38
  end
26
- class ResizeNotAllowed < StandardError # :nodoc:
39
+ class ResizeNotAllowed < CloudServersError # :nodoc:
27
40
  end
28
- class NotImplemented < StandardError # :nodoc:
41
+ class NotImplemented < CloudServersError # :nodoc:
42
+ end
43
+ class Other < CloudServersError # :nodoc:
29
44
  end
30
45
 
31
46
  # Plus some others that we define here
32
47
 
33
- class Other < StandardError # :nodoc:
34
- end
35
48
  class ExpiredAuthToken < StandardError # :nodoc:
36
49
  end
37
50
  class MissingArgument < StandardError # :nodoc:
@@ -52,12 +65,17 @@ module CloudServers
52
65
  # proper error. Note that all exceptions are scoped in the CloudServers::Exception namespace.
53
66
  def self.raise_exception(response)
54
67
  return if response.code =~ /^20.$/
55
- fault,info = JSON.parse(response.body).first
56
68
  begin
69
+ fault = nil
70
+ info = nil
71
+ JSON.parse(response.body).each_pair do |key, val|
72
+ fault=key
73
+ info=val
74
+ end
57
75
  exception_class = self.const_get(fault[0,1].capitalize+fault[1,fault.length])
58
- raise exception_class, info["message"]
76
+ raise exception_class.new(info["message"], response.code, response.body)
59
77
  rescue NameError
60
- raise CloudServers::Exception::Other, "The server returned status #{response.code}"
78
+ raise CloudServers::Exception::Other.new("The server returned status #{response.code}", response.code, response.body)
61
79
  end
62
80
  end
63
81
 
@@ -249,6 +249,37 @@ module CloudServers
249
249
  CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
250
250
  true
251
251
  end
252
-
252
+
253
+ # Share IP between servers in Shared IP group.
254
+ # Takes a hash of the form: {:sharedIpGroupId => "1234", :ipAddress => "67.23.10.132", :configureServer => false} as an argument.
255
+ # The :sharedIpGroupId key is required.
256
+ # The :ipAddress key is required.
257
+ # The :configureServer key is optional and defaults to false.
258
+ #
259
+ # >> server.share_ip(:sharedIpGroupId => 100, :ipAddress => "67.23.10.132")
260
+ # => true
261
+ def share_ip(options)
262
+ raise CloudServers::Exception::MissingArgument, "Shared IP Group ID must be supplied" unless options[:sharedIpGroupId]
263
+ raise CloudServers::Exception::MissingArgument, "Ip Address must be supplied" unless options[:ipAddress]
264
+ options[:configureServer] = false if options[:configureServer].nil?
265
+ data = JSON.generate(:shareIp => {:sharedIpGroupId => options[:sharedIpGroupId], :configureServer => options[:configureServer]})
266
+ response = @connection.csreq("PUT",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/ips/public/#{options[:ipAddress]}",@svrmgmtport,@svrmgmtscheme,{'content-type' => 'application/json'},data)
267
+ CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
268
+ true
269
+ end
270
+
271
+ # Unshare an IP address.
272
+ # Takes a hash of the form: {:ipAddress => "67.23.10.132"} as an argument.
273
+ # The :ipAddress key is required.
274
+ #
275
+ # >> server.unshare_ip(:ipAddress => "67.23.10.132")
276
+ # => true
277
+ def unshare_ip(options)
278
+ raise CloudServers::Exception::MissingArgument, "Ip Address must be supplied" unless options[:ipAddress]
279
+ response = @connection.csreq("DELETE",@svrmgmthost,"#{@svrmgmtpath}/servers/#{URI.encode(self.id.to_s)}/ips/public/#{options[:ipAddress]}",@svrmgmtport,@svrmgmtscheme)
280
+ CloudServers::Exception.raise_exception(response) unless response.code.match(/^20.$/)
281
+ true
282
+ end
283
+
253
284
  end
254
- end
285
+ end
data/lib/cloudservers.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # == Cloud Servers API
4
4
  # ==== Connects Ruby Applications to Rackspace's {Cloud Servers service}[http://www.rackspacecloud.com/cloud_hosting_products/servers]
5
- # By H. Wade Minter <wade.minter@rackspace.com> and Mike Mayo <mike.mayo@rackspace.com>
5
+ # By H. Wade Minter <minter@lunenburg.org> and Mike Mayo <mike.mayo@rackspace.com>
6
6
  #
7
7
  # See COPYING for license information.
8
8
  # Copyright (c) 2009, Rackspace US, Inc.
@@ -23,6 +23,7 @@ module CloudServers
23
23
  require 'uri'
24
24
  require 'rubygems'
25
25
  require 'json'
26
+ require 'date'
26
27
 
27
28
  unless "".respond_to? :each_char
28
29
  require "jcode"
@@ -79,5 +80,12 @@ module CloudServers
79
80
  end
80
81
  end
81
82
 
83
+ def self.paginate(options = {})
84
+ path_args = []
85
+ path_args.push(URI.encode("limit=#{options[:limit]}")) if options[:limit]
86
+ path_args.push(URI.encode("offset=#{options[:offset]}")) if options[:offset]
87
+ path_args.join("&")
88
+ end
89
+
82
90
 
83
91
  end
@@ -1,16 +1,15 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class CloudserversAuthenticationTest < Test::Unit::TestCase
4
-
5
-
4
+
6
5
  def test_good_authentication
7
- response = {'x-cdn-management-url' => 'http://cdn.example.com/path', 'x-storage-url' => 'http://cdn.example.com/storage', 'authtoken' => 'dummy_token'}
6
+ response = {'x-server-management-url' => 'http://server-manage.example.com/path', 'x-auth-token' => 'dummy_token'}
8
7
  response.stubs(:code).returns('204')
9
8
  server = mock(:use_ssl= => true, :verify_mode= => true, :start => true, :finish => true)
10
9
  server.stubs(:get).returns(response)
11
10
  Net::HTTP.stubs(:new).returns(server)
12
- @connection = stub(:authuser => 'dummy_user', :authkey => 'dummy_key', :cdnmgmthost= => true, :cdnmgmtpath= => true, :cdnmgmtport= => true, :cdnmgmtscheme= => true, :storagehost= => true, :storagepath= => true, :storageport= => true, :storagescheme= => true, :authtoken= => true, :authok= => true)
13
- result = CloudServers::Authentication.new(@connection)
11
+ connection = stub(:authuser => 'bad_user', :authkey => 'bad_key', :authok= => true, :authtoken= => true, :svrmgmthost= => "", :svrmgmtpath= => "", :svrmgmtpath => "", :svrmgmtport= => "", :svrmgmtscheme= => "", :proxy_host => nil, :proxy_port => nil)
12
+ result = CloudServers::Authentication.new(connection)
14
13
  assert_equal result.class, CloudServers::Authentication
15
14
  end
16
15
 
@@ -20,18 +19,18 @@ class CloudserversAuthenticationTest < Test::Unit::TestCase
20
19
  server = mock(:use_ssl= => true, :verify_mode= => true, :start => true)
21
20
  server.stubs(:get).returns(response)
22
21
  Net::HTTP.stubs(:new).returns(server)
23
- @connection = stub(:authuser => 'bad_user', :authkey => 'bad_key', :authok= => true, :authtoken= => true)
24
- assert_raises(AuthenticationException) do
25
- result = CloudServers::Authentication.new(@connection)
22
+ connection = stub(:authuser => 'bad_user', :authkey => 'bad_key', :authok= => true, :authtoken= => true, :proxy_host => nil, :proxy_port => nil)
23
+ assert_raises(CloudServers::Exception::Authentication) do
24
+ result = CloudServers::Authentication.new(connection)
26
25
  end
27
26
  end
28
27
 
29
28
  def test_bad_hostname
30
- Net::HTTP.stubs(:new).raises(ConnectionException)
31
- @connection = stub(:authuser => 'bad_user', :authkey => 'bad_key', :authok= => true, :authtoken= => true)
32
- assert_raises(ConnectionException) do
33
- result = CloudServers::Authentication.new(@connection)
29
+ Net::HTTP.stubs(:new).raises(CloudServers::Exception::Connection)
30
+ connection = stub(:authuser => 'bad_user', :authkey => 'bad_key', :authok= => true, :authtoken= => true, :proxy_host => nil, :proxy_port => nil)
31
+ assert_raises(CloudServers::Exception::Connection) do
32
+ result = CloudServers::Authentication.new(connection)
34
33
  end
35
34
  end
36
35
 
37
- end
36
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class CloudServersConnectionTest < Test::Unit::TestCase
4
+
5
+ def test_init_connection_no_credentials
6
+ assert_raises(CloudServers::Exception::Authentication) do
7
+ conn = CloudServers::Connection.new(:api_key => "AABBCCDD11")
8
+ end
9
+ end
10
+
11
+ def test_init_connection_no_password
12
+ assert_raises(CloudServers::Exception::Authentication) do
13
+ conn = CloudServers::Connection.new(:username => "test_account")
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class CloudServersExceptionTest < Test::Unit::TestCase
4
+
5
+ def test_400_cloud_servers_fault
6
+ response = mock()
7
+ response.stubs(:code => "400", :body => "{\"cloudServersFault\":{\"message\":\"422 Unprocessable Entity: We could not process your request at this time. We have been notified and are looking into the issue. [E03]\",\"details\":\"com.rackspace.cloud.service.servers.CloudServersFault: Fault occured\",\"code\":400}}" )
8
+ exception=nil
9
+ begin
10
+ CloudServers::Exception.raise_exception(response)
11
+ rescue Exception => e
12
+ exception=e
13
+ end
14
+ assert_equal(CloudServers::Exception::CloudServersFault, e.class)
15
+ assert_equal("400", e.response_code)
16
+ assert_not_nil(e.response_body)
17
+ end
18
+
19
+ def test_413_over_limit
20
+ response = mock()
21
+ response.stubs(:code => "413", :body => "{\"overLimit\":{\"message\":\"Too many requests...\",\"code\":413,\"retryAfter\":\"2010-08-25T10:47:57.890-05:00\"}}")
22
+ exception=nil
23
+ begin
24
+ CloudServers::Exception.raise_exception(response)
25
+ rescue Exception => e
26
+ exception=e
27
+ end
28
+ assert_equal(CloudServers::Exception::OverLimit, e.class)
29
+ assert_equal("413", e.response_code)
30
+ assert_not_nil(e.response_body)
31
+ end
32
+
33
+ def test_other
34
+ response = mock()
35
+ body="{\"blahblah\":{\"message\":\"Failed...\",\"code\":500}}"
36
+ response.stubs(:code => "500", :body => body)
37
+ exception=nil
38
+ begin
39
+ CloudServers::Exception.raise_exception(response)
40
+ rescue Exception => e
41
+ exception=e
42
+ end
43
+ assert_equal(CloudServers::Exception::Other, exception.class)
44
+ assert_equal("500", exception.response_code)
45
+ assert_not_nil(exception.response_body)
46
+ assert_equal(body, exception.response_body)
47
+ end
48
+
49
+ end
@@ -0,0 +1,176 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class CloudServersServersTest < Test::Unit::TestCase
4
+
5
+ include TestConnection
6
+
7
+ def setup
8
+ @conn=get_test_connection
9
+ end
10
+
11
+ def test_list_servers
12
+
13
+ json_response = %{{
14
+ "servers" : [
15
+ {
16
+ "id" : 1234,
17
+ "name" : "sample-server",
18
+ "imageId" : 2,
19
+ "flavorId" : 1,
20
+ "hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0",
21
+ "status" : "BUILD",
22
+ "progress" : 60,
23
+ "addresses" : {
24
+ "public" : [
25
+ "67.23.10.132",
26
+ "67.23.10.131"
27
+ ],
28
+ "private" : [
29
+ "10.176.42.16"
30
+ ]
31
+ },
32
+ "metadata" : {
33
+ "Server Label" : "Web Head 1",
34
+ "Image Version" : "2.1"
35
+ }
36
+ },
37
+ {
38
+ "id" : 5678,
39
+ "name" : "sample-server2",
40
+ "imageId" : 2,
41
+ "flavorId" : 1,
42
+ "hostId" : "9e107d9d372bb6826bd81d3542a419d6",
43
+ "status" : "ACTIVE",
44
+ "addresses" : {
45
+ "public" : [
46
+ "67.23.10.133"
47
+ ],
48
+ "private" : [
49
+ "10.176.42.17"
50
+ ]
51
+ },
52
+ "metadata" : {
53
+ "Server Label" : "DB 1"
54
+ }
55
+ }
56
+ ]
57
+ }}
58
+ response = mock()
59
+ response.stubs(:code => "200", :body => json_response)
60
+
61
+ @conn.stubs(:csreq).returns(response)
62
+ servers=@conn.list_servers
63
+
64
+ assert_equal 2, servers.size
65
+ assert_equal 1234, servers[0][:id]
66
+ assert_equal "sample-server", servers[0][:name]
67
+
68
+ end
69
+
70
+ def test_get_server
71
+
72
+ server=get_test_server
73
+ assert "sample-server", server.name
74
+ assert "2", server.imageId
75
+ assert "1", server.flavorId
76
+ assert "e4d909c290d0fb1ca068ffaddf22cbd0", server.hostId
77
+ assert "BUILD", server.status
78
+ assert "60", server.progress
79
+ assert "67.23.10.132", server.addresses[:public][0]
80
+ assert "67.23.10.131", server.addresses[:public][1]
81
+ assert "10.176.42.16", server.addresses[:private][1]
82
+
83
+ end
84
+
85
+ def test_share_ip
86
+
87
+ server=get_test_server
88
+ response = mock()
89
+ response.stubs(:code => "200")
90
+
91
+ @conn.stubs(:csreq).returns(response)
92
+
93
+ assert server.share_ip(:sharedIpGroupId => 100, :ipAddress => "67.23.10.132")
94
+ end
95
+
96
+ def test_share_ip_requires_shared_ip_group_id
97
+
98
+ server=get_test_server
99
+
100
+ assert_raises(CloudServers::Exception::MissingArgument) do
101
+ assert server.share_ip(:ipAddress => "67.23.10.132")
102
+ end
103
+
104
+ end
105
+
106
+ def test_share_ip_requires_ip_address
107
+
108
+ server=get_test_server
109
+
110
+ assert_raises(CloudServers::Exception::MissingArgument) do
111
+ assert server.share_ip(:sharedIpGroupId => 100)
112
+ end
113
+
114
+ end
115
+
116
+ def test_unshare_ip
117
+
118
+ server=get_test_server
119
+ response = mock()
120
+ response.stubs(:code => "200")
121
+
122
+ @conn.stubs(:csreq).returns(response)
123
+
124
+ assert server.unshare_ip(:ipAddress => "67.23.10.132")
125
+
126
+ end
127
+
128
+ def test_unshare_ip_requires_ip_address
129
+
130
+ server=get_test_server
131
+
132
+ assert_raises(CloudServers::Exception::MissingArgument) do
133
+ assert server.share_ip({})
134
+ end
135
+
136
+ end
137
+
138
+ private
139
+ def get_test_server
140
+
141
+ json_response = %{{
142
+ "server" : {
143
+ "id" : 1234,
144
+ "name" : "sample-server",
145
+ "imageId" : 2,
146
+ "flavorId" : 1,
147
+ "hostId" : "e4d909c290d0fb1ca068ffaddf22cbd0",
148
+ "status" : "BUILD",
149
+ "progress" : 60,
150
+ "addresses" : {
151
+ "public" : [
152
+ "67.23.10.132",
153
+ "67.23.10.131"
154
+ ],
155
+ "private" : [
156
+ "10.176.42.16"
157
+ ]
158
+ },
159
+ "metadata" : {
160
+ "Server Label" : "Web Head 1",
161
+ "Image Version" : "2.1"
162
+ }
163
+ }
164
+ }}
165
+
166
+ response = mock()
167
+ response.stubs(:code => "200", :body => json_response)
168
+
169
+ @conn=get_test_connection
170
+
171
+ @conn.stubs(:csreq).returns(response)
172
+ return @conn.server(1234)
173
+
174
+ end
175
+
176
+ end
data/test/test_helper.rb CHANGED
@@ -3,3 +3,19 @@ $:.unshift File.dirname(__FILE__) + '/../lib'
3
3
  require 'cloudservers'
4
4
  require 'rubygems'
5
5
  require 'mocha'
6
+
7
+ module TestConnection
8
+
9
+ def get_test_connection
10
+
11
+ conn_response = {'x-server-management-url' => 'http://server-manage.example.com/path', 'x-auth-token' => 'dummy_token'}
12
+ conn_response.stubs(:code).returns('204')
13
+ server = mock(:use_ssl= => true, :verify_mode= => true, :start => true, :finish => true)
14
+ server.stubs(:get).returns(conn_response)
15
+ Net::HTTP.stubs(:new).returns(server)
16
+
17
+ CloudServers::Connection.new(:username => "test_account", :api_key => "AABBCCDD11")
18
+
19
+ end
20
+
21
+ end
metadata CHANGED
@@ -1,37 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudservers
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 19
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 2
8
+ - 3
8
9
  - 0
9
- version: 0.2.0
10
+ version: 0.3.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - H. Wade Minter
13
14
  - Mike Mayo
15
+ - Dan Prince
14
16
  autorequire:
15
17
  bindir: bin
16
18
  cert_chain: []
17
19
 
18
- date: 2010-05-06 00:00:00 -07:00
20
+ date: 2010-10-21 00:00:00 -04:00
19
21
  default_executable:
20
22
  dependencies:
21
23
  - !ruby/object:Gem::Dependency
22
24
  name: json
23
25
  prerelease: false
24
26
  requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
25
28
  requirements:
26
29
  - - ">="
27
30
  - !ruby/object:Gem::Version
31
+ hash: 3
28
32
  segments:
29
33
  - 0
30
34
  version: "0"
31
35
  type: :runtime
32
36
  version_requirements: *id001
33
37
  description: A Ruby API to version 1.0 of the Rackspace Cloud Servers product.
34
- email: wade.minter@rackspace.com
38
+ email: minter@lunenburg.org
35
39
  executables: []
36
40
 
37
41
  extensions: []
@@ -57,6 +61,9 @@ files:
57
61
  - lib/cloudservers/server.rb
58
62
  - lib/cloudservers/shared_ip_group.rb
59
63
  - test/cloudservers_authentication_test.rb
64
+ - test/cloudservers_connection_test.rb
65
+ - test/cloudservers_exception_test.rb
66
+ - test/cloudservers_servers_test.rb
60
67
  - test/test_helper.rb
61
68
  has_rdoc: true
62
69
  homepage: http://github.com/rackspace/cloudservers
@@ -68,26 +75,33 @@ rdoc_options:
68
75
  require_paths:
69
76
  - lib
70
77
  required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
71
79
  requirements:
72
80
  - - ">="
73
81
  - !ruby/object:Gem::Version
82
+ hash: 3
74
83
  segments:
75
84
  - 0
76
85
  version: "0"
77
86
  required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
78
88
  requirements:
79
89
  - - ">="
80
90
  - !ruby/object:Gem::Version
91
+ hash: 3
81
92
  segments:
82
93
  - 0
83
94
  version: "0"
84
95
  requirements: []
85
96
 
86
97
  rubyforge_project:
87
- rubygems_version: 1.3.6
98
+ rubygems_version: 1.3.7
88
99
  signing_key:
89
100
  specification_version: 3
90
101
  summary: Rackspace Cloud Servers Ruby API
91
102
  test_files:
92
103
  - test/cloudservers_authentication_test.rb
104
+ - test/cloudservers_connection_test.rb
105
+ - test/cloudservers_exception_test.rb
106
+ - test/cloudservers_servers_test.rb
93
107
  - test/test_helper.rb