right_api 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README +0 -0
- data/Rakefile +37 -0
- data/VERSION.yml +4 -0
- data/install.rb +1 -0
- data/lib/right_api/accessible_fragment.rb +19 -0
- data/lib/right_api/http_server.rb +144 -0
- data/lib/right_api/rightscale.rb +148 -0
- data/lib/right_api.rb +1 -0
- data/right_api.gemspec +52 -0
- data/uninstall.rb +1 -0
- metadata +84 -0
data/.gitignore
ADDED
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "#{ENV['GITHUB'] ? 'moneypools-' : ''}right_api"
|
8
|
+
gem.summary = %Q{A ruby wrapper for the RightScale api}
|
9
|
+
gem.description = "A ruby wrapper for the RightScale api"
|
10
|
+
gem.email = "mpdev@businesslogic.com"
|
11
|
+
gem.homepage = "http://github.com/moneypools/right_api"
|
12
|
+
gem.authors = ["MoneyPools"]
|
13
|
+
gem.add_dependency('hpricot')
|
14
|
+
gem.add_dependency('activesupport')
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/rdoctask'
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
if File.exist?('VERSION.yml')
|
25
|
+
config = YAML.load(File.read('VERSION.yml'))
|
26
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
27
|
+
else
|
28
|
+
version = ""
|
29
|
+
end
|
30
|
+
|
31
|
+
rdoc.rdoc_dir = 'rdoc'
|
32
|
+
rdoc.title = "right_api #{version}"
|
33
|
+
rdoc.rdoc_files.include('README*')
|
34
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
35
|
+
end
|
36
|
+
|
37
|
+
task :default => ['gemspec', 'build']
|
data/VERSION.yml
ADDED
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AccessibleFragment
|
2
|
+
def initialize(data, connection)
|
3
|
+
@data = data
|
4
|
+
@connection = connection
|
5
|
+
end
|
6
|
+
|
7
|
+
def has_element?(elem)
|
8
|
+
!@data.search(elem).size.zero?
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_element(elem)
|
12
|
+
@data.search(elem).first.inner_text
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(method_name, *args)
|
16
|
+
name_with_dashes = method_name.to_s.gsub('_', '-')
|
17
|
+
has_element?(name_with_dashes) ? get_element(name_with_dashes) : super
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
class HttpServer
|
5
|
+
attr_accessor :response_error_checker, :handle_errors, :headers
|
6
|
+
attr_reader :last_response
|
7
|
+
|
8
|
+
def initialize(server_config, timeout)
|
9
|
+
@server_url, @port, @username, @password = server_config['server_url'], server_config['port'], server_config['username'], server_config['password']
|
10
|
+
@timeout = timeout
|
11
|
+
raise 'No configuration for timeout length' if @timeout.nil?
|
12
|
+
|
13
|
+
@handle_errors = true
|
14
|
+
@response_error_checker = Proc.new do |response, path|
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
@headers = {
|
19
|
+
'User-Agent' => 'Mozilla/4.0',
|
20
|
+
'Content-Type' => 'application/x-www-form-urlencoded',
|
21
|
+
'Connection' => 'Keep-Alive',
|
22
|
+
'X-API-VERSION' => RightScale::API_VERSION
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(resource, &block)
|
27
|
+
connect(resource, Net::HTTP::Delete, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(resource, &block)
|
31
|
+
connect(resource, Net::HTTP::Get, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_with_params(resource, params={}, &block)
|
35
|
+
connect(resource, Net::HTTP::Get, encode_params(params), &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def post(resource, params={}, &block)
|
39
|
+
connect(resource, Net::HTTP::Post, encode_params(params), &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def put(resource, params={}, &block)
|
43
|
+
connect(resource, Net::HTTP::Put, encode_params(params), &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_error_handling_disabled
|
47
|
+
original_handle_errors = handle_errors
|
48
|
+
self.handle_errors = false
|
49
|
+
|
50
|
+
result = yield
|
51
|
+
ensure
|
52
|
+
self.handle_errors = original_handle_errors
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def connect(resource, request_object, *args, &block)
|
58
|
+
uri = URI.parse url(resource)
|
59
|
+
req = request_object.new(uri.path, @headers)
|
60
|
+
req.basic_auth @username, @password if @username
|
61
|
+
|
62
|
+
response_data = nil
|
63
|
+
|
64
|
+
begin
|
65
|
+
create_http(uri).start do |http|
|
66
|
+
response, data = http.request(req, *args)
|
67
|
+
|
68
|
+
block.call(self, response) if block
|
69
|
+
|
70
|
+
@last_response = response_data = data_from(response)
|
71
|
+
end
|
72
|
+
rescue Timeout::Error
|
73
|
+
raise "A timeout error occured when connecting to #{resource}.\nThe timeout is currently set to #{@timeout} seconds."
|
74
|
+
rescue Errno::ECONNREFUSED
|
75
|
+
raise "Could not connect when connecting to #{resource} - the server (#{@server_url}) is down."
|
76
|
+
end
|
77
|
+
|
78
|
+
check_response_for_errors(response_data, uri.path) if handle_errors
|
79
|
+
|
80
|
+
response_data
|
81
|
+
end
|
82
|
+
|
83
|
+
def data_from(response)
|
84
|
+
case response
|
85
|
+
when Net::HTTPSuccess
|
86
|
+
response.body
|
87
|
+
when Net::HTTPRedirection
|
88
|
+
response['location']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def create_http(uri)
|
93
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
94
|
+
http.use_ssl = true if uri.port == 443
|
95
|
+
http.read_timeout = @timeout
|
96
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
97
|
+
|
98
|
+
# http.set_debug_output $stderr
|
99
|
+
|
100
|
+
http
|
101
|
+
end
|
102
|
+
|
103
|
+
def url(resource)
|
104
|
+
"#{server_path}/#{resource.sub /^\//, ''}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def server_path
|
108
|
+
@port ? "#{@server_url}:#{@port}" : @server_url
|
109
|
+
end
|
110
|
+
|
111
|
+
def encode_params(hash)
|
112
|
+
params = transform_params(hash)
|
113
|
+
stringify_params(params)
|
114
|
+
end
|
115
|
+
|
116
|
+
def transform_params(hash)
|
117
|
+
params = []
|
118
|
+
|
119
|
+
hash.each do |key, value|
|
120
|
+
if value.instance_of? Hash
|
121
|
+
params.concat transform_params(value).each{|elements| elements[0].unshift key}
|
122
|
+
else
|
123
|
+
params.push [[key], value]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
params
|
128
|
+
end
|
129
|
+
|
130
|
+
def stringify_params(params)
|
131
|
+
params.map do |keys, value|
|
132
|
+
left = keys.join('[') + (']' * (keys.length - 1))
|
133
|
+
"#{left}=#{CGI::escape(value.to_s)}"
|
134
|
+
end.join('&')
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_response_for_errors(response, path)
|
138
|
+
if response && response.include?('No connection could be made')
|
139
|
+
raise Errno::ECONNREFUSED
|
140
|
+
elsif @response_error_checker.call(response, path)
|
141
|
+
raise "An error occured requesting #{path} - please check the XML response:\n #{response}\nCurrent data:\n#{@data}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
require 'right_api/http_server'
|
5
|
+
require 'right_api/accessible_fragment'
|
6
|
+
|
7
|
+
# Rightscale
|
8
|
+
module RightScale
|
9
|
+
API_VERSION = '1.0'
|
10
|
+
|
11
|
+
class Base
|
12
|
+
include AccessibleFragment
|
13
|
+
|
14
|
+
def initialize(data)
|
15
|
+
raise "You must first establish a connection via Base.establish_connection" unless @@connection
|
16
|
+
@data = data
|
17
|
+
end
|
18
|
+
|
19
|
+
def id
|
20
|
+
@id ||= href.split('/').last
|
21
|
+
end
|
22
|
+
|
23
|
+
def data
|
24
|
+
@data ||= @@connection.get("/#{plural_name}/#{id}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_attribute(attribute, value)
|
28
|
+
@@connection.put("/#{self.class.plural_name}/#{id}", "#{self.class.class_name}[#{attribute}]" => value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.class_name
|
32
|
+
name.split("::").last.underscore
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.plural_name
|
36
|
+
class_name.pluralize
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.xml_name
|
40
|
+
class_name.dasherize
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def self.establish_connection(user, password, account_id)
|
45
|
+
url = "https://my.rightscale.com/api/acct/#{account_id}"
|
46
|
+
params = {'server_url' => url, 'username' => user, 'password' => password}
|
47
|
+
|
48
|
+
@@connection = HttpServer.new(params, 60)
|
49
|
+
@@connection.get('/login')
|
50
|
+
|
51
|
+
params = {'server_url' => "https://my.rightscale.com"}
|
52
|
+
|
53
|
+
@@non_api_connection = HttpServer.new(params, 60)
|
54
|
+
@@non_api_connection.post('/sessions', 'email' => user, 'password' => password) do |server, response|
|
55
|
+
cookies = response.response.get_fields('set-cookie')
|
56
|
+
session_cookie = cookies.detect { |cookie| cookie =~ /_session_id/ }
|
57
|
+
cookie_header_for_request = session_cookie.split(';').first
|
58
|
+
server.headers = server.headers.merge('cookie' => cookie_header_for_request, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.all(opts={})
|
64
|
+
find_with_options(opts)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.first(opts={})
|
68
|
+
find_first_with_options(opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.find(opts=nil)
|
72
|
+
if opts.nil?
|
73
|
+
raise "Find requires a hash of options or an id"
|
74
|
+
elsif opts.is_a?(Hash)
|
75
|
+
find_with_options(opts)
|
76
|
+
else
|
77
|
+
find(:id => opts)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def self.find_with_options(opts)
|
84
|
+
find_all
|
85
|
+
|
86
|
+
@all.select do |member|
|
87
|
+
opts.all? {|k, v| member.send(k) == v }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.find_first_with_options(opts)
|
92
|
+
find_all
|
93
|
+
|
94
|
+
@all.detect do |member|
|
95
|
+
opts.all? {|k, v| member.send(k) == v }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.find_all
|
100
|
+
return unless @all.nil?
|
101
|
+
|
102
|
+
response = @@connection.get("/#{plural_name}")
|
103
|
+
doc = Hpricot::XML(response)
|
104
|
+
@all = (doc / xml_name).map {|data| new(data)}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Deployment < Base
|
109
|
+
def self.find_by_nickname(nickname)
|
110
|
+
deployments = all.select { |deployment| deployment.nickname == nickname }
|
111
|
+
|
112
|
+
if deployments.size != 1
|
113
|
+
raise "Found #{deployments.size} deployments matching #{nickname}. Double check your nickname, it should be exact to avoid hitting the deployment."
|
114
|
+
end
|
115
|
+
|
116
|
+
deployments.first
|
117
|
+
end
|
118
|
+
|
119
|
+
def servers
|
120
|
+
(@data / 'server').map {|data| Server.new(data)}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Server < Base
|
125
|
+
attr_accessor :aws_id
|
126
|
+
|
127
|
+
def deployment
|
128
|
+
@deployment ||= Deployment.all.detect {|deployment| deployment.id == deployment_href.split('/').last}
|
129
|
+
end
|
130
|
+
|
131
|
+
def aws_id
|
132
|
+
@aws_id ||= @@non_api_connection.get("/servers/#{id}").scan(/i-[a-f0-9]{8}/i).first
|
133
|
+
end
|
134
|
+
|
135
|
+
def volume_rightscale_ids
|
136
|
+
puts @@non_api_connection.get("/servers/#{id}/volumes").scan(/(?=ec2_ebs_volumes\/)\d+/).inspect
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class RightScript < Base
|
141
|
+
end
|
142
|
+
|
143
|
+
class Ec2EbsVolume < Base
|
144
|
+
end
|
145
|
+
|
146
|
+
class Ec2EbsSnapshot < Base
|
147
|
+
end
|
148
|
+
end
|
data/lib/right_api.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# require 'right_api/rightscale'
|
data/right_api.gemspec
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{right_api}
|
8
|
+
s.version = "0.1.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["MoneyPools"]
|
12
|
+
s.date = %q{2009-08-24}
|
13
|
+
s.description = %q{A ruby wrapper for the RightScale api}
|
14
|
+
s.email = %q{mpdev@businesslogic.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION.yml",
|
23
|
+
"install.rb",
|
24
|
+
"lib/right_api.rb",
|
25
|
+
"lib/right_api/accessible_fragment.rb",
|
26
|
+
"lib/right_api/http_server.rb",
|
27
|
+
"lib/right_api/rightscale.rb",
|
28
|
+
"right_api.gemspec",
|
29
|
+
"uninstall.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/moneypools/right_api}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.3}
|
35
|
+
s.summary = %q{A ruby wrapper for the RightScale api}
|
36
|
+
|
37
|
+
if s.respond_to? :specification_version then
|
38
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
39
|
+
s.specification_version = 3
|
40
|
+
|
41
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
42
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0"])
|
43
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
44
|
+
else
|
45
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
46
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
47
|
+
end
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<hpricot>, [">= 0"])
|
50
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
51
|
+
end
|
52
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: right_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MoneyPools
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-27 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: A ruby wrapper for the RightScale api
|
36
|
+
email: mpdev@businesslogic.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- README
|
46
|
+
- Rakefile
|
47
|
+
- VERSION.yml
|
48
|
+
- install.rb
|
49
|
+
- lib/right_api.rb
|
50
|
+
- lib/right_api/accessible_fragment.rb
|
51
|
+
- lib/right_api/http_server.rb
|
52
|
+
- lib/right_api/rightscale.rb
|
53
|
+
- right_api.gemspec
|
54
|
+
- uninstall.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/moneypools/right_api
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: A ruby wrapper for the RightScale api
|
83
|
+
test_files: []
|
84
|
+
|