cloudservers 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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