rscalr 0.0.12 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -43,6 +43,24 @@ script = dashboard.get_script 'my-script-name'
43
43
  script.execute farm.id
44
44
  ```
45
45
 
46
+ Configuration
47
+ -------------
48
+
49
+ If you use this tool a lot from the command line, you can set environment variables that will automagically configure your Scalr or Dashboard instance. The following code sets the relevant parameters:
50
+ ```bash
51
+ export SCALR_API_KEY=your-api-key
52
+ export SCALR_API_SECRET=your-api-secret
53
+ export SCALR_ENV_ID=id-of-desired-environment # optional
54
+ export SCALR_CLIENT_VERBOSE=true # Print API calls/responses to stdout, optional, default=false
55
+ ```
56
+
57
+ Then just instaniate your client:
58
+ ```ruby
59
+ scalr = Scalr.new
60
+ # or
61
+ dash = Dashboard.new
62
+ ```
63
+
46
64
 
47
65
  Caveats
48
66
  -------
@@ -7,4 +7,7 @@ require 'rscalr/model/log'
7
7
  require 'rscalr/model/log_list'
8
8
  require 'rscalr/model/role'
9
9
  require 'rscalr/model/script'
10
+ require 'rscalr/model/script_log'
11
+ require 'rscalr/model/script_execution'
10
12
  require 'rscalr/model/server'
13
+ require 'rscalr/model/server_log'
@@ -16,8 +16,13 @@ class Scalr
16
16
  $DEFAULT_VERSION = '2.3.0'
17
17
  $DEFAULT_AUTH_VERSION = '3'
18
18
 
19
- def initialize(config)
20
- @config = config
19
+ def initialize(config=nil)
20
+ if (config.nil?)
21
+ @config = load_config_from_environment
22
+ else
23
+ @config = config
24
+ end
25
+
21
26
  @config[:version] = $DEFAULT_VERSION if @config[:version].nil?
22
27
  @config[:auth_version] = $DEFAULT_AUTH_VERSION if @config[:auth_version].nil?
23
28
  end
@@ -164,7 +169,7 @@ class Scalr
164
169
  params[:Prefix] = prefix unless prefix.nil?
165
170
  params[:ImageID] = image_id unless image_id.nil?
166
171
 
167
- execute_api_call('RolesList', params)
172
+ execute_api_call('RolesList', params)
168
173
  end
169
174
 
170
175
  def scripts_list
@@ -253,6 +258,18 @@ class Scalr
253
258
 
254
259
  #=============== Helper methods ==================================
255
260
 
261
+ # Loads config hash based on environment variables
262
+ def load_config_from_environment
263
+ config = {}
264
+ config[:key_id] = ENV['SCALR_API_KEY'] unless ENV['SCALR_API_KEY'].nil?
265
+ config[:key_secret] = ENV['SCALR_API_SECRET'] unless ENV['SCALR_API_SECRET'].nil?
266
+ config[:env_id] = ENV['SCALR_ENV_ID'] unless ENV['SCALR_ENV_ID'].nil?
267
+ config[:version] = ENV['SCALR_API_VERSION'] unless ENV['SCALR_API_VERSION'].nil?
268
+ config[:auth_version] = ENV['SCALR_API_AUTH_VERSION'] unless ENV['SCALR_API_AUTH_VERSION'].nil?
269
+ config[:verbose] = true if (ENV['SCALR_CLIENT_VERBOSE'] == "true" || ENV['SCALR_CLIENT_VERBOSE'] == "1")
270
+ config
271
+ end
272
+
256
273
  # Generates request signature based on config, action, timestamp
257
274
  def generate_sig(action, timestamp)
258
275
  message = action + ':' + @config[:key_id] + ':' + timestamp
@@ -297,6 +314,8 @@ class Scalr
297
314
  result = build_error_response(ex.message, params[:Signature])
298
315
  end
299
316
 
317
+ $stdout.puts(result.pretty_print) if @config[:verbose]
318
+
300
319
  result
301
320
  end
302
321
 
@@ -362,7 +381,9 @@ class ScalrResponse < REXML::Document
362
381
 
363
382
  # Convenience method to output a repsonse to a stream in a human readable format
364
383
  def pretty_print(dest=$stdout)
365
- write(dest, 1)
384
+ formatter = REXML::Formatters::Pretty.new
385
+ formatter.compact = true
386
+ formatter.write(root,dest)
366
387
  end
367
388
  end
368
389
 
@@ -1,12 +1,12 @@
1
1
  class Dashboard
2
2
 
3
- def initialize config
3
+ def initialize(config=nil)
4
4
  if config.is_a?(Scalr)
5
5
  @client = config
6
- elsif config.is_a?(Hash)
6
+ elsif config.nil? || config.is_a?(Hash)
7
7
  @client = Scalr.new(config)
8
8
  else
9
- raise 'Dashboard may only be initialized with a config Hash or Scalr object'
9
+ raise 'Dashboard may only be initialized with a config Hash, Scalr object, or nil (load config from environment)'
10
10
  end
11
11
 
12
12
  @env_id = @client.env_id
@@ -19,6 +19,17 @@ class Dashboard
19
19
  @farms[name]
20
20
  end
21
21
 
22
+ def get_farm_by_id(farm_id)
23
+ if @farms.nil?
24
+ load_farms
25
+ end
26
+ @farms.each { |name,farm|
27
+ return farm if farm.id == farm_id
28
+ }
29
+
30
+ nil
31
+ end
32
+
22
33
  def load_farms
23
34
  @farms = {}
24
35
 
@@ -98,4 +109,57 @@ class Dashboard
98
109
 
99
110
  @environments
100
111
  end
112
+
113
+ # Blocks up to timeout seconds waiting on log response(s) of script execution. Returns true iff
114
+ # script executed successfully (returned) exit_value on all servers.
115
+ def verify_script_success(script_execution, exit_value=0, timeout_sec=60)
116
+ return false unless script_execution.success?
117
+
118
+ sleep_time = 5
119
+
120
+ start_time = Time.now.to_i
121
+
122
+ # 1. get list of servers that this execution should run on
123
+ farm = get_farm_by_id(script_execution.farm_id)
124
+ if !script_execution.server_id.nil?
125
+ server = farm.get_server(script_execution.server_id)
126
+ script_execution.add_server(server.server_id)
127
+ elsif !script_execution.farm_role_id.nil?
128
+ role_servers = farm.get_servers_for_role_id(script_execution.farm_role_id)
129
+ role_servers.each { |server|
130
+ script_execution.add_server(server.server_id)
131
+ }
132
+ else
133
+ farm_servers = farm.get_all_servers
134
+ farm_servers.each { |server|
135
+ script_execution.add_server(server.server_id)
136
+ }
137
+ end
138
+
139
+ # 2. Call logs list and match results to server IDs
140
+ success = false
141
+ finished = false
142
+ begin
143
+ start = 0
144
+ begin
145
+ loglist = farm.load_script_logs(script_execution.server_id, script_execution.event_id, start, 20)
146
+ total_records = loglist.total_records
147
+
148
+ loglist.each { |log|
149
+ script_execution.set_server_result(log)
150
+ start += 1
151
+ }
152
+ end while start < total_records
153
+
154
+ # 3. If not all servers have responses logged, repeat Step 2 until done or timeout_sec seconds have expired
155
+ success = true
156
+ finished = true
157
+ script_execution.server_results.each { |server_id, result|
158
+ success &&= (!result.nil? && result.exec_exit_code == exit_value)
159
+ finished &&= !result.nil?
160
+ }
161
+ end while !finished && (Time.now.to_i - start_time) < (timeout_sec + sleep_time) && sleep(sleep_time)
162
+
163
+ success
164
+ end
101
165
  end
@@ -12,8 +12,40 @@ class Farm
12
12
  @farm_roles[name]
13
13
  end
14
14
 
15
+ def get_server(name)
16
+ if @farm_servers.nil?
17
+ load_details
18
+ end
19
+ @farm_servers[name]
20
+ end
21
+
22
+ def get_servers_for_role_id(farm_role_id)
23
+ if @farm_roles.nil?
24
+ load_details
25
+ end
26
+ result = []
27
+ @farm_roles.each { |name,role|
28
+ result = role.servers.dup if role.farm_role_id == farm_role_id
29
+ }
30
+ result
31
+ end
32
+
33
+ def get_all_servers
34
+ if @farm_roles.nil?
35
+ load_details
36
+ end
37
+ result = []
38
+ @farm_roles.each { |name,role|
39
+ role.servers.each { |server|
40
+ result << server
41
+ }
42
+ }
43
+ result
44
+ end
45
+
15
46
  def load_details
16
47
  @farm_roles = {}
48
+ @farm_servers = {}
17
49
 
18
50
  scalr_response = @client.farm_get_details @id
19
51
  scalr_response.root.each_element('FarmRoleSet/Item') { |row|
@@ -21,9 +53,9 @@ class Farm
21
53
 
22
54
  row.each_element { |prop|
23
55
  if "ID" == prop.name
24
- role.farm_role_id = prop.text
56
+ role.farm_role_id = prop.text.to_i
25
57
  elsif "RoleID" == prop.name
26
- role.role_id = prop.text
58
+ role.role_id = prop.text.to_i
27
59
  elsif "Name" == prop.name
28
60
  role.name = prop.text
29
61
  elsif "ServerSet" == prop.name
@@ -32,21 +64,22 @@ class Farm
32
64
  server.role = role
33
65
 
34
66
  server_element.each_element { |server_prop|
35
- if "ServerID" == prop.name
36
- server.server_id = prop.text
37
- elsif "ExternalIP" == prop.name
38
- server.external_ip = prop.text
39
- elsif "InternalIP" == prop.name
40
- server.external_ip = prop.text
41
- elsif "Status" == prop.name
42
- server.status = prop.text
43
- elsif "Index" == prop.name
44
- server.index = prop.text.to_i
45
- elsif "Uptime" == prop.name
46
- server.uptime = prop.text.to_f
67
+ if "ServerID" == server_prop.name
68
+ server.server_id = server_prop.text
69
+ elsif "ExternalIP" == server_prop.name
70
+ server.external_ip = server_prop.text
71
+ elsif "InternalIP" == server_prop.name
72
+ server.external_ip = server_prop.text
73
+ elsif "Status" == server_prop.name
74
+ server.status = server_prop.text
75
+ elsif "Index" == server_prop.name
76
+ server.index = server_prop.text.to_i
77
+ elsif "Uptime" == server_prop.name
78
+ server.uptime = server_prop.text.to_f
47
79
  end
48
80
  }
49
81
  role.servers << server
82
+ @farm_servers[server.server_id] = server
50
83
  }
51
84
  end
52
85
  }
@@ -71,7 +104,7 @@ class Farm
71
104
  loglist.limit = element.text.to_i
72
105
  elsif "LogSet" == element.name
73
106
  element.each_element do |item|
74
- log = Log.new
107
+ log = ServerLog.new
75
108
 
76
109
  item.each_element do |prop|
77
110
  if "ServerID" == prop.name
@@ -83,7 +116,48 @@ class Farm
83
116
  elsif "Timestamp" == prop.name
84
117
  log.timestamp = prop.text.to_i
85
118
  elsif "Source" == prop.name
86
- server.source = prop.text
119
+ log.source = prop.text
120
+ end
121
+ end
122
+ loglist << log
123
+ end
124
+ end
125
+ end
126
+
127
+ loglist
128
+ end
129
+
130
+ # Loads script execution logs for the farm, or a particular server in the farm, optionally relating to a particular script execution.
131
+ def load_script_logs(server_id=nil, event_id=nil, start=nil, limit=nil)
132
+ scalr_response = @client.scripting_logs_list(@id, server_id, event_id, start, limit)
133
+ loglist = LogList.new
134
+
135
+ scalr_response.root.each_element do |element|
136
+ if "TotalRecords" == element.name
137
+ loglist.total_records = element.text.to_i
138
+ elsif "StartFrom" == element.name
139
+ loglist.start = element.text.to_i
140
+ elsif "RecordsLimit" == element.name
141
+ loglist.limit = element.text.to_i
142
+ elsif "LogSet" == element.name
143
+ element.each_element do |item|
144
+ log = ScriptLog.new
145
+
146
+ item.each_element do |prop|
147
+ if "ServerID" == prop.name
148
+ log.server_id = prop.text
149
+ elsif "Message" == prop.name
150
+ log.message = prop.text
151
+ elsif "ScriptName" == prop.name
152
+ log.script_name = prop.text
153
+ elsif "Timestamp" == prop.name
154
+ log.timestamp = prop.text.to_i
155
+ elsif "ExecTime" == prop.name
156
+ log.exec_time = prop.text.to_i
157
+ elsif "ExecExitCode" == prop.name
158
+ log.exec_exit_code = prop.text.to_i
159
+ elsif "Event" == prop.name
160
+ log.event = prop.text
87
161
  end
88
162
  end
89
163
  loglist << log
@@ -1,7 +1,7 @@
1
1
  class Log
2
- attr_accessor :server_id, :message, :severity, :timestamp, :source
2
+ attr_accessor :server_id, :message, :timestamp
3
3
 
4
4
  def to_s
5
- "{ type: \"log\", server_id: \"#{@server_id}\", message: \"#{@message}\", severity: #{@severity}, timestamp: #{@timestamp}, source: \"#{@source}\" }"
5
+ "{ type: \"log\", server_id: \"#{@server_id}\", message: \"#{@message}\", timestamp: #{@timestamp} }"
6
6
  end
7
7
  end
@@ -10,6 +10,12 @@ class LogList
10
10
  @logs << log if log.is_a? Log
11
11
  end
12
12
 
13
+ def each
14
+ @logs.each { |log|
15
+ yield log
16
+ }
17
+ end
18
+
13
19
  def to_s
14
20
  "{ type: \"loglist\", total_records: #{@total_records}, start: \"#{@message}\", limit: #{@severity}, logs: [ #{@logs.each do |log| log.to_s + ',' end} ] }"
15
21
  end
@@ -9,7 +9,8 @@ class Script
9
9
 
10
10
  def execute(farm_id, timeout=30, async=:no_async, farm_role_id=nil, server_id=nil, revision=nil, config_vars=nil)
11
11
  set_revision(revision) # Side effect!
12
- @client.script_execute farm_id, @id, timeout, async, farm_role_id, server_id, @revision, config_vars
12
+ api_response = @client.script_execute farm_id, @id, timeout, async, farm_role_id, server_id, @revision, config_vars
13
+ parse_script_execution_response(api_response, farm_id, farm_role_id, server_id)
13
14
  end
14
15
 
15
16
  def load_details
@@ -57,6 +58,23 @@ class Script
57
58
  end
58
59
  end
59
60
 
61
+ def parse_script_execution_response(api_response, farm_id, farm_role_id=nil, server_id=nil)
62
+ if api_response.success?
63
+ result = nil
64
+ event_id = nil
65
+ api_response.root.each_element { |field|
66
+ if "TransactionID" == field.name
67
+ event_id = field.text
68
+ elsif "Result" == field.name
69
+ result = field.text.to_i
70
+ end
71
+ }
72
+ ScriptExecution.new(@id, result, event_id, farm_id, farm_role_id, server_id)
73
+ elsif
74
+ ScriptExecution.new(@id, 0, nil, farm_id)
75
+ end
76
+ end
77
+
60
78
  def to_s
61
79
  "{ type: \"script\", id: #{@id}, name: \"#{@name}\" }"
62
80
  end
@@ -0,0 +1,45 @@
1
+ class ScriptExecution
2
+ attr_reader :script_id, :result, :event_id, :farm_id, :farm_role_id, :server_id, :server_results
3
+
4
+ def initialize(script_id, result, event_id, farm_id, farm_role_id=nil, server_id=nil)
5
+ @script_id = script_id
6
+ @result = result
7
+ @result = 0 if @result != 1
8
+ @event_id = event_id
9
+ @farm_id = farm_id
10
+ @farm_role_id = farm_role_id
11
+ @server_id = server_id
12
+
13
+ # instance field to store execution results
14
+ @server_results = {}
15
+ end
16
+
17
+ def success?
18
+ @result == 1
19
+ end
20
+
21
+ def add_server(server_id)
22
+ @server_results[server_id] = nil
23
+ end
24
+
25
+ def set_server_result(log)
26
+ @server_results[log.server_id] = log if @server_results.has_key?(log.server_id)
27
+ end
28
+
29
+ def to_s
30
+ "{ type: \"script_execution\", script_id: #{@script_id}, result: #{@result}, event_id: \"#{@event_id}\"}"
31
+ end
32
+ end
33
+
34
+ class ScriptExecutionServerResult
35
+ attr_accessor :script_id, :server_id, :log
36
+
37
+ def initialize(script_id, server_id)
38
+ @script_id = script_id
39
+ @server_id = server_id
40
+ end
41
+
42
+ def to_s
43
+ "{ type: \"script_execution_server_result\", script_id: #{@script_id}, server_id: \"#{@server_id}\", result: #{@log} }"
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ class ScriptLog < Log
2
+ attr_accessor :script_name, :exec_time, :exec_exit_code, :event
3
+
4
+ def to_s
5
+ "{ type: \"script_log\", server_id: \"#{@server_id}\", script_name: \"#{@script_name}\", message: \"#{@message}\", timestamp: #{@timestamp}, exec_exit_code: #{@exec_exit_code} }"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class ServerLog < Log
2
+ attr_accessor :severity, :source
3
+
4
+ def to_s
5
+ "{ type: \"server_log\", server_id: \"#{@server_id}\", message: \"#{@message}\", timestamp: #{@timestamp}, severity: #{@severity}, source: \"#{@source}\" }"
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module Rscalr
2
- VERSION = "0.0.12"
2
+ VERSION = "0.0.15"
3
3
  end
@@ -22,9 +22,7 @@ class TestObjLogs < Test::Unit::TestCase
22
22
 
23
23
  loglist = farm.load_logs
24
24
 
25
- assert_not_nil(loglist, "Log list could not be loaded")
26
- assert_equal(0, loglist.total_records, "Test farm 1 should not have any logs associated with it")
27
-
25
+ assert_not_nil(loglist, "Log list could not be loaded")
28
26
  end
29
27
 
30
28
  end
metadata CHANGED
@@ -1,25 +1,28 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rscalr
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.12
3
+ version: !ruby/object:Gem::Version
5
4
  prerelease:
5
+ version: 0.0.15
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Nathan Smith
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-11 00:00:00.000000000 -07:00
13
- default_executable:
12
+
13
+ date: 2013-03-14 00:00:00 Z
14
14
  dependencies: []
15
- description: Rscalr is a Ruby implementation of the Scalr API, written to interface
16
- cleanly with Chef and other internal release management tasks.
17
- email:
15
+
16
+ description: Rscalr is a Ruby implementation of the Scalr API, written to interface cleanly with Chef and other internal release management tasks.
17
+ email:
18
18
  - nate@branchout.com
19
19
  executables: []
20
+
20
21
  extensions: []
22
+
21
23
  extra_rdoc_files: []
22
- files:
24
+
25
+ files:
23
26
  - .gitignore
24
27
  - README.md
25
28
  - lib/rscalr.rb
@@ -32,7 +35,10 @@ files:
32
35
  - lib/rscalr/model/log_list.rb
33
36
  - lib/rscalr/model/role.rb
34
37
  - lib/rscalr/model/script.rb
38
+ - lib/rscalr/model/script_execution.rb
39
+ - lib/rscalr/model/script_log.rb
35
40
  - lib/rscalr/model/server.rb
41
+ - lib/rscalr/model/server_log.rb
36
42
  - lib/rscalr/version.rb
37
43
  - rscalr.gemspec
38
44
  - test/test_env_id.rb
@@ -40,32 +46,34 @@ files:
40
46
  - test_integration/test_client_farms.rb
41
47
  - test_integration/test_obj_environments.rb
42
48
  - test_integration/test_obj_logs.rb
43
- has_rdoc: true
44
49
  homepage:
45
50
  licenses: []
51
+
46
52
  post_install_message:
47
53
  rdoc_options: []
48
- require_paths:
54
+
55
+ require_paths:
49
56
  - lib
50
- required_ruby_version: !ruby/object:Gem::Requirement
57
+ required_ruby_version: !ruby/object:Gem::Requirement
51
58
  none: false
52
- requirements:
53
- - - ! '>='
54
- - !ruby/object:Gem::Version
55
- version: '0'
56
- required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
64
  none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
62
69
  requirements: []
70
+
63
71
  rubyforge_project:
64
- rubygems_version: 1.6.2
72
+ rubygems_version: 1.8.25
65
73
  signing_key:
66
74
  specification_version: 3
67
75
  summary: Ruby implementation of the Scalr API
68
- test_files:
76
+ test_files:
69
77
  - test/test_env_id.rb
70
78
  - test/test_sig.rb
71
79
  - test_integration/test_client_farms.rb