right_api 0.1.2
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/.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
|
+
|