ztk 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ztk.rb CHANGED
@@ -31,6 +31,8 @@ module ZTK
31
31
  class Error < StandardError; end
32
32
 
33
33
  autoload :Base, 'ztk/base'
34
+
35
+ autoload :Background, 'ztk/background'
34
36
  autoload :Benchmark, 'ztk/benchmark'
35
37
  autoload :Command, 'ztk/command'
36
38
  autoload :Config, 'ztk/config'
@@ -0,0 +1,145 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.net>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, VersIOn 2.0
6
+ #
7
+ # Licensed under the Apache License, VersIOn 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissIOns and
17
+ # limitatIOns under the License.
18
+ #
19
+ ################################################################################
20
+
21
+ require "base64"
22
+
23
+ module ZTK
24
+
25
+ # ZTK::Background Error Class
26
+ #
27
+ # @author Zachary Patten <zachary@jovelabs.net>
28
+ class BackgroundError < Error; end
29
+
30
+ # Background Processing Class
31
+ #
32
+ # This class can be used to easily run a linear process in a background manner.
33
+ #
34
+ # a_callback = Proc.new do |pid|
35
+ # puts "Hello from After Callback - PID #{pid}"
36
+ # end
37
+ #
38
+ # b_callback = Proc.new do |pid|
39
+ # puts "Hello from Before Callback - PID #{pid}"
40
+ # end
41
+ #
42
+ # background = ZTK::Background.new
43
+ # background.config do |config|
44
+ # config.before_fork = b_callback
45
+ # config.after_fork = a_callback
46
+ # end
47
+ #
48
+ # background.process do
49
+ # x
50
+ # end
51
+ #
52
+ # background.wait
53
+ # background.result
54
+ #
55
+ # The before fork callback is called once in the parent process.
56
+ #
57
+ # The after fork callback is called twice, once in the parent process and once
58
+ # in the child process.
59
+ #
60
+ # @author Zachary Patten <zachary@jovelabs.net>
61
+ class Background < ZTK::Base
62
+
63
+ # Result Set
64
+ attr_accessor :pid, :result
65
+
66
+ # @param [Hash] config Configuration options hash.
67
+ # @option config [Integer] :max_forks Maximum number of forks to use.
68
+ # @option config [Proc] :before_fork (nil) Proc to call before forking.
69
+ # @option config [Proc] :after_fork (nil) Proc to call after forking.
70
+ def initialize(configuration={})
71
+ super({
72
+ }.merge(configuration))
73
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
74
+
75
+ @result = nil
76
+ GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
77
+ end
78
+
79
+ # Process in background.
80
+ #
81
+ # @yield Block should execute tasks to be performed in background.
82
+ # @yieldreturn [Object] Block can return any object to be marshalled back to
83
+ # the parent processes result set.
84
+ # @return [Integer] Returns the pid of the child process forked.
85
+ def process(&block)
86
+ !block_given? and log_and_raise(BackgroundError, "You must supply a block to the process method!")
87
+
88
+ @child_reader, @parent_writer = IO.pipe
89
+ @parent_reader, @child_writer = IO.pipe
90
+
91
+ config.before_fork and config.before_fork.call(Process.pid)
92
+ @pid = Process.fork do
93
+ config.after_fork and config.after_fork.call(Process.pid)
94
+
95
+ @parent_writer.close
96
+ @parent_reader.close
97
+
98
+ STDOUT.reopen("/dev/null", "a")
99
+ STDERR.reopen("/dev/null", "a")
100
+ STDIN.reopen("/dev/null")
101
+
102
+ if !(data = block.call).nil?
103
+ config.logger.debug { "write(#{data.inspect})" }
104
+ @child_writer.write(Base64.encode64(Marshal.dump(data)))
105
+ end
106
+
107
+ @child_reader.close
108
+ @child_writer.close
109
+ Process.exit!(0)
110
+ end
111
+ config.after_fork and config.after_fork.call(Process.pid)
112
+
113
+ @child_reader.close
114
+ @child_writer.close
115
+
116
+ @pid
117
+ end
118
+
119
+ # Wait for the background process to finish.
120
+ #
121
+ # If a process successfully finished, it's return value from the *process*
122
+ # block is stored into the result set.
123
+ #
124
+ # @return [Array<pid, status, data>] An array containing the pid,
125
+ # status and data returned from the process block. If wait2() fails nil
126
+ # is returned.
127
+ def wait
128
+ config.logger.debug { "wait" }
129
+ pid, status = (Process.wait2(@pid) rescue nil)
130
+ if !pid.nil? && !status.nil?
131
+ data = (Marshal.load(Base64.decode64(@parent_reader.read.to_s)) rescue nil)
132
+ config.logger.debug { "read(#{data.inspect})" }
133
+ !data.nil? and @result = data
134
+
135
+ @parent_reader.close
136
+ @parent_writer.close
137
+
138
+ return [pid, status, data]
139
+ end
140
+ nil
141
+ end
142
+
143
+ end
144
+
145
+ end
data/lib/ztk/benchmark.rb CHANGED
@@ -35,7 +35,7 @@ module ZTK
35
35
 
36
36
  def bench(options={}, &block)
37
37
  options = Base.build_config(options)
38
- options.logger.debug { "options(#{options.inspect})" }
38
+ options.logger.debug { "options=#{options.send(:table).inspect}" }
39
39
 
40
40
  !block_given? and Base.log_and_raise(options.logger, BenchmarkError, "You must supply a block!")
41
41
 
@@ -54,7 +54,7 @@ module ZTK
54
54
  end
55
55
 
56
56
  (options.message && options.mark) and options.stdout.print("#{options.mark}\n" % benchmark)
57
- options.logger.info { "#{options.message} #{options.mark}" }
57
+ options.logger.info { "#{options.message} #{options.mark}" % benchmark }
58
58
 
59
59
  benchmark
60
60
  end
data/lib/ztk/command.rb CHANGED
@@ -44,9 +44,10 @@ module ZTK
44
44
 
45
45
  def initialize(configuration={})
46
46
  super({
47
- :timeout => 600
47
+ :timeout => 600,
48
+ :ignore_exit_status => false
48
49
  }.merge(configuration))
49
- config.logger.debug { "config(#{config.inspect})" }
50
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
50
51
  end
51
52
 
52
53
  def inspect
@@ -78,8 +79,8 @@ module ZTK
78
79
 
79
80
  options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(options))
80
81
 
81
- config.logger.debug { "config(#{config.inspect})" }
82
- config.logger.debug { "options(#{options.inspect})" }
82
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
83
+ config.logger.debug { "options=#{options.send(:table).inspect}" }
83
84
  config.logger.info { "command(#{command.inspect})" }
84
85
 
85
86
  output = ""
@@ -167,7 +168,7 @@ module ZTK
167
168
 
168
169
  config.logger.debug { "exit_code(#{exit_code})" }
169
170
 
170
- if (exit_code != options.exit_code)
171
+ if !config.ignore_exit_status && (exit_code != options.exit_code)
171
172
  log_and_raise(CommandError, "exec(#{command.inspect}, #{options.inspect}) failed! [#{exit_code}]")
172
173
  end
173
174
  OpenStruct.new(:output => output, :exit_code => exit_code)
data/lib/ztk/logger.rb CHANGED
@@ -61,6 +61,10 @@ module ZTK
61
61
  add(severity, nil, nil, shift, &block)
62
62
  end
63
63
 
64
+ def inspect
65
+ "#<#{self.class} filename=#{@logdev.filename.inspect}>"
66
+ end
67
+
64
68
 
65
69
  private
66
70
 
data/lib/ztk/parallel.rb CHANGED
@@ -80,7 +80,7 @@ module ZTK
80
80
  super({
81
81
  :max_forks => MAX_FORKS
82
82
  }.merge(configuration))
83
- config.logger.debug { "config(#{config.inspect})" }
83
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
84
84
 
85
85
  (config.max_forks < 1) and log_and_raise(ParallelError, "max_forks must be equal to or greater than one!")
86
86
 
@@ -34,9 +34,10 @@ module ZTK
34
34
  def try(options={}, &block)
35
35
  options = Base.build_config({
36
36
  :tries => 1,
37
- :on => Exception
37
+ :on => Exception,
38
+ :delay => 1
38
39
  }.merge(options))
39
- options.logger.debug { "options(#{options.inspect})" }
40
+ options.logger.debug { "options=#{options.send(:table).inspect}" }
40
41
 
41
42
  !block_given? and Base.log_and_raise(options.logger, RescueRetryError, "You must supply a block!")
42
43
 
@@ -45,9 +46,10 @@ module ZTK
45
46
  rescue options.on => e
46
47
  if ((options.tries -= 1) > 0)
47
48
  options.logger.warn { "Caught #{e.inspect}, we will give it #{options.tries} more tr#{options.tries > 1 ? 'ies' : 'y'}." }
49
+ sleep(options.delay)
48
50
  retry
49
51
  else
50
- options.logger.fatal { "Caught #{e.inspect}, sorry, we have to give up now." }
52
+ options.logger.fatal { "Caught #{e.inspect} and we have no more tries left, sorry, we have to give up now." }
51
53
  raise e
52
54
  end
53
55
  end
data/lib/ztk/spinner.rb CHANGED
@@ -40,7 +40,7 @@ module ZTK
40
40
  options = Base.build_config({
41
41
  :sleep => 0.1
42
42
  }.merge(options))
43
- options.logger.debug { "options(#{options.inspect})" }
43
+ options.logger.debug { "options(#{options.send(:table).inspect})" }
44
44
 
45
45
  !block_given? and Base.log_and_raise(options.logger, SpinnerError, "You must supply a block!")
46
46
 
data/lib/ztk/ssh.rb CHANGED
@@ -85,6 +85,36 @@ module ZTK
85
85
  # @author Zachary Patten <zachary@jovelabs.net>
86
86
  class SSH < ZTK::Base
87
87
 
88
+ EXIT_SIGNALS = {
89
+ 1 => "SIGHUP",
90
+ 2 => "SIGINT",
91
+ 3 => "SIGQUIT",
92
+ 4 => "SIGILL",
93
+ 5 => "SIGTRAP",
94
+ 6 => "SIGABRT",
95
+ 7 => "SIGBUS",
96
+ 8 => "SIGFPE",
97
+ 9 => "SIGKILL",
98
+ 10 => "SIGUSR1",
99
+ 11 => "SIGSEGV",
100
+ 12 => "SIGUSR2",
101
+ 13 => "SIGPIPE",
102
+ 14 => "SIGALRM",
103
+ 15 => "SIGTERM",
104
+ # 16 unused?
105
+ 17 => "SIGCHLD",
106
+ 18 => "SIGCONT",
107
+ 19 => "SIGSTOP",
108
+ 20 => "SIGTSTP",
109
+ 21 => "SIGTTIN",
110
+ 22 => "SIGTTOU",
111
+ 23 => "SIGURG",
112
+ 24 => "SIGXCPU",
113
+ 25 => "SIGXFSZ",
114
+ 26 => "SIGVTALRM",
115
+ 27 => "SIGPROF"
116
+ }
117
+
88
118
  # @param [Hash] config Configuration options hash.
89
119
  # @option config [String] :host_name Server hostname to connect to.
90
120
  # @option config [String] :user Username to use for authentication.
@@ -106,9 +136,10 @@ module ZTK
106
136
  :forward_agent => true,
107
137
  :compression => false,
108
138
  :user_known_hosts_file => '/dev/null',
109
- :timeout => 60
139
+ :timeout => 60,
140
+ :ignore_exit_status => false
110
141
  }.merge(configuration))
111
- config.logger.debug { "config(#{config.inspect})" }
142
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
112
143
  end
113
144
 
114
145
  def inspect
@@ -160,7 +191,7 @@ module ZTK
160
191
  # end
161
192
  # ssh.console
162
193
  def console
163
- config.logger.debug { "config(#{config.inspect})" }
194
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
164
195
  config.logger.info { "console(#{console_command.inspect})" }
165
196
 
166
197
  Kernel.exec(console_command)
@@ -198,12 +229,12 @@ module ZTK
198
229
 
199
230
  options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(options))
200
231
 
201
- config.logger.debug { "config(#{config.inspect})" }
202
- config.logger.debug { "options(#{options.inspect})" }
203
- config.logger.info { "exec(#{command.inspect}, #{options.inspect})" }
232
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
233
+ config.logger.debug { "options=#{options.send(:table).inspect}" }
234
+ config.logger.info { "exec(#{command.inspect})" }
204
235
 
205
236
  output = ""
206
- exit_code = nil
237
+ exit_code = -1
207
238
  exit_signal = nil
208
239
  stdout_header = false
209
240
  stderr_header = false
@@ -273,10 +304,15 @@ module ZTK
273
304
  log_and_raise(SSHError, "Session timed out after #{config.timeout} seconds!")
274
305
  end
275
306
 
276
- config.logger.debug { "exit_code(#{exit_code}), exit_signal(#{exit_signal})" }
307
+ message = [
308
+ "exit_code=#{exit_code}",
309
+ (exit_signal.nil? ? nil : "exit_signal=#{exit_signal} (#{EXIT_SIGNALS[exit_signal]})")
310
+ ].compact.join(", ")
311
+
312
+ config.logger.debug { message }
277
313
 
278
- if (exit_code != options.exit_code)
279
- log_and_raise(SSHError, "exec(#{command.inspect}, #{options.inspect}) failed! [#{exit_code}, #{exit_signal}]")
314
+ if !config.ignore_exit_status && (exit_code != options.exit_code)
315
+ log_and_raise(SSHError, message)
280
316
  end
281
317
  OpenStruct.new(:output => output, :exit_code => exit_code, :exit_signal => exit_signal)
282
318
  end
@@ -297,7 +333,7 @@ module ZTK
297
333
  # remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
298
334
  # ssh.upload(local, remote)
299
335
  def upload(local, remote)
300
- config.logger.debug { "config(#{config.inspect})" }
336
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
301
337
  config.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
302
338
 
303
339
  ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
@@ -337,7 +373,7 @@ module ZTK
337
373
  # remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
338
374
  # ssh.download(remote, local)
339
375
  def download(remote, local)
340
- config.logger.debug { "config(#{config.inspect})" }
376
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
341
377
  config.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
342
378
 
343
379
  ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
@@ -95,7 +95,7 @@ module ZTK
95
95
  :timeout => 5,
96
96
  :wait => 60
97
97
  }.merge(configuration))
98
- config.logger.debug { "config(#{config.inspect})" }
98
+ config.logger.debug { "config=#{config.send(:table).inspect}" }
99
99
  end
100
100
 
101
101
  # Check to see if socket on the host and port specified is ready. This
data/lib/ztk/version.rb CHANGED
@@ -19,5 +19,5 @@
19
19
  ################################################################################
20
20
 
21
21
  module ZTK
22
- VERSION = "0.2.5" unless const_defined?(:VERSION)
22
+ VERSION = "0.2.6" unless const_defined?(:VERSION)
23
23
  end
@@ -0,0 +1,108 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Zachary Patten <zachary@jovelabs.net>
4
+ # Copyright: Copyright (c) Jove Labs
5
+ # License: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ ################################################################################
20
+
21
+ require "spec_helper"
22
+
23
+ describe ZTK::Background do
24
+
25
+ subject { ZTK::Background.new }
26
+
27
+ before(:all) do
28
+ $stdout = File.open("/dev/null", "w")
29
+ $stderr = File.open("/dev/null", "w")
30
+ $stdin = File.open("/dev/null", "r")
31
+ end
32
+
33
+ describe "class" do
34
+
35
+ it "should be an instance of ZTK::Background" do
36
+ subject.should be_an_instance_of ZTK::Background
37
+ end
38
+
39
+ describe "default config" do
40
+
41
+ it "should use $stdout as the default" do
42
+ subject.config.stdout.should be_a_kind_of $stdout.class
43
+ subject.config.stdout.should == $stdout
44
+ end
45
+
46
+ it "should use $stderr as the default" do
47
+ subject.config.stderr.should be_a_kind_of $stderr.class
48
+ subject.config.stderr.should == $stderr
49
+ end
50
+
51
+ it "should use $stdin as the default" do
52
+ subject.config.stdin.should be_a_kind_of $stdin.class
53
+ subject.config.stdin.should == $stdin
54
+ end
55
+
56
+ it "should use $logger as the default" do
57
+ subject.config.logger.should be_a_kind_of ZTK::Logger
58
+ subject.config.logger.should == $logger
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ describe "behaviour" do
66
+
67
+ it "should throw an exception if the process method is called without a block" do
68
+ lambda{ subject.process }.should raise_error ZTK::BackgroundError, "You must supply a block to the process method!"
69
+ end
70
+
71
+ describe "process" do
72
+
73
+ it "should spawn a process to handle the task" do
74
+ subject.process do
75
+ Process.pid
76
+ end
77
+ subject.wait
78
+
79
+ subject.result.should be_kind_of Integer
80
+ subject.result.should > 0
81
+ subject.result.should_not == Process.pid
82
+ end
83
+
84
+ end
85
+
86
+ describe "result" do
87
+
88
+ it "should marshal objects" do
89
+ class BackgroundMarshalTest
90
+ def hello_world
91
+ "Hello World"
92
+ end
93
+ end
94
+
95
+ subject.process do
96
+ BackgroundMarshalTest.new
97
+ end
98
+ subject.wait
99
+
100
+ subject.result.should be_kind_of BackgroundMarshalTest
101
+ subject.result.hello_world.should == "Hello World"
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ end
@@ -74,8 +74,9 @@ describe ZTK::Parallel do
74
74
  Process.pid
75
75
  end
76
76
  end
77
+
77
78
  subject.waitall
78
- puts subject.results.inspect
79
+
79
80
  subject.results.all?{ |r| r.should be_kind_of Integer }
80
81
  subject.results.all?{ |r| r.should > 0 }
81
82
  subject.results.uniq.count.should == 3
@@ -88,10 +89,11 @@ describe ZTK::Parallel do
88
89
  Process.pid
89
90
  end
90
91
  end
92
+
91
93
  3.times do
92
94
  subject.wait
93
95
  end
94
- puts subject.results.inspect
96
+
95
97
  subject.results.all?{ |r| r.should be_kind_of Integer }
96
98
  subject.results.all?{ |r| r.should > 0 }
97
99
  subject.results.uniq.count.should == 3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ztk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -173,6 +173,7 @@ files:
173
173
  - Rakefile
174
174
  - bin/ztk
175
175
  - lib/ztk.rb
176
+ - lib/ztk/background.rb
176
177
  - lib/ztk/base.rb
177
178
  - lib/ztk/benchmark.rb
178
179
  - lib/ztk/command.rb
@@ -188,6 +189,7 @@ files:
188
189
  - spec/spec_helper.rb
189
190
  - spec/support/test-config.rb
190
191
  - spec/support/test-template.txt.erb
192
+ - spec/ztk/background_spec.rb
191
193
  - spec/ztk/base_spec.rb
192
194
  - spec/ztk/benchmark_spec.rb
193
195
  - spec/ztk/command_spec.rb
@@ -214,7 +216,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
214
216
  version: '0'
215
217
  segments:
216
218
  - 0
217
- hash: 394683779756367584
219
+ hash: -2261439734103096284
218
220
  required_rubygems_version: !ruby/object:Gem::Requirement
219
221
  none: false
220
222
  requirements:
@@ -223,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
225
  version: '0'
224
226
  segments:
225
227
  - 0
226
- hash: 394683779756367584
228
+ hash: -2261439734103096284
227
229
  requirements: []
228
230
  rubyforge_project:
229
231
  rubygems_version: 1.8.24
@@ -234,6 +236,7 @@ test_files:
234
236
  - spec/spec_helper.rb
235
237
  - spec/support/test-config.rb
236
238
  - spec/support/test-template.txt.erb
239
+ - spec/ztk/background_spec.rb
237
240
  - spec/ztk/base_spec.rb
238
241
  - spec/ztk/benchmark_spec.rb
239
242
  - spec/ztk/command_spec.rb