nand 0.1.2

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.
@@ -0,0 +1,63 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ require 'pathname'
4
+ require 'nand/launcher'
5
+
6
+ module Nand
7
+ module Launcher
8
+ class RbFileLauncher < Base
9
+ def self.launchable?( target, io, opts )
10
+ return false unless target.to_s =~/\.rb$/
11
+ require_rb(target) and
12
+ true
13
+ rescue LoadError => e
14
+ io.puts "\t- " + e.message
15
+ false
16
+ rescue => e
17
+ io.puts "\t- " + e.message
18
+ false
19
+ end
20
+ def self.load( target, opts, *argv )
21
+ plugin = opts[:plugin] || File.basename(target, ".rb").split(/_/).map{|s| s.capitalize}.join
22
+ #raise "Option --plugin is Required for #{target}" if plugin.nil?
23
+ executor = Plugin.plugin!(plugin, *argv)
24
+ raise "Executor #{plugin} is Not Emplemented exec Method" unless executor.respond_to?(:exec)
25
+ RbFileLauncher.new(target, executor, opts, *argv)
26
+ rescue LoadError => e
27
+ raise "Not Found Plugin #{target}"
28
+ end
29
+ def self.require_rb(target)
30
+ require "#{RbFileLauncher.rb_file(target).to_s}"
31
+ end
32
+ def self.rb_file(target)
33
+ Pathname.new(target).expand_path
34
+ end
35
+ def initialize(target, executor, opts, *argv)
36
+ super(target, opts, *argv)
37
+ @executor = executor
38
+ end
39
+ def launch
40
+ if child = Process.fork
41
+ child
42
+ else
43
+ begin
44
+ STDIN.reopen(@exec_stdin)
45
+ STDOUT.reopen(@exec_stdout)
46
+ STDERR.reopen(@exec_stderr)
47
+ Signal.trap(:INT) {exit 0}
48
+ Signal.trap(:TERM) {exit 0}
49
+ Process.setpgrp
50
+ @executor.exec
51
+ rescue LoadError => e
52
+ STDERR.puts e.message
53
+ rescue => e
54
+ STDERR.puts e.message
55
+ ensure
56
+
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,25 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ require 'nand/launcher'
4
+
5
+ module Nand
6
+ module Launcher
7
+ class ShellLauncher < Base
8
+ def self.launchable?(target, io, opts)
9
+ require 'mkmf'
10
+ raise "Not Executable #{target}" if find_executable0(target).nil?
11
+ true
12
+ rescue => e
13
+ io.puts "\t- " + e.message
14
+ false
15
+ end
16
+ def self.load(target, opts = {}, *argv)
17
+ new(target, opts, *argv)
18
+ end
19
+ def cmd; "#{@progname} #{@argv.join(" ")}" end
20
+ def launch
21
+ spawn("#{cmd}", :out => @exec_stdout, :err => @exec_stderr, :in => @exec_stdin, :pgroup => true)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ require 'logger'
4
+
5
+ module Nand
6
+ module Logging
7
+ LOG_FATAL = ::Logger::FATAL
8
+ LOG_ERROR = ::Logger::ERROR
9
+ LOG_WARN = ::Logger::WARN
10
+ LOG_INFO = ::Logger::INFO
11
+ LOG_DEBUG = ::Logger::DEBUG
12
+
13
+ def logger_output_params; [STDOUT] end
14
+ def logger_formatter; SimpleFormatter.new end
15
+ def logger_progname ; "" end
16
+ def log
17
+ @log ||= begin
18
+ logger = ::Logger.new(*logger_output_params)
19
+ logger.formatter = logger_formatter
20
+ logger.progname = logger_progname
21
+ logger.level = LOG_WARN
22
+ logger
23
+ end
24
+ end
25
+ def log_debug!; log.level = LOG_DEBUG end
26
+
27
+ class SimpleFormatter < ::Logger::Formatter
28
+ def call(severity, time, progname, msg)
29
+ format = "%s\n"
30
+ format % [msg2str(msg)]
31
+ end
32
+ end
33
+ class TimeFormatter < ::Logger::Formatter
34
+ def call(severity, time, progname, msg)
35
+ # time PID ThreadID LEVEL progname message
36
+ format = "%s #%d[%x] - %s - %s: %s\n"
37
+ format % ["#{time.strftime('%H:%M:%S.%6N')}",
38
+ $$, Thread.current.object_id, severity[0...1], progname, msg2str(msg)]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ module Nand
4
+ module Plugin
5
+ def plugin_name; self.name end
6
+
7
+ def executor(*argv) ; raise "Not Implemented #{__method__} in #{self}" end
8
+ def self.extended(klass)
9
+ raise "Already Registered Name #{klass.plugin_name}" if extended_class_map.include? klass.plugin_name
10
+ extended_class_map[klass.plugin_name] = klass
11
+ end
12
+ def self.plugin!( name, *argv )
13
+ raise "Unregisterd #{name}" unless extended_class_map.include? name
14
+ extended_class_map[name].executor(*argv)
15
+ end
16
+ private
17
+ def self.extended_class_map
18
+ @@extended_class_map ||= {}
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,47 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ require 'sys/proctable'
4
+
5
+ module Nand
6
+ module ProcOperation
7
+ def pid_filename( name ); ".nand_#{name}.pid" end
8
+ def ps(pid); Sys::ProcTable.ps(pid) end
9
+
10
+ def running_ps?(ps, name = nil)
11
+ ps.cmdline =~ /#{$PROGRAM_NAME}/ and ps.cmdline =~/start/ and ( name.nil? or ps.cmdline.include? name.to_s)
12
+ end
13
+ def running_with?(pid, name)
14
+ proc = ps(pid)
15
+ !proc.nil? and running_ps?(proc, name)
16
+ end
17
+ def pid_from_pidfile(file)
18
+ raise "File Not Found #{file}" unless FileTest.exist? file
19
+ raise "File Not Readable #{file}" unless FileTest.readable? file
20
+ File.open(file, "r"){|fs| fs.read }.strip.to_i
21
+ end
22
+ def name_from_pidfile(file)
23
+ File.basename(file).to_s.scan(/^\.nand_(\S+)\.pid$/).flatten.first
24
+ end
25
+ def running_in(dir, pattern = nil)
26
+ pattern ||= "*"
27
+ Dir.glob(dir.join(pid_filename(pattern))).
28
+ map{|f|[pid_from_pidfile(f), name_from_pidfile(f)]}.
29
+ map{ |pid, name| Daemon.new(dir, name, :uid => ps(pid).euid)}
30
+ end
31
+ def all_runnings
32
+ uid = Process.uid
33
+ runnings = Sys::ProcTable.ps.select{ |ps| running_ps?(ps) and ps.ppid == 1 and (ps.euid == uid or uid == 0)}
34
+ dirs = runnings.map do |run|
35
+ boot_dir = run.environ["PWD"]
36
+ run_dir = run.cmdline.scan(/--run_dir\s+(\S+)/).flatten.first
37
+ Pathname.new(run_dir || boot_dir)
38
+ end.uniq
39
+ daemons = dirs.map{ |d| running_in(d) }.flatten
40
+ unless daemons.size == runnings.size
41
+ unmanageds = runnings.reject{ |ps| daemons.find{|d| d.pid == ps.pid} }.map{ |ps| ps.pid}
42
+ raise "Found Unmanaged Nand Process [#{unmanageds.join(", ")}], Send Signal Yourself"
43
+ end
44
+ daemons
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ module Nand
4
+ module Util
5
+ class ::Object
6
+ def whoami
7
+ self.is_a?(Class) ? self : self.class
8
+ end
9
+ def short_name
10
+ whoami.to_s.split("::").last
11
+ end
12
+ def full_name
13
+ whoami.to_s
14
+ end
15
+ def parent_name
16
+ self.parent_class.to_s
17
+ end
18
+ def parent_class(upper = 1 )
19
+ whoami.to_s.split("::")[0...-1*upper].inject( ::Object ){ |parent, child| parent.const_get( child ) }
20
+ end
21
+ alias :namespace :parent_class
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,5 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ module Nand
4
+ VERSION = "0.1.2"
5
+ end
@@ -0,0 +1,31 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nand/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nand"
8
+ spec.version = Nand::VERSION
9
+ spec.authors = ["nstoym"]
10
+ spec.email = ["nstoym@linkode.co.jp"]
11
+ spec.description = %q{Nand is a simple CLI tool to make anything daemon by Ruby.}
12
+ spec.summary = %q{Nandemo Daemon Tool}
13
+ spec.homepage = "https://github.com/linkodehub/nand"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "pry"
26
+ spec.add_development_dependency "fssm"
27
+
28
+ spec.required_ruby_version = '>=2.0'
29
+ spec.add_dependency "thor"
30
+ spec.add_dependency "sys-proctable"
31
+ end
@@ -0,0 +1,67 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+
3
+ require 'pathname'
4
+ require 'fssm'
5
+ require 'nand/plugin'
6
+
7
+ class SpecMon
8
+ extend Plugin
9
+ def self.executor(*argv)
10
+ top_dir = Pathname.new(argv.shift || Dir.pwd).expand_path
11
+ lib_dir = top_dir.join("lib")
12
+ spec_dir = top_dir.join("spec")
13
+
14
+ [top_dir, lib_dir, spec_dir].each do |dir|
15
+ raise "Not Found Directory #{dir}" unless dir.exist?
16
+ raise "Not Directory #{dir}" unless dir.directory?
17
+ end
18
+
19
+ raise "Not Readable Directory #{lib_dir}" unless lib_dir.readable?
20
+ raise "Not Writable Directory #{spec_dir}" unless spec_dir.writable?
21
+ puts "Monitoring #{lib_dir} and Generate to #{spec_dir}"
22
+ new(lib_dir, spec_dir)
23
+ end
24
+ attr_reader :monitoring_dir, :spec_dir
25
+ def initialize( monitoring_dir, spec_dir )
26
+ @monitoring_dir = Pathname.new(monitoring_dir).expand_path
27
+ @spec_dir = Pathname.new(spec_dir).expand_path
28
+ end
29
+ def monitor
30
+ spec_dir = @spec_dir
31
+ FSSM.monitor(@monitoring_dir, "**/*") do
32
+ create do |dir, file|
33
+ if file =~/\.rb$/
34
+ puts "Created Ruby File #{file} in #{dir}"
35
+ spec = spec_dir.join(file.gsub(/\.rb$/, "_spec.rb"))
36
+ puts "Generate Spec file #{spec}"
37
+ unless spec.dirname.exist?
38
+ puts "Not Found Dist Path #{spec.dirname}, mkpath!"
39
+ FileUtils.mkdir_p(spec.dirname, :mode => 0755)
40
+ end
41
+ unless spec.exist?
42
+ File.open(spec, "w") do |fs|
43
+ code = <<-END_SPEC
44
+ # -*-mode: ruby; coding: utf-8 -*-
45
+ require 'spec_helper'
46
+ require '#{file.gsub(/\.rb$/, "")}'
47
+
48
+ describe "#{file} specification" do
49
+ it "Not Specify"
50
+ end
51
+
52
+ END_SPEC
53
+ fs.puts code
54
+ end
55
+ if spec.exist?
56
+ puts "Success Genrated #{spec}"
57
+ else
58
+ puts "Failed Genrated #{spec}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ alias :exec :monitor
66
+ end
67
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/cli'
4
+
5
+ describe "nand/cli.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/daemon'
4
+
5
+ describe "nand/daemon.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/launcher/executable_file_launcher'
4
+
5
+ describe "nand/launcher/executable_file_launcher.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/launcher/plugin_launcher'
4
+
5
+ describe "nand/launcher/plugin_launcher.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/launcher/rb_file_launcher'
4
+
5
+ describe "nand/launcher/rb_file_launcher.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/launcher/shell_launcher'
4
+
5
+ describe "nand/launcher/shell_launcher.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/launcher'
4
+
5
+ describe "nand/launcher.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,8 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/logger'
4
+
5
+ describe "nand/logger.rb specification" do
6
+ it "Not Specify"
7
+ end
8
+
@@ -0,0 +1,22 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/plugin'
4
+
5
+ describe Nand::Plugin do
6
+ before(:all) do
7
+ class Hoge ; extend Plugin end
8
+ class Fuga ; extend Plugin ; def self.executor(*argv); nil end end
9
+ end
10
+ it { expect(Hoge).to be_respond_to :executor }
11
+ it :plugin_name do
12
+ expect(Hoge.plugin_name).to eq Hoge.to_s
13
+ end
14
+ it "implemented executor" do
15
+ expect(Fuga.executor).to be_nil
16
+ end
17
+ it "not implemented executor" do
18
+ expect{Hoge.executor}.to raise_error RuntimeError
19
+ end
20
+ it "#plugin!" do expect(Plugin.plugin!("Fuga")).to be_nil end
21
+ end
22
+
@@ -0,0 +1,44 @@
1
+ # -*-mode: ruby; coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'nand/proc_operation'
4
+ require 'pathname'
5
+
6
+ include ProcOperation
7
+
8
+ describe "nand/proc_operation.rb specification" do
9
+ before(:all) do
10
+ @work_dir = Pathname.new("/tmp")
11
+ end
12
+ context :pid_filename do
13
+ it { expect(pid_filename("abc")).to eq ".nand_abc.pid" }
14
+ end
15
+ context :name_from_pidfile do
16
+ before(:all){ @pidfile = @work_dir.join(".nand_abc.pid"); system("touch #{@pidfile}")}
17
+ after(:all){ @pidfile.delete if @pidfile.exist? }
18
+ it { expect(name_from_pidfile(@pidfile)).to eq "abc" }
19
+ end
20
+ context :ps do
21
+ let(:proc){ps(Process.pid)}
22
+ it { expect(proc).to_not be_nil }
23
+ it { expect(proc).to be_is_a Struct::ProcTableStruct }
24
+ it { expect(proc.cmdline).to be_include "rspec" }
25
+ end
26
+ context :pid_from_pidfile do
27
+ before(:all) do
28
+ @pidfile = @work_dir.join(".nand_abc.pid")
29
+ @pid = 12345
30
+ File.open(@pidfile, "w"){|fs| fs.puts @pid }
31
+ end
32
+ after(:all){ @pidfile.delete if @pidfile.exist? }
33
+ it { expect(pid_from_pidfile(@pidfile)).to eq @pid}
34
+ end
35
+ context :running_in do
36
+ end
37
+ context :running_ps? do
38
+ end
39
+ context :running_with? do
40
+ end
41
+ context :all_runnings do
42
+ end
43
+ end
44
+