rackspace-cloud 0.5

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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.5 2009-10-11
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,22 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/rackspace.rb
7
+ lib/rackspace-cloud.rb
8
+ lib/rackspace/connection.rb
9
+ lib/rackspace/exceptions.rb
10
+ lib/rackspace/cloud_servers/base.rb
11
+ lib/rackspace/cloud_servers/server.rb
12
+ lib/rackspace/cloud_servers/flavor.rb
13
+ lib/rackspace/cloud_servers/image.rb
14
+ script/console
15
+ script/destroy
16
+ script/generate
17
+ test/test_base.rb
18
+ test/test_connection.rb
19
+ test/test_flavor.rb
20
+ test/test_helper.rb
21
+ test/test_image.rb
22
+ test/test_server.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,4 @@
1
+
2
+ For more information on rackspace-cloud, see http://github.com/edraper/rackspace-cloud
3
+
4
+
data/README.rdoc ADDED
@@ -0,0 +1,72 @@
1
+ = rackspace-cloud
2
+
3
+ * http://github.com/edraper/rackspace-cloud
4
+
5
+ == DESCRIPTION:
6
+
7
+ rackspace-cloud is a Ruby gem that provides easy, simple access to the Rackspace Cloud APIs, and specifically the Cloud Servers API.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Currently just has support for Cloud Servers, and not Cloud Files.
12
+
13
+ Server addresses, IP groups, backup schedules, and reboots/rebuilds/resizes not yet implemented for Cloud Servers.
14
+
15
+ == SYNOPSIS:
16
+
17
+ To initialize:
18
+
19
+ Rackspace::Connection.init "user", "key"
20
+
21
+ Optionally, you can specify a version of the API to use too (defaults to "v1.0"):
22
+
23
+ Rackspace::Connection.init "user", "key", "v1.1"
24
+
25
+ To authenticate:
26
+
27
+ Rackspace::Connection.authenticate
28
+
29
+ For Cloud Servers:
30
+
31
+ Rackspace::CloudServers::Server.all
32
+
33
+ Rackspace::CloudServers::Server.create(:name => "api-test", :imageId => 8, :flavorId => 1)
34
+
35
+ Rackspace::CloudServers::Image.all
36
+
37
+ Rackspace::CloudServers::Flavor.all
38
+
39
+ == REQUIREMENTS:
40
+
41
+ rest-client (1.0.3+)
42
+
43
+ activesupport (2.3.4+)
44
+
45
+ == INSTALL:
46
+
47
+ gem install rackspace-cloud
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2009 Elliott Draper <el@ejdraper.com>
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require File.join(File.dirname(__FILE__), "lib", "rackspace-cloud")
6
+
7
+ Hoe.plugin :newgem
8
+
9
+ $hoe = Hoe.spec 'rackspace-cloud' do
10
+ self.developer 'Elliott Draper', 'el@ejdraper.com'
11
+ self.post_install_message = 'PostInstall.txt'
12
+ self.rubyforge_name = self.name
13
+ self.extra_deps = [['rest-client','>= 1.0.3'], ['activesupport','>= 2.3.4']]
14
+ end
15
+
16
+ require 'newgem/tasks'
17
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "rackspace")
data/lib/rackspace.rb ADDED
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require "rubygems"
4
+ gem "activesupport"
5
+ require "active_support"
6
+ gem "rest-client"
7
+ require "rest_client"
8
+
9
+ module Rackspace
10
+ VERSION = '0.5'
11
+
12
+ module CloudServers
13
+ end
14
+ end
15
+
16
+ require File.join(File.dirname(__FILE__), "rackspace", "exceptions")
17
+ require File.join(File.dirname(__FILE__), "rackspace", "connection")
18
+ require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "base")
19
+ require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "server")
20
+ require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "flavor")
21
+ require File.join(File.dirname(__FILE__), "rackspace", "cloud_servers", "image")
@@ -0,0 +1,133 @@
1
+ class Rackspace::CloudServers::Base
2
+ attr_accessor :id
3
+
4
+ # The resource can be established with a hash of parameters
5
+ def initialize(attribs = {})
6
+ set_attributes(attribs)
7
+ end
8
+
9
+ # This sets the relevant accessors using the specified attributes
10
+ def set_attributes(attribs)
11
+ attribs.each_pair do |key, value|
12
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
13
+ end
14
+ end
15
+
16
+ # This returns true if the record hasn't yet been persisted, false otherwise
17
+ def new_record?
18
+ self.id.nil?
19
+ end
20
+
21
+ # This creates the record if it is new, otherwise it attempts to update the record
22
+ def save
23
+ self.new_record? ? create : update
24
+ end
25
+
26
+ # This creates the new record using the API
27
+ def create
28
+ set_attributes(JSON.parse(Rackspace::Connection.post(self.class.resource_url, {self.class.resource.singularize => JSON.parse(self.to_json)}))[self.class.resource.singularize])
29
+ true
30
+ end
31
+
32
+ # This updates the existing record using the API
33
+ def update
34
+ attribs = JSON.parse(self.to_json)
35
+ unless self.attributes_for_update.nil?
36
+ attribs.keys.each do |key|
37
+ attribs.delete(key) unless self.attributes_for_update.include?(key.to_sym)
38
+ end
39
+ end
40
+ Rackspace::Connection.put(self.class.resource_url(self.id), {self.class.resource.singularize => attribs})
41
+ true
42
+ end
43
+
44
+ # This deletes the record using the API
45
+ def destroy
46
+ Rackspace::Connection.delete(self.class.resource_url(self.id))
47
+ true
48
+ end
49
+
50
+ # These are the attributes used for the update operations
51
+ # Empty array means no properties will be updated, but this can be overridden with
52
+ # nil (all properties are updated), or an explicit array of properties that can be updated
53
+ def attributes_for_update
54
+ []
55
+ end
56
+
57
+ # This reloads the current object with the latest persisted data
58
+ def reload
59
+ return false if self.new_record?
60
+ result = Rackspace::Connection.get(self.class.resource_url(self.id))
61
+ return nil if result.to_s.blank?
62
+ json = JSON.parse(result.to_s)
63
+ self.set_attributes(json[self.class.resource.singularize])
64
+ self
65
+ end
66
+
67
+ class << self
68
+ # This returns the name of the resource, used for the API URLs
69
+ def resource
70
+ self.name.split("::").last.tableize
71
+ end
72
+
73
+ # This returns the resource URL
74
+ def resource_url(id = nil)
75
+ root = "#{Rackspace::Connection.server_management_url}/#{self.resource}"
76
+ id.nil? ? root : "#{root}/#{id}"
77
+ end
78
+
79
+ # This returns all records for the resource
80
+ def all
81
+ self.find
82
+ end
83
+
84
+ # This returns the first record for the resource
85
+ def first
86
+ self.find(:first)
87
+ end
88
+
89
+ # This returns the last record for the resource
90
+ def last
91
+ self.find(:last)
92
+ end
93
+
94
+ # This finds all records for the resource, or a specific resource by ID
95
+ def find(action = :all)
96
+ result = case action
97
+ when :all
98
+ Rackspace::Connection.get "#{self.resource_url}/detail"
99
+ when :first
100
+ Rackspace::Connection.get "#{self.resource_url}/detail"
101
+ when :last
102
+ Rackspace::Connection.get "#{self.resource_url}/detail"
103
+ else
104
+ Rackspace::Connection.get self.resource_url(action)
105
+ end
106
+ return nil if result.to_s.blank?
107
+ json = JSON.parse(result.to_s)
108
+ case action
109
+ when :all
110
+ json[self.resource].collect { |h| self.new(h) }
111
+ when :first
112
+ json[self.resource].collect { |h| self.new(h) }.first
113
+ when :last
114
+ json[self.resource].collect { |h| self.new(h) }.last
115
+ else
116
+ self.new(json[self.resource.singularize])
117
+ end
118
+ end
119
+
120
+ # This returns the amount of records for this resource
121
+ def count
122
+ records = self.all
123
+ records.nil? ? 0 : records.length
124
+ end
125
+
126
+ # This creates and saves a resource with the specified attributes in one call
127
+ def create(attribs = {})
128
+ o = self.new(attribs)
129
+ o.save
130
+ o
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,13 @@
1
+ class Rackspace::CloudServers::Flavor < Rackspace::CloudServers::Base
2
+ attr_accessor :name, :ram, :disk
3
+
4
+ # Saving (create/update) isn't allowed for flavors
5
+ def save
6
+ false
7
+ end
8
+
9
+ # Deletion isn't allowed for flavors
10
+ def destroy
11
+ false
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ class Rackspace::CloudServers::Image < Rackspace::CloudServers::Base
2
+ attr_accessor :name, :serverId, :updated, :created, :status, :progress
3
+
4
+ # Updating isn't allowed for images
5
+ def update
6
+ false
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ class Rackspace::CloudServers::Server < Rackspace::CloudServers::Base
2
+ attr_accessor :name, :imageId, :flavorId, :hostId, :status, :progress, :addresses, :metadata, :personality, :adminPass
3
+
4
+ # Overriding the update attributes so that just name and adminPass are persisted on an update
5
+ def attributes_for_update
6
+ [:name, :adminPass]
7
+ end
8
+ end
@@ -0,0 +1,115 @@
1
+ class Rackspace::Connection
2
+ AUTH_URL = "https://auth.api.rackspacecloud.com"
3
+ VERSION_URL = "https://servers.api.rackspacecloud.com"
4
+
5
+ class << self
6
+ # This initializes the Rackspace environment with the data necessary to make API calls
7
+ def init(user, key, version = "v1.0")
8
+ @user = user
9
+ @key = key
10
+ @version = version
11
+ raise Rackspace::InvalidVersion unless self.versions.include?(version)
12
+ @initialized = true
13
+ end
14
+
15
+ # This returns the API user being used for calls
16
+ def api_user
17
+ @user
18
+ end
19
+
20
+ # This returns the API key being used for calls
21
+ def api_key
22
+ @key
23
+ end
24
+
25
+ # This returns the API version being used for calls
26
+ def api_version
27
+ @version
28
+ end
29
+
30
+ # This returns whether or not we've been initialized yet
31
+ def initialized?
32
+ @initialized || false
33
+ end
34
+
35
+ # This authenticates with Rackspace and returns the information necessary to make subsequent authenticated calls to the API
36
+ def authenticate
37
+ raise Rackspace::NotInitialized unless self.initialized?
38
+ headers = RestClient::Request.execute(:method => :get, :url => "#{AUTH_URL}/#{self.api_version}", :headers => {"X-Auth-User" => self.api_user, "X-Auth-Key" => self.api_key}, :raw_response => true).headers
39
+ {:auth_token => headers[:x_auth_token], :storage_url => headers[:x_storage_url], :server_management_url => headers[:x_server_management_url], :cdn_management_url => headers[:x_cdn_management_url]}
40
+ end
41
+
42
+ # These are default headers we need to use on all requests
43
+ def default_headers
44
+ {:accept => "application/json", :content_type => "application/json"}
45
+ end
46
+
47
+ # This returns the available versions of the API
48
+ def versions
49
+ JSON.parse(RestClient.get("#{VERSION_URL}/.json", self.default_headers))["versions"].collect { |v| v["id"] }.uniq
50
+ end
51
+
52
+ # This caches the authentication response for subsequent usage
53
+ def auth_response
54
+ @auth_response ||= self.authenticate
55
+ end
56
+
57
+ # This is the auth token provided by Rackspace after a successful authentication
58
+ def auth_token
59
+ self.auth_response[:auth_token]
60
+ end
61
+
62
+ # This returns the root URL for Cloud Files API queries (not yet implemented)
63
+ def storage_url
64
+ self.auth_response[:storage_url]
65
+ end
66
+
67
+ # This returns the root URL for Cloud Servers API queries
68
+ def server_management_url
69
+ self.auth_response[:server_management_url]
70
+ end
71
+
72
+ # This returns the root URL for CDN Cloud Files API queries (not yet implemented)
73
+ def cdn_management_url
74
+ self.auth_response[:cdn_management_url]
75
+ end
76
+
77
+ # This performs a basic GET request using the supplied URL and headers
78
+ def get(url, headers = {})
79
+ http :get, "#{url}.json", headers
80
+ end
81
+
82
+ # This performs a basic POST request using the supplied URL, payload and headers
83
+ def post(url, payload = {}, headers = {})
84
+ http :post, "#{url}.json", payload.to_json, headers
85
+ end
86
+
87
+ # This performs a basic PUT request using the supplied URL, payload and headers
88
+ def put(url, payload = {}, headers = {})
89
+ http :put, "#{url}.json", payload.to_json, headers
90
+ end
91
+
92
+ # This performs a basic DELETE request using the supplied URL and headers
93
+ def delete(url, headers = {})
94
+ http :delete, "#{url}.json", headers
95
+ end
96
+
97
+ # This will perform an HTTP call with the specified method, and arguments
98
+ # It will also pick up if the response is that the request was unauthorized, and will attempt
99
+ # the same request again after re-authenticating (in case the auth token has expired)
100
+ def http(method, *args)
101
+ args.last.merge!(self.default_headers).merge!("X-Auth-Token" => self.auth_token)
102
+ response = RestClient.send(method, *args)
103
+ @retried = false
104
+ response
105
+ rescue RestClient::Unauthorized
106
+ @auth_response = nil
107
+ if @retried
108
+ raise
109
+ else
110
+ @retried = true
111
+ retry
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,7 @@
1
+ module Rackspace
2
+ class InvalidVersion < Exception
3
+ end
4
+
5
+ class NotInitialized < Exception
6
+ end
7
+ end