rest_connection 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.rdoc +49 -0
- data/VERSION +1 -1
- data/examples/relaunch_deployment.rb +1 -1
- data/lib/rest_connection/rightscale/alert_spec.rb +27 -0
- data/lib/rest_connection/rightscale/audit_entry.rb +5 -3
- data/lib/rest_connection/rightscale/ec2_ebs_volume.rb +4 -0
- data/lib/rest_connection/rightscale/ec2_server_array.rb +3 -6
- data/lib/rest_connection/rightscale/rightscale_api_resources.rb +1 -0
- data/lib/rest_connection/rightscale/server.rb +27 -36
- data/lib/rest_connection/rightscale/status.rb +5 -3
- data/lib/rest_connection/ssh_hax.rb +4 -5
- data/rest_connection.gemspec +5 -4
- metadata +7 -6
- data/README +0 -7
data/.gitignore
CHANGED
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= rest_connection Quick Start
|
2
|
+
== Install
|
3
|
+
==== Installing with rubygems
|
4
|
+
"gem install rest_connection"
|
5
|
+
|
6
|
+
==== Installing from source
|
7
|
+
"git clone http://github.com/jeremyd/rest_connection.git"
|
8
|
+
"gem install jeweler rspec"
|
9
|
+
"rake check_dependencies" <- Install any gems listed.
|
10
|
+
"rake install"
|
11
|
+
|
12
|
+
== Configuration
|
13
|
+
|
14
|
+
You must setup ~/.rest_connection/rest_api_config.yaml or /etc/rest_connection/rest_api_config.yaml
|
15
|
+
|
16
|
+
Copy the example from GEMHOME/rest_connection/examples/rest_api_config.yaml.sample and fill in your connection info.
|
17
|
+
|
18
|
+
Pro Tip: to find a GEMHOME, use gemedit
|
19
|
+
"gem install gemedit"
|
20
|
+
"gem edit rest_connection"
|
21
|
+
|
22
|
+
== Usage: some IRB samples
|
23
|
+
|
24
|
+
$ irb
|
25
|
+
ruby> require 'rubygems'; require 'rest_connection'
|
26
|
+
|
27
|
+
=== Lookup and run a RightScript
|
28
|
+
|
29
|
+
first_fe = Server.find(:first) { |s| s.nickname =~ /Front End/ }
|
30
|
+
st = ServerTemplate.find(first_fe.server_template_href)
|
31
|
+
connect_script = st.executables.detect { |ex| ex.name =~ /LB [app|mongrels]+ to HA proxy connect/i }
|
32
|
+
state = first_fe.run_executable(connect_script)
|
33
|
+
state.wait_for_completed
|
34
|
+
|
35
|
+
=== Stop a Deployment
|
36
|
+
|
37
|
+
deployment = Deployment.find(opts[:id])
|
38
|
+
my_servers = deployment.servers
|
39
|
+
my_servers.each { |s| s.stop }
|
40
|
+
my_servers.each { |s| s.wait_for_state("stopped") }
|
41
|
+
|
42
|
+
=== Activate an Ec2ServerArray / Display instances IPs
|
43
|
+
|
44
|
+
my_array = Ec2ServerArray.find(opts[:href])
|
45
|
+
my_array.active = true
|
46
|
+
my_array.save
|
47
|
+
|
48
|
+
puts my_array.instances.map { |i| i['ip-address'] }
|
49
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.10
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# This file is part of RestConnection
|
2
|
+
#
|
3
|
+
# RestConnection is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# RestConnection is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
#
|
16
|
+
class AlertSpec
|
17
|
+
include RightScale::Api::Base
|
18
|
+
extend RightScale::Api::BaseExtend
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def attach(params)
|
23
|
+
connection.post('alert_spec_subjects.js' , :alert_spec_subject => params)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
end
|
@@ -28,16 +28,18 @@ class AuditEntry
|
|
28
28
|
include RightScale::Api::Base
|
29
29
|
extend RightScale::Api::BaseExtend
|
30
30
|
|
31
|
-
def wait_for_state(state)
|
32
|
-
while(
|
31
|
+
def wait_for_state(state, timeout=900)
|
32
|
+
while(timeout > 0)
|
33
33
|
reload
|
34
34
|
connection.logger("state is #{self.state}, waiting for #{state}")
|
35
35
|
friendly_url = "https://my.rightscale.com/audit_entries/"
|
36
36
|
friendly_url += self.href.split(/\//).last
|
37
37
|
raise "FATAL error, #{self.summary}\nSee Audit: API:#{self.href}, WWW:<a href='#{friendly_url}'>#{friendly_url}</a>\n" if self.state == 'failed'
|
38
|
-
sleep
|
38
|
+
sleep 30
|
39
|
+
timeout -= 30
|
39
40
|
return true if state == self.state
|
40
41
|
end
|
42
|
+
raise "FATAL: Timeout waiting for Executable to complete. State was #{self.state}" if timeout <= 0
|
41
43
|
end
|
42
44
|
|
43
45
|
def wait_for_completed(legacy=nil)
|
@@ -13,16 +13,13 @@
|
|
13
13
|
# You should have received a copy of the GNU General Public License
|
14
14
|
# along with RestConnection. If not, see <http://www.gnu.org/licenses/>.
|
15
15
|
|
16
|
-
# Example:
|
17
|
-
# a = Ec2ServerArray.new(:href => "https://validhref")
|
18
|
-
# st = ServerTemplate.new(:href => "https://validhref")
|
19
|
-
# st.executables.find(:nickname
|
20
|
-
# a.run_script_on_all(
|
21
|
-
|
22
16
|
class Ec2ServerArray
|
23
17
|
include RightScale::Api::Base
|
24
18
|
extend RightScale::Api::BaseExtend
|
25
19
|
|
20
|
+
# Example:
|
21
|
+
# right_script = @server_template.executables.first
|
22
|
+
# result = @my_array.run_script_on_all(right_script, [@server_template.href])
|
26
23
|
def run_script_on_all(script, server_template_hrefs, inputs=nil)
|
27
24
|
serv_href = URI.parse(self.href)
|
28
25
|
options = Hash.new
|
@@ -29,6 +29,7 @@ require 'rest_connection/rightscale/multi_cloud_image'
|
|
29
29
|
require 'rest_connection/rightscale/tag'
|
30
30
|
require 'rest_connection/rightscale/rs_internal'
|
31
31
|
require 'rest_connection/rightscale/audit_entry'
|
32
|
+
require 'rest_connection/rightscale/alert_spec'
|
32
33
|
require 'rest_connection/rightscale/ec2_ebs_volume'
|
33
34
|
require 'rest_connection/rightscale/ec2_ebs_snapshot'
|
34
35
|
require 'rest_connection/rightscale/server_internal'
|
@@ -30,27 +30,36 @@ class Server
|
|
30
30
|
newrecord
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
# waits until the specified state is reached for this Server
|
34
|
+
# *st <~String> the name of the state to wait for, eg. "operational"
|
35
|
+
# *timeout <~Integer> optional, how long to wait for the state before declare failure (in seconds).
|
36
|
+
def wait_for_state(st,timeout=1200)
|
34
37
|
reload
|
35
38
|
connection.logger("#{nickname} is #{self.state}")
|
36
|
-
while(
|
39
|
+
while(timeout > 0)
|
37
40
|
return true if state == st
|
38
41
|
raise "FATAL error, this server is stranded and needs to be #{st}: #{nickname}, see audit: #{self.audit_link}" if state.include?('stranded')
|
39
|
-
sleep
|
42
|
+
sleep 30
|
43
|
+
timeout -= 30
|
40
44
|
connection.logger("waiting for server #{nickname} to go #{st}, state is #{state}")
|
41
45
|
reload
|
42
46
|
end
|
47
|
+
raise "FATAL, this server #{self.audit_link} timed out waiting for the state to be #{st}" if timeout <= 0
|
43
48
|
end
|
44
49
|
|
50
|
+
# waits until the server is operational and dns_name is available
|
45
51
|
def wait_for_operational_with_dns
|
52
|
+
timeout = 600
|
46
53
|
wait_for_state("operational")
|
47
|
-
while(
|
48
|
-
connection.logger "waiting for dns-name for #{self.nickname}"
|
49
|
-
break if self['dns-name'] && !self['dns-name'].empty?
|
54
|
+
while(timeout > 0)
|
50
55
|
self.settings
|
51
|
-
|
56
|
+
break if self['dns-name'] && !self['dns-name'].empty?
|
57
|
+
connection.logger "waiting for dns-name for #{self.nickname}"
|
58
|
+
sleep 30
|
59
|
+
timeout -= 30
|
52
60
|
end
|
53
61
|
connection.logger "got DNS: #{self['dns-name']}"
|
62
|
+
raise "FATAL, this server #{self.audit_link} timed out waiting for DNS" if timeout <= 0
|
54
63
|
end
|
55
64
|
|
56
65
|
def audit_link
|
@@ -89,8 +98,8 @@ class Server
|
|
89
98
|
@server_internal.stop
|
90
99
|
end
|
91
100
|
|
92
|
-
#
|
93
|
-
# executable
|
101
|
+
# Works on v4 and v5 images.
|
102
|
+
# *executable can be an <~Executable> or <~RightScript>
|
94
103
|
def run_executable(executable, opts=nil)
|
95
104
|
script_options = Hash.new
|
96
105
|
script_options[:server] = Hash.new
|
@@ -141,6 +150,13 @@ class Server
|
|
141
150
|
@params.merge! connection.get(serv_href.path + "/settings")
|
142
151
|
end
|
143
152
|
|
153
|
+
def attach_volume(params)
|
154
|
+
hash = {}
|
155
|
+
hash[:server] = params
|
156
|
+
serv_href = URI.parse(self.href)
|
157
|
+
connection.post(serv_href.path + "/attach_volume", hash)
|
158
|
+
end
|
159
|
+
|
144
160
|
def get_sketchy_data
|
145
161
|
serv_href = URI.parse(self.href)
|
146
162
|
@params.merge! connection.get(serv_href.path + "/get_sketchy_data")
|
@@ -162,12 +178,6 @@ class Server
|
|
162
178
|
end
|
163
179
|
end
|
164
180
|
|
165
|
-
def events
|
166
|
-
my_events = Event.new
|
167
|
-
id = self.href.split(/\//).last
|
168
|
-
my_events.filter_by(:server_id, id)
|
169
|
-
end
|
170
|
-
|
171
181
|
def relaunch
|
172
182
|
self.stop
|
173
183
|
self.wait_for_state("stopped")
|
@@ -182,31 +192,12 @@ class Server
|
|
182
192
|
old_state = self.state unless old_state
|
183
193
|
connection.logger("#{nickname} is #{self.state}")
|
184
194
|
return true if self.state != old_state
|
185
|
-
sleep
|
186
|
-
timer +=
|
195
|
+
sleep 30
|
196
|
+
timer += 30
|
187
197
|
connection.logger("waiting for server #{nickname} to change from #{old_state} state.")
|
188
198
|
end
|
189
199
|
raise("FATAL: timeout after #{timeout}s waiting for state change")
|
190
200
|
end
|
191
201
|
|
192
|
-
# DOES NOT WORK: fragile web scraping
|
193
|
-
# def relaunch
|
194
|
-
# unless state == "stopped"
|
195
|
-
# wind_monkey
|
196
|
-
# server_id = self.href.split(/\//).last
|
197
|
-
# base_url = URI.parse(self.href)
|
198
|
-
# base_url.path = "/servers/#{server_id}"
|
199
|
-
#
|
200
|
-
# s = agent.get(base_url.to_s)
|
201
|
-
# relaunch = s.links.detect {|d| d.to_s == "Relaunch"}
|
202
|
-
# prelaunch_page = agent.get(relaunch.href)
|
203
|
-
# debugger
|
204
|
-
# launch_form = prelaunch_page.forms[2]
|
205
|
-
# launch_form.radiobuttons_with(:name => 'launch_immediately').first.check
|
206
|
-
# agent.submit(launch_form, launch_form.buttons.first)
|
207
|
-
# else
|
208
|
-
# connection.logger("WARNING: detected server is #{self.state}, skipping relaunch")
|
209
|
-
# end
|
210
|
-
# end
|
211
202
|
end
|
212
203
|
|
@@ -20,13 +20,15 @@
|
|
20
20
|
class Status
|
21
21
|
include RightScale::Api::Base
|
22
22
|
extend RightScale::Api::BaseExtend
|
23
|
-
def wait_for_completed(audit_link = "no audit link available")
|
24
|
-
while(
|
23
|
+
def wait_for_completed(audit_link = "no audit link available", timeout = 900)
|
24
|
+
while(timeout > 0)
|
25
25
|
reload
|
26
26
|
return true if self.state == "completed"
|
27
27
|
raise "FATAL error, script failed\nSee Audit: #{audit_link}" if self.state == 'failed'
|
28
|
-
sleep
|
28
|
+
sleep 30
|
29
|
+
timeout -= 30
|
29
30
|
connection.logger("querying status of right_script.. got: #{self.state}")
|
30
31
|
end
|
32
|
+
raise "FATAL: Timeout waiting for Executable to complete. State was #{self.state}" if timeout <= 0
|
31
33
|
end
|
32
34
|
end
|
@@ -42,7 +42,7 @@ module SshHax
|
|
42
42
|
result = nil
|
43
43
|
output = ""
|
44
44
|
connection.logger("Running: #{run_this}")
|
45
|
-
Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key)) do |ssh|
|
45
|
+
Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key), :user_known_hosts_file => "/dev/null") do |ssh|
|
46
46
|
cmd_channel = ssh.open_channel do |ch1|
|
47
47
|
ch1.on_request('exit-status') do |ch, data|
|
48
48
|
status = data.read_long
|
@@ -152,15 +152,13 @@ module SshHax
|
|
152
152
|
retry_count = 0
|
153
153
|
while (!success && retry_count < SSH_RETRY_COUNT) do
|
154
154
|
begin
|
155
|
-
Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key)) do |ssh|
|
155
|
+
Net::SSH.start(host_dns, 'root', :keys => ssh_key_config(ssh_key), :user_known_hosts_file => "/dev/null") do |ssh|
|
156
156
|
cmd_channel = ssh.open_channel do |ch1|
|
157
157
|
ch1.on_request('exit-status') do |ch, data|
|
158
158
|
status = data.read_long
|
159
159
|
end
|
160
|
-
output += "Running: #{command}\n"
|
161
160
|
ch1.exec(command) do |ch2, success|
|
162
161
|
unless success
|
163
|
-
output = "ERROR: SSH cmd failed to exec"
|
164
162
|
status = 1
|
165
163
|
end
|
166
164
|
ch2.on_data do |ch, data|
|
@@ -174,10 +172,11 @@ module SshHax
|
|
174
172
|
end
|
175
173
|
rescue Exception => e
|
176
174
|
retry_count += 1 # opening the ssh channel failed -- try again.
|
175
|
+
connection.logger "ERROR during SSH session to #{host_dns}, retrying #{retry_count}: #{e} #{e.backtrace}"
|
177
176
|
sleep 10
|
178
177
|
end
|
179
178
|
end
|
180
|
-
connection.logger output
|
179
|
+
connection.logger "SSH Run: #{command} on #{host_dns}. Retry was #{retry_count}. Exit status was #{status}. Output below ---\n#{output}\n---"
|
181
180
|
return {:status => status, :output => output}
|
182
181
|
end
|
183
182
|
|
data/rest_connection.gemspec
CHANGED
@@ -5,19 +5,19 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rest_connection}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.10"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jeremy Deininger"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-09-02}
|
13
13
|
s.description = %q{provides rest_connection}
|
14
14
|
s.email = %q{jeremy@rubyonlinux.org}
|
15
15
|
s.extra_rdoc_files = [
|
16
|
-
"README"
|
16
|
+
"README.rdoc"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".gitignore",
|
20
|
-
"README",
|
20
|
+
"README.rdoc",
|
21
21
|
"Rakefile",
|
22
22
|
"VERSION",
|
23
23
|
"config/rest_api_config.yaml.sample",
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
"examples/relaunch_deployment.rb",
|
32
32
|
"examples/right_scale_ec2_instances_api_test.rb",
|
33
33
|
"lib/rest_connection.rb",
|
34
|
+
"lib/rest_connection/rightscale/alert_spec.rb",
|
34
35
|
"lib/rest_connection/rightscale/audit_entry.rb",
|
35
36
|
"lib/rest_connection/rightscale/deployment.rb",
|
36
37
|
"lib/rest_connection/rightscale/ec2_ebs_snapshot.rb",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest_connection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 10
|
10
|
+
version: 0.0.10
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Deininger
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-09-02 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -67,10 +67,10 @@ executables: []
|
|
67
67
|
extensions: []
|
68
68
|
|
69
69
|
extra_rdoc_files:
|
70
|
-
- README
|
70
|
+
- README.rdoc
|
71
71
|
files:
|
72
72
|
- .gitignore
|
73
|
-
- README
|
73
|
+
- README.rdoc
|
74
74
|
- Rakefile
|
75
75
|
- VERSION
|
76
76
|
- config/rest_api_config.yaml.sample
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- examples/relaunch_deployment.rb
|
85
85
|
- examples/right_scale_ec2_instances_api_test.rb
|
86
86
|
- lib/rest_connection.rb
|
87
|
+
- lib/rest_connection/rightscale/alert_spec.rb
|
87
88
|
- lib/rest_connection/rightscale/audit_entry.rb
|
88
89
|
- lib/rest_connection/rightscale/deployment.rb
|
89
90
|
- lib/rest_connection/rightscale/ec2_ebs_snapshot.rb
|
data/README
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
RestConnection is a library designed to facilitate Restful connections to APIs using Basic auth and more. However! If you've done any API work you know that every API is different. The goal of RestConnection is to be as lean as possible and a EXAMPLE of how to use Net::HTTP in a restful way. Right now RestConnection uses JSON and Basic Auth, this is easy to change or expand on and it's written to give more control to the user of the library (You).
|
2
|
-
|
3
|
-
My hope is to include 'helper resources' for common API endpoints while maintaining a somewhat universal 'Connection' class that can be easily understood and modified to suit any Restful API needs. These resources will have a similar usage to ActiveResource.
|
4
|
-
|
5
|
-
The first API we will be supporting is the RightScale API.
|
6
|
-
|
7
|
-
I welcome any contributions or suggestions. Thanks!
|