corosync-commander 0.0.2 → 0.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f9f90af1b3fac9c2164e54e664cae15be76ef65
4
- data.tar.gz: ccddc4d241a87b80def6ce1ad6dae3073a520e5e
3
+ metadata.gz: 9ffd9a76d120685a5cc4ca5b46d63f416c25f2ac
4
+ data.tar.gz: 8c9f81c68f0ac85a2bc053f634e0f61a8f86257a
5
5
  SHA512:
6
- metadata.gz: 47de186e7ba2e4bfc304761b6e704f776ded20ab55d3428fdb5dbd5d5e886511fc80ee438003d357a269c5f66703f5c68142502507c38a6b0d55004ccccdf3c1
7
- data.tar.gz: 47136a7dbb32771007e34f76a4ae86341b14d360b6cbfa7988e34c05533451961991c2564cce2bcf6da891190d3cbba17129c9f1017649066c472902223764ad
6
+ metadata.gz: 5e6ca5cd16bca2f7e87392357cc65de13e6e9f53608d8b3f87f1aef52332f1df86a027bc56abed06a42263ac52627612730bb371664837f0c42d82e263a2375b
7
+ data.tar.gz: e001b910753074d6633c0704f7c10099119d6d6dcd006e20851e5c4b46a443a1edfe5f19aac1cd37aad39cd59c86ff856db545a7ea8195f9cdff48ef7c61f81a
data/Gemfile CHANGED
@@ -1,7 +1,8 @@
1
1
  # A sample Gemfile
2
2
  source "https://rubygems.org"
3
3
 
4
- gem 'corosync', '~>0.0.3'
4
+ gem 'corosync', '~>0.1.0'
5
+ #gem 'corosync', :path => '/home/phemmer/git/ruby-corosync'
5
6
 
6
7
  group :development do
7
8
  gem 'rake'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- corosync (0.0.3)
4
+ corosync (0.1.0.0)
5
5
  ffi (~> 1.9)
6
6
  diff-lcs (1.2.4)
7
7
  ffi (1.9.3)
@@ -23,7 +23,7 @@ PLATFORMS
23
23
  ruby
24
24
 
25
25
  DEPENDENCIES
26
- corosync (~> 0.0.3)
26
+ corosync (~> 0.1.0)
27
27
  rake
28
28
  rdoc
29
29
  rspec
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # corosync-commander
2
+
3
+ ## Description
4
+ corosync-commander is a simplified interface into cluster communication via the Corosync library, based on the [corosync gem](http://github.com/phemmer/ruby-corosync/).
5
+
6
+ It allows you to build apps which communicate with each other in a reliable fashion. You can send messages to the apps and ensure that every app receives the message in the exact same order. This lets you synchronize data between the apps by only sending deltas.
7
+ The key to this is that when you send a message to the cluster, you receive your own message. Thus corosync-commander becomes a communication bus between the frontend and backend of your application.
8
+
9
+ ## Examples
10
+
11
+ There is a fully working example in the `examples` directory, but here's a brief condensed version:
12
+
13
+ require 'corosync_commander'
14
+ cc = CorosyncCommander.new
15
+ cc.commands.register('shell command') do |sender, shellcmd|
16
+ %x{#{shellcmd}}
17
+ end
18
+ cc.join('remote commands')
19
+
20
+ exe = cc.execute([], 'shell command', 'hostname')
21
+ enum = exe.to_enum
22
+ begin
23
+ enum.each do |sender, response|
24
+ $stdout.write "#{sender.nodeid}:#{sender.pid}: #{response.chomp}\n"
25
+ end
26
+ rescue CorosyncCommander::RemoteException => e
27
+ puts "Caught remote exception: #{e}"
28
+ retry
29
+ end
30
+ end
data/Rakefile CHANGED
@@ -17,10 +17,7 @@ end
17
17
 
18
18
  desc 'Bump version'
19
19
  task 'version' do
20
- file = 'lib/version.rb'
21
- const = 'CorosyncCommander::GEM_VERSION'
22
-
23
- current_version = %x{ruby -e "require './#{file}'; puts #{const}"}.chomp
20
+ current_version = File.read('VERSION').chomp
24
21
  current_version_commit = %x{git rev-parse --verify #{current_version} 2>/dev/null}.chomp
25
22
  current_head_commit = %x{git rev-parse HEAD}.chomp
26
23
  if current_version_commit != '' and current_version_commit != current_head_commit then
@@ -32,23 +29,17 @@ task 'version' do
32
29
  print "Next version? (#{next_version}): "
33
30
  response = STDIN.gets.chomp
34
31
  if response != '' then
35
- raise StandardError, "Not a valid version" unless response.match(/^[0-9\.]$/)
32
+ raise StandardError, "Not a valid version" unless response.match(/^[0-9\.]+$/)
36
33
  next_version = response
37
34
  end
38
35
 
39
- const_name = const.sub(/^.+:/, '')
40
- new_file_content = ''
41
- File.open(file, 'r') do |file|
42
- file.each_line do |line|
43
- new_file_content += line.sub(/(#{const_name}\s*=\s*['"])#{current_version}(['"])/, "\\1#{next_version}\\2")
44
- end
45
- end
46
- File.open(file, 'w') do |file|
47
- file.write new_file_content
36
+ File.open('VERSION', 'w') do |file|
37
+ file.puts next_version
48
38
  end
49
39
  message = %x{git log #{current_version_commit}..HEAD --pretty=format:'* %s%n %an (%ai) - @%h%n'}.gsub(/'/, "'\\\\''")
50
40
 
51
- sh "git commit -m 'Version: #{next_version}\n\n#{message}' #{file}"
41
+ sh "git commit -m 'Version: #{next_version}\n\n#{message}' VERSION"
42
+ sh "git tag #{next_version}"
52
43
 
53
44
  @spec = nil
54
45
  end
@@ -62,7 +53,6 @@ end
62
53
  desc 'Publish gem file'
63
54
  task 'publish' do
64
55
  gem_file = "#{spec.name}-#{spec.version}.gem"
65
- sh "git tag #{spec.version}"
66
56
  sh "git push"
67
57
  sh "gem push #{gem_file}"
68
58
  end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0.0
@@ -1,6 +1,4 @@
1
- require File.expand_path('../lib/version.rb', __FILE__)
2
-
3
- Gem::Specification.new 'corosync-commander', CorosyncCommander::GEM_VERSION do |s|
1
+ Gem::Specification.new 'corosync-commander', File.read('VERSION').chomp do |s|
4
2
  s.description = 'Provides a simplified interface for issuing commands to nodes in a Corosync closed process group.'
5
3
  s.summary = 'Sends/receives Corosync CPG commands'
6
4
  s.homepage = 'http://github.com/phemmer/ruby-corosync-commander/'
@@ -9,5 +7,5 @@ Gem::Specification.new 'corosync-commander', CorosyncCommander::GEM_VERSION do |
9
7
  s.license = 'MIT'
10
8
  s.files = %x{git ls-files}.split("\n")
11
9
 
12
- s.add_runtime_dependency 'corosync', '~> 0.0.3'
10
+ s.add_runtime_dependency 'corosync', '~> 0.1.0'
13
11
  end
data/examples/ccsh.rb ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ # This is a more heavyweight example of the remote_commands example.
3
+ # It utilizes additional features such as quorum. It follows the same principles though, and you can launch it as many times as you want to play with it.
4
+
5
+ if RUBY_ENGINE == 'ruby' and ENV['RUBY_THREAD_MACHINE_STACK_SIZE'].to_i < 1572864 then
6
+ ENV['RUBY_THREAD_MACHINE_STACK_SIZE'] = '1572864'
7
+ exec('ruby', $0, *ARGV)
8
+ end
9
+
10
+ require 'rubygems'
11
+ require 'bundler/setup'
12
+
13
+ $:.unshift(File.expand_path('../../lib', File.realpath(__FILE__)))
14
+
15
+ require 'corosync_commander'
16
+ require 'timeout'
17
+ require 'readline'
18
+
19
+ class CCSH
20
+ def initialize
21
+ @cc = CorosyncCommander.new
22
+ @cc.commands.register 'sh', &self.method(:cc_sh)
23
+ @cc.on_confchg &self.method(:cc_confchg)
24
+ @cc.on_quorumchg &self.method(:cc_quorumchg)
25
+
26
+ @cc.join('ccsh')
27
+ end
28
+
29
+ def cc_sh(sender, command)
30
+ output = nil
31
+ status = nil
32
+ begin
33
+ Timeout::timeout(10) do
34
+ output = %x{#{command}}
35
+ status = $?.exitstatus
36
+ end
37
+ rescue Timeout::Error => e
38
+ output = 'Command timed out'
39
+ status = 255
40
+ end
41
+
42
+ [status, output]
43
+ end
44
+
45
+ def cc_confchg(members, left, joined)
46
+ $stderr.puts "Group membership changed"
47
+ $stderr.puts "Members: #{members.map{|m| m.to_s}.join(' ')}" if members.size > 0
48
+ $stderr.puts "Lost: #{left.map{|m| m.to_s}.join(' ')}" if left.size > 0
49
+ $stderr.puts "Joined: #{joined.map{|m| m.to_s}.join(' ')}" if joined.size > 0
50
+ $stderr.puts ""
51
+ end
52
+
53
+ def cc_quorumchg(quorate, members)
54
+ msg = quorate ? "gained" : "lost"
55
+ $stderr.puts "Quorum #{msg}"
56
+ $stderr.puts ""
57
+ end
58
+
59
+ def run
60
+ while line = Readline.readline('> ', true) do
61
+ line.chomp!
62
+
63
+ if line.match(/^!\s*(.*)/) then
64
+ # internal command
65
+ command = $1
66
+ if command == 'leaders' then
67
+ leader_pool = @cc.instance_variable_get(:@leader_pool)
68
+ $stdout.write(leader_pool.map{|m| m.to_s}.join("\n") + "\n")
69
+ elsif command == 'whoami' then
70
+ $stdout.write(@cc.cpg.member.to_s + "\n")
71
+ elsif command == 'exit' then
72
+ exit
73
+ end
74
+ else
75
+ # remote command
76
+ exe = @cc.execute([], 'sh', line)
77
+ exe.to_enum.each do |sender, result|
78
+ status, output = result
79
+ output.split("\n").each do |line|
80
+ $stdout.write("#{sender}: #{line}\n")
81
+ end
82
+ end
83
+ end
84
+
85
+ $stdout.write("\n")
86
+ end
87
+ end
88
+ end
89
+
90
+ ccsh = CCSH.new
91
+ ccsh.run
@@ -0,0 +1,38 @@
1
+ # This example will send shell commands to all the running instances in the cluster.
2
+ # It connects to the 'remote commands' CPG group, executes any commands received, and sends the result back to the app that sent the command.
3
+ # The local app will read commands from STDIN and display the response from all the nodes.
4
+ #
5
+ # Launch this as many times as you want and play around with it.
6
+ #
7
+
8
+
9
+ if RUBY_ENGINE == 'ruby' and !ENV['RUBY_THREAD_MACHINE_STACK_SIZE'] then
10
+ ENV['RUBY_THREAD_MACHINE_STACK_SIZE'] = '1572864'
11
+ exec('ruby', $0, *ARGV)
12
+ end
13
+
14
+ $:.unshift(File.expand_path('../../lib', __FILE__))
15
+ require 'corosync_commander'
16
+ cc = CorosyncCommander.new
17
+ cc.commands.register('shell command') do |sender, shellcmd|
18
+ %x{#{shellcmd}}
19
+ end
20
+ cc.join('remote commands')
21
+
22
+ while line = STDIN.gets do
23
+ exe = cc.execute([], 'shell command', line.chomp)
24
+ enum = exe.to_enum
25
+ begin
26
+ enum.each do |sender, response|
27
+ $stdout.write "
28
+ #{sender.nodeid}:#{sender.pid}:
29
+ #{response.chomp}
30
+ ========================================
31
+
32
+ "
33
+ end
34
+ rescue CorosyncCommander::RemoteException => e
35
+ puts "Caught remote exception: #{e}"
36
+ retry
37
+ end
38
+ end
@@ -47,18 +47,21 @@ class CorosyncCommander::Execution
47
47
  # Provides an enumerator that can be looped through.
48
48
  # Will raise an exception if the remote node generated an exception. Can be verified by calling `is_a?(CorosyncCommander::RemoteException)`.
49
49
  # Restarting the enumerator will not restart from the first response, it will continue on to the next.
50
+ # @param ignore [Exception, Boolean] List of exception classes to suppress. `true` for all
50
51
  # @yieldparam response [Object] The response generated by the remote command.
51
52
  # @yieldparam sender [Corosync::CPG::Member] The node which generated the response.
52
53
  # @return [Enumerator]
53
- def to_enum(ignore_exception = false)
54
+ def to_enum(*ignore)
55
+ ignore = [Exception] if ignore.size == 1 and ignore[0] == true
56
+
54
57
  Enumerator.new do |block|
55
58
  begin
56
59
  while response = self.response do
57
- block.yield response.content, response.sender
60
+ block.yield response.sender, response.content
58
61
  end
59
62
  rescue CorosyncCommander::RemoteException => e
60
- raise e unless ignore_exception
61
- retry
63
+ retry if ignore.find {|extype| e.is_a?(extype)}
64
+ raise e
62
65
  end
63
66
  end
64
67
  end
@@ -104,6 +107,7 @@ class CorosyncCommander::Execution
104
107
 
105
108
  raise RuntimeError, "Received unexpected response while waiting for echo" if message.type != 'echo'
106
109
 
110
+ # CorosyncCommander#cpg_message sets the content to the list of cpg members at the time the echo was received
107
111
  @pending_members = @recipients.size == 0 ? message.content.dup : message.content & @recipients
108
112
  end
109
113
 
@@ -1,4 +1,5 @@
1
1
  require 'corosync/cpg'
2
+ require 'corosync/quorum'
2
3
  require File.expand_path('../corosync_commander/execution', __FILE__)
3
4
  require File.expand_path('../corosync_commander/execution/message', __FILE__)
4
5
  require File.expand_path('../corosync_commander/callback_list', __FILE__)
@@ -16,7 +17,7 @@ require File.expand_path('../corosync_commander/callback_list', __FILE__)
16
17
  #
17
18
  # @example
18
19
  # cc = CorosyncCommander.new
19
- # cc.commands.register('shell command') do |shellcmd|
20
+ # cc.commands.register('shell command') do |sender, shellcmd|
20
21
  # %x{#{shellcmd}}
21
22
  # end
22
23
  # cc.join('my group')
@@ -26,7 +27,7 @@ require File.expand_path('../corosync_commander/callback_list', __FILE__)
26
27
  # enum = exe.to_enum
27
28
  # hostnames = []
28
29
  # begin
29
- # enum.each do |response, node|
30
+ # enum.each do |sender, response|
30
31
  # hostnames << response
31
32
  # end
32
33
  # rescue CorosyncCommander::RemoteException => e
@@ -64,6 +65,11 @@ class CorosyncCommander
64
65
  @cpg.connect
65
66
  @cpg.fd.close_on_exec = true
66
67
 
68
+ @quorum = Corosync::Quorum.new
69
+ @quorum.on_notify {|*args| quorum_notify(*args)}
70
+ @quorum.connect
71
+ @quorum.fd.close_on_exec = true
72
+
67
73
  @cpg_members = nil
68
74
 
69
75
  @leader_pool = []
@@ -78,16 +84,30 @@ class CorosyncCommander
78
84
 
79
85
  @command_callbacks = CorosyncCommander::CallbackList.new
80
86
 
87
+ if RUBY_ENGINE == 'ruby' and (Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0') or ENV['RUBY_THREAD_MACHINE_STACK_SIZE'].to_i < 1572864) then
88
+ abort "MRI Ruby must be >= 2.0 and RUBY_THREAD_MACHINE_STACK_SIZE must be > 1572864"
89
+ end
90
+
91
+ join(group_name) if group_name
92
+ end
93
+
94
+ # Starts watching for notifications
95
+ # @return [void]
96
+ def start
97
+ @quorum.start(true)
98
+
81
99
  @dispatch_thread = Thread.new do
82
100
  Thread.current.abort_on_exception = true
83
101
  loop do
84
- @cpg.dispatch
102
+ select_ready = select([@cpg.fd, @quorum.fd], [], [])
103
+ if select_ready[0].include?(@quorum.fd) then
104
+ @quorum.dispatch
105
+ end
106
+ if select_ready[0].include?(@cpg.fd) then
107
+ @cpg.dispatch
108
+ end
85
109
  end
86
110
  end
87
-
88
- if group_name then
89
- join(group_name)
90
- end
91
111
  end
92
112
 
93
113
  # Joins the specified group.
@@ -95,17 +115,30 @@ class CorosyncCommander
95
115
  # @param group_name [String] Name of group to join
96
116
  # @return [void]
97
117
  def join(group_name)
118
+ start unless @dispatch_thread
119
+
98
120
  @cpg.join(group_name)
99
121
  end
100
122
 
123
+ # Leave the active CPG group.
124
+ # Will not stop quorum notifications. If you wish to stop quorum as well you should use {#stop} instead.
125
+ # @return [void]
126
+ def leave
127
+ @cpg.leave
128
+ end
129
+
101
130
  # Shuts down the dispatch thread and disconnects CPG
102
131
  # @return [void]
103
132
  def stop
104
- @dispatch_thread.kill
133
+ @dispatch_thread.kill if !@dispatch_thread.nil?
105
134
  @dispatch_thread = nil
106
- @cpg.disconnect
135
+
136
+ @cpg.close if !@cpg.nil?
107
137
  @cpg = nil
108
138
  @cpg_members = nil
139
+
140
+ @quorum.finalize if !@quorum.nil?
141
+ @quorum = nil
109
142
  end
110
143
 
111
144
  def next_execution_id()
@@ -154,6 +187,19 @@ class CorosyncCommander
154
187
  message_echo.content = @cpg_members
155
188
  execution_queue << message_echo
156
189
  end
190
+ elsif message.type == 'leader reset' then
191
+ # The sender is requesting we reset their leader position
192
+ # For remote node, act as if the node left.
193
+ # For the local node, act as if we just joined.
194
+ if sender != @cpg.member then
195
+ @leader_pool.sync_synchronize(:EX) do
196
+ @leader_pool.delete(sender)
197
+ end
198
+ else
199
+ @leader_pool.sync_synchronize(:EX) do
200
+ @leader_pool.replace(@cpg_members.to_a)
201
+ end
202
+ end
157
203
  elsif message.type != 'command' and message.recipients.include?(@cpg.member) then
158
204
  # It's a response to us
159
205
  execution_queue = nil
@@ -183,6 +229,7 @@ class CorosyncCommander
183
229
  message_reply = message.reply(reply_value)
184
230
  @cpg.send(message_reply)
185
231
  rescue => e
232
+ $stderr.puts "Exception: #{e} (#{e.class})\n#{e.backtrace.join("\n")}"
186
233
  message_reply = message.reply([e.class, e.to_s, e.backtrace])
187
234
  message_reply.type = 'exception'
188
235
  @cpg.send(message_reply)
@@ -214,7 +261,7 @@ class CorosyncCommander
214
261
  end
215
262
 
216
263
  @execution_queues.sync_synchronize(:SH) do
217
- @execution_queues.each do |queue|
264
+ @execution_queues.values.each do |queue|
218
265
  messages.each do |message|
219
266
  queue << message
220
267
  end
@@ -222,10 +269,34 @@ class CorosyncCommander
222
269
  end
223
270
  end
224
271
 
272
+ # Callback to execute when the CPG configuration changes
273
+ # @yieldparam member_list [Array<Corosync::CPG::Member>] List of members in group after change
274
+ # @yieldparam left_list [Array<Corosync::CPG::Member>] List of members which left the group
275
+ # @yieldparam join_list [Array<Corosync::CPG::Member>] List of members which joined the group
225
276
  def on_confchg(&block)
226
277
  @confchg_callback = block
227
278
  end
228
279
 
280
+ # @!visibility private
281
+ def quorum_notify(quorate, node_list)
282
+ return if @quorate == quorate # didn't change
283
+ @quorate = quorate
284
+
285
+ if quorate then
286
+ # we just became quorate
287
+ @cpg.send(CorosyncCommander::Execution::Message.new(:recipients => [], :type => 'leader reset')) # 'leader reset' simulates a leave and then a join of this node
288
+ end
289
+
290
+ @quorumchg_callback.call(quorate, node_list) if @quorumchg_callback
291
+ end
292
+
293
+ # Callback to execute when the quorum state changes
294
+ # @yieldparam quorate [Boolean] Whether cluster is quorate
295
+ # @yieldparam member_list [Array] List of node IDs in the cluster after change
296
+ def on_quorumchg(&block)
297
+ @quorumchg_callback = block
298
+ end
299
+
229
300
  # @!attribute [r] commands
230
301
  # @return [CorosyncCommander::CallbackList] List of command callbacks
231
302
  def commands
@@ -277,6 +348,12 @@ class CorosyncCommander
277
348
  # This is slightly different than just calling `leader_position == 0` in that if it is -1 (meaning we havent received the CPG confchg callback yet), we wait for the CPG join to complete.
278
349
  # @return [Boolean]
279
350
  def leader?
351
+
352
+ # The way leadership works is that we record the members that were present when we joined the group in @leader_pool. Each time a node leaves the group, we remove them from @leader_pool. Once we become the only member in @leader_pool, we are the leader.
353
+ # Now in the event that the cluster splits, this becomes complicated. Each side will see the members of the other side leaving. So each side will end up with their own leader. Since they can't talk to eachother, having a leader in each group is perfectly fine. However when the 2 sides re-join, each side will see the members of the other side joining as new nodes, and both leaders will remain as leaders.
354
+ # We solve this by using the quorum status. When we go from inquorate to quorate, we give up our position. We send a 'leader reset' command to the cluster which tells everyone to remove us from their @leader_pool. When we receive the message ourself, we set @leader_pool to the group members at that moment.
355
+ # It doesn't matter if multiple members end up doing a 'leader reset' at the same time. It basically simulates the node leaving and then joining. Whoever performs the action first will move to the front. It will capture @leader_pool as the current members when it receives it's own message, and as the other resets come in, it will remove those members. Leaving itself in front of the ones that just joined (and reset after). But it will still remain after all the members that didn't do a reset.
356
+
280
357
  position = nil
281
358
  loop do
282
359
  position = leader_position
@@ -287,4 +364,16 @@ class CorosyncCommander
287
364
  end
288
365
  position == 0
289
366
  end
367
+
368
+ # Indicates whether cluster is quorate.
369
+ # @return [Boolean]
370
+ def quorate?
371
+ @quorate
372
+ end
373
+
374
+ # List of current members
375
+ # @return [Array<Corosync::CPG::Member>] List of members currently in the group
376
+ def members
377
+ @cpg_members
378
+ end
290
379
  end
@@ -44,7 +44,7 @@ describe CorosyncCommander do
44
44
 
45
45
  it 'calls the callback' do
46
46
  exe = @cc.execute([], 'summation', 123, 456)
47
- results = exe.to_enum.collect do |response,node|
47
+ results = exe.to_enum.collect do |node,response|
48
48
  response
49
49
  end
50
50
 
@@ -101,6 +101,31 @@ describe CorosyncCommander do
101
101
  end
102
102
  end
103
103
 
104
+ it 'handles pending responses when a node leaves' do
105
+ complete = false
106
+ Timeout.timeout(5) do
107
+ forkpid = fork do
108
+ cc = CorosyncCommander.new(@cc.cpg.group)
109
+ cc.commands.register('exit') do |sender|
110
+ sleep 2
111
+ exit!
112
+ end
113
+ cc.dispatch_thread.join
114
+ exit 1
115
+ end
116
+
117
+ sleep 1 # wait for the fork to connect
118
+
119
+ @cc.commands.register('exit') {} # ignore it
120
+ exe = @cc.execute([], 'exit')
121
+ exe.wait
122
+ complete = true
123
+ Process.kill('TERM', forkpid)
124
+ end
125
+
126
+ expect(complete).to eq(true)
127
+ end
128
+
104
129
  it 'captures remote exceptions' do
105
130
  Timeout.timeout(5) do
106
131
  @cc.commands.register('make exception') do
@@ -141,7 +166,7 @@ describe CorosyncCommander do
141
166
  responses = []
142
167
  exceptions = 0
143
168
  begin
144
- enum.each do |response,node|
169
+ enum.each do |node,response|
145
170
  responses << response
146
171
  end
147
172
  rescue CorosyncCommander::RemoteException
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: corosync-commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Hemmer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-10 00:00:00.000000000 Z
11
+ date: 2014-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: corosync
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.3
19
+ version: 0.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.3
26
+ version: 0.1.0
27
27
  description: Provides a simplified interface for issuing commands to nodes in a Corosync
28
28
  closed process group.
29
29
  email: patrick.hemmer@gmail.com
@@ -35,13 +35,16 @@ files:
35
35
  - Gemfile
36
36
  - Gemfile.lock
37
37
  - LICENSE
38
+ - README.md
38
39
  - Rakefile
40
+ - VERSION
39
41
  - corosync-commander.gemspec
42
+ - examples/ccsh.rb
43
+ - examples/remote_commands.rb
40
44
  - lib/corosync_commander.rb
41
45
  - lib/corosync_commander/callback_list.rb
42
46
  - lib/corosync_commander/execution.rb
43
47
  - lib/corosync_commander/execution/message.rb
44
- - lib/version.rb
45
48
  - spec/corosync_commander.rb
46
49
  - spec/spec_helper.rb
47
50
  homepage: http://github.com/phemmer/ruby-corosync-commander/
data/lib/version.rb DELETED
@@ -1,3 +0,0 @@
1
- class CorosyncCommander
2
- GEM_VERSION = '0.0.2'
3
- end