ztk 0.2.5 → 0.2.6

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/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