miga-base 0.6.3.2 → 0.6.4.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
  SHA256:
3
- metadata.gz: e975745242a5e71b84fbc5fdea4610eb58c67f43fa08b59344b44dfd83ac5791
4
- data.tar.gz: c57a5780ac419c290448d06fda58ba16dc953d7a6be96d3612407bafdfedef3c
3
+ metadata.gz: 56e28b1948d83d30fcfbb1bb1925875996d4c23dd6f707ae1f11198ae4fe598f
4
+ data.tar.gz: d1285c61c990a597f9ed65ba298467c7ea4aac49441af91e2711a5590fa8f05b
5
5
  SHA512:
6
- metadata.gz: 1860bbd81459d9adb4022b60efa380ca6ce6894418e27cbe961f3e85b63b89026c8ee278aaee1f440bf625e869c81862bf0ab55f60b43cf7e3866bb7a211198b
7
- data.tar.gz: 79f463ecd256b866a3a52e3f4adb5102fbef2b130131b9febe4b7d81372e00df00b6adb90a9b57ced423087fa0c6fa86dee853bff1041821e42569d8795b79b5
6
+ metadata.gz: '021513891f1a58f8d518a77691aa89da856741bd7627454a2763b9c825b354ba14f712b7ff9a37b584629bb17ce04eec8a147c5ea2d4511a6a62d556b2a567fc'
7
+ data.tar.gz: 62898b0497e07db953534a24b40f9df1b76ba2304676a2c4763599be0ae8d03c7fa36e1a7d580745f04074c27d53f808dd1e8842f9bccda9641b01c76fda383b
data/lib/miga/cli.rb CHANGED
@@ -106,9 +106,7 @@ class MiGA::Cli < MiGA::MiGA
106
106
  # otherwise it's sent to +$stderr+
107
107
  def say(*par)
108
108
  return unless self[:verbose]
109
- par.map! { |i| "[#{Time.now}] #{i}" }
110
- io = par.first.is_a?(IO) ? par.shift : $stderr
111
- io.puts(*par)
109
+ super(*par)
112
110
  end
113
111
 
114
112
  ##
@@ -0,0 +1,81 @@
1
+ # @package MiGA
2
+ # @license Artistic-2.0
3
+
4
+ require 'miga/cli/action'
5
+ require 'miga/lair'
6
+
7
+ class MiGA::Cli::Action::Lair < MiGA::Cli::Action
8
+
9
+ def parse_cli
10
+ cli.defaults = { daemon_opts: [] }
11
+ cli.expect_operation = true
12
+ cli.parse do |opt|
13
+ opt.separator 'Available operations:'
14
+ { start: 'Start an instance of the application.',
15
+ stop: 'Start an instance of the application.',
16
+ restart: 'Stop all instances and restart them afterwards.',
17
+ reload: 'Send a SIGHUP to all instances of the application.',
18
+ run: 'Start the application and stay on top.',
19
+ zap: 'Set the application to a stopped state.',
20
+ status: 'Show status (PID) of application instances.'
21
+ }.each { |k,v| opt.separator sprintf ' %*s%s', -33, k, v }
22
+ opt.separator ''
23
+
24
+ opt.separator 'MiGA options:'
25
+ opt.on(
26
+ '-p', '--path PATH',
27
+ '(Mandatory) Path to the directory where the MiGA projects are located'
28
+ ) { |v| cli[:path] = v }
29
+ opt.on(
30
+ '--latency INT', Integer,
31
+ 'Time to wait between iterations in seconds, by default: 120'
32
+ ) { |v| cli[:latency] = v }
33
+ opt.on(
34
+ '--wait-for INT', Integer,
35
+ 'Time to wait for a daemon to report being alive in seconds',
36
+ 'by default: 900'
37
+ ) { |v| cli[:wait_for] = v }
38
+ opt.on(
39
+ '--keep-inactive',
40
+ 'If set, daemons are kept alive even when inactive;',
41
+ 'i.e., when all tasks are complete'
42
+ ) { |v| cli[:keep_inactive] = v }
43
+ opt.on(
44
+ '--name STRING',
45
+ 'A name for the chief daemon process'
46
+ ) { |v| cli[:name] = v }
47
+ opt.on(
48
+ '--json PATH',
49
+ 'Path to a custom daemon definition in json format'
50
+ ) { |v| cli[:json] = v }
51
+ cli.opt_common(opt)
52
+
53
+ opt.separator 'Daemon options:'
54
+ opt.on(
55
+ '-t', '--ontop',
56
+ 'Stay on top (does not daemonize)'
57
+ ) { cli[:daemon_opts] << '-t' }
58
+ opt.on(
59
+ '-f', '--force',
60
+ 'Force operation'
61
+ ) { cli[:daemon_opts] << '-f' }
62
+ opt.on(
63
+ '-n', '--no_wait',
64
+ 'Do not wait for processes to stop'
65
+ ) { cli[:daemon_opts] << '-n' }
66
+ opt.on(
67
+ '--shush',
68
+ 'Silence the daemon'
69
+ ) { cli[:daemon_opts] << '--shush' }
70
+ opt.separator ''
71
+ end
72
+ end
73
+
74
+ def perform
75
+ cli.ensure_par(path: '-p')
76
+ k_opts = %i[latency wait_for keep_inactive name json]
77
+ opts = Hash[k_opts.map { |k| [k, cli[k]] }]
78
+ lair = MiGA::Lair.new(cli[:path], opts)
79
+ lair.daemon(cli.operation, cli[:daemon_opts])
80
+ end
81
+ end
data/lib/miga/cli/base.rb CHANGED
@@ -37,6 +37,7 @@ module MiGA::Cli::Base
37
37
  # System
38
38
  init: 'Initialize MiGA to process new projects',
39
39
  daemon: 'Controls the daemon of a MiGA project',
40
+ lair: 'Controls groups of daemons for several MiGA projects',
40
41
  date: 'Returns the current date in standard MiGA format',
41
42
  console: 'Opens an IRB console with MiGA',
42
43
  # Taxonomy
data/lib/miga/common.rb CHANGED
@@ -36,5 +36,15 @@ class MiGA::MiGA
36
36
  end
37
37
  end
38
38
 
39
+ ##
40
+ # Print +par+ ensuring new line at the end.
41
+ # Date/time-stamp each line.
42
+ # If the first parameter is +IO+, the output is sent there,
43
+ # otherwise it's sent to +$stderr+
44
+ def say(*par)
45
+ io = par.first.is_a?(IO) ? par.shift : $stderr
46
+ io.puts(*par.map { |i| "[#{Time.now}] #{i}" })
47
+ end
48
+
39
49
  end
40
50
 
data/lib/miga/lair.rb ADDED
@@ -0,0 +1,153 @@
1
+ # @package MiGA
2
+ # @license Artistic-2.0
3
+
4
+ require 'daemons'
5
+ require 'miga/daemon'
6
+
7
+ ##
8
+ # Lair of MiGA Daemons handling job submissions
9
+ class MiGA::Lair < MiGA::MiGA
10
+ ##
11
+ # When was the last time the chief daemon in this lair was seen active?
12
+ # Returns Time
13
+ def self.last_alive(path)
14
+ f = File.expand_path('.lair-alive', path)
15
+ return nil unless File.exist? f
16
+ Time.parse(File.read(f))
17
+ end
18
+
19
+ # Absolute path to the directory where the projects are located
20
+ attr_reader :path
21
+
22
+ # Options used to setup the chief daemon
23
+ attr_accessor :options
24
+
25
+ # Integer indicating the current iteration
26
+ attr_reader :loop_i
27
+
28
+ ##
29
+ # Initialize an inactive daemon for the directory at +path+. See #daemon
30
+ # to wake the chief daemon. Supported options include:
31
+ # - json: json definition for all children daemons, by default: nil
32
+ # - latency: time to wait between iterations in seconds, by default: 120
33
+ # - wait_for: time to wait for a daemon to report being alive in seconds,
34
+ # by default: 900
35
+ # - keep_inactive: boolean indicating if daemons should stay alive even when
36
+ # inactive (when all tasks are complete), by default: false
37
+ # - name: A name for the chief daemon process, by default: basename of +path+
38
+ def initialize(path, opts = {})
39
+ @path = File.expand_path(path)
40
+ @options = opts
41
+ @loop_i = -1
42
+ {
43
+ json: nil,
44
+ latency: 120,
45
+ wait_for: 900,
46
+ keep_inactive: false,
47
+ name: File.basename(@path)
48
+ }.each { |k, v| @options[k] = v if @options[k].nil? }
49
+ end
50
+
51
+ ##
52
+ # When was the last time the chief daemon for was seen active here?
53
+ # Returns Time.
54
+ def last_alive
55
+ MiGA::Lair.last_alive path
56
+ end
57
+
58
+ ##
59
+ # Returns Hash containing the default options for the daemon.
60
+ def default_options
61
+ { dir_mode: :normal, dir: path, multiple: false, log_output: true }
62
+ end
63
+
64
+ ##
65
+ # Launches the +task+ with options +opts+ (as command-line arguments) and
66
+ # returns the process ID as an Integer. If +wait+ it waits for the process to
67
+ # complete, immediately returns otherwise.
68
+ # Supported tasks: start, stop, restart, status.
69
+ def daemon(task, opts = [], wait = true)
70
+ MiGA.DEBUG "Lair.daemon #{task} #{opts}"
71
+ config = default_options
72
+ opts.unshift(task.to_s)
73
+ config[:ARGV] = opts
74
+ # This additional degree of separation below was introduced so the Daemons
75
+ # package doesn't kill the parent process in workflows.
76
+ pid = fork do
77
+ Daemons.run_proc("MiGA:#{options[:name]}", config) { while in_loop; end }
78
+ end
79
+ Process.wait(pid) if wait
80
+ pid
81
+ end
82
+
83
+ ##
84
+ # Tell the world that you're alive.
85
+ def declare_alive
86
+ File.open(File.join(path, '.lair-alive'), 'w') do |fh|
87
+ fh.print Time.now.to_s
88
+ end
89
+ end
90
+
91
+ ##
92
+ # Perform block for each project in the +dir+ directory,
93
+ # passing the absolute path of the project to the block.
94
+ # Searches for MiGA projects recursively in all
95
+ # subdirectories that are not MiGA projects.
96
+ def each_project(dir = path)
97
+ Dir.entries(dir) do |f|
98
+ f = File.join(dir, f)
99
+ if MiGA::Project.exists? f
100
+ yield(f)
101
+ elsif Dir.exists? f
102
+ each_project(f) { |p| yield(p) }
103
+ end
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Traverse directories checking MiGA projects
109
+ def check_directories
110
+ each_project do |dir|
111
+ alive = MiGA::Daemon.last_alive(dir)
112
+ next if !alive.nil? && alive > Time.now - options[:wait_for]
113
+ launch_daemon(dir)
114
+ end
115
+ end
116
+
117
+ ##
118
+ # Launch daemon for the project stored in +dir+
119
+ def launch_daemon(dir)
120
+ project = MiGA::Project.load(dir)
121
+ raise "Cannot load project: #{dir}" if project.nil?
122
+ d = MiGA::Daemon.new(project, options[:json])
123
+ d.runopts(:shutdown_when_done, true) unless options[:keep_inactive]
124
+ say "Launching daemon: #{dir}"
125
+ d.daemon(:start, [], false)
126
+ end
127
+
128
+ ##
129
+ # Run one loop step. Returns a Boolean indicating if the loop should continue.
130
+ def in_loop
131
+ declare_alive
132
+ if loop_i == -1
133
+ say '-----------------------------------'
134
+ say 'MiGA:%s launched' % options[:name]
135
+ say '-----------------------------------'
136
+ say 'Configuration options:'
137
+ say options.to_s
138
+ @loop_i = 0
139
+ end
140
+ @loop_i += 1
141
+ check_directories
142
+ sleep(options[:latency])
143
+ true
144
+ end
145
+
146
+ ##
147
+ # Terminates a chief daemon
148
+ def terminate
149
+ say 'Terminating chief daemon...'
150
+ f = File.expand_path('.lair-alive', project.path)
151
+ File.unlink(f) if File.exist? f
152
+ end
153
+ end
data/lib/miga/version.rb CHANGED
@@ -10,7 +10,7 @@ module MiGA
10
10
  # - Float representing the major.minor version.
11
11
  # - Integer representing gem releases of the current version.
12
12
  # - Integer representing minor changes that require new version number.
13
- VERSION = [0.6, 3, 2]
13
+ VERSION = [0.6, 4, 0]
14
14
 
15
15
  ##
16
16
  # Nickname for the current major.minor version.
@@ -18,7 +18,7 @@ module MiGA
18
18
 
19
19
  ##
20
20
  # Date of the current gem release.
21
- VERSION_DATE = Date.new(2020, 4, 13)
21
+ VERSION_DATE = Date.new(2020, 4, 15)
22
22
 
23
23
  ##
24
24
  # Reference of MiGA.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miga-base
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3.2
4
+ version: 0.6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luis M. Rodriguez-R
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-13 00:00:00.000000000 Z
11
+ date: 2020-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: daemons
@@ -133,6 +133,7 @@ files:
133
133
  - lib/miga/cli/action/index_wf.rb
134
134
  - lib/miga/cli/action/init.rb
135
135
  - lib/miga/cli/action/init/daemon_helper.rb
136
+ - lib/miga/cli/action/lair.rb
136
137
  - lib/miga/cli/action/ln.rb
137
138
  - lib/miga/cli/action/ls.rb
138
139
  - lib/miga/cli/action/ncbi_get.rb
@@ -165,6 +166,7 @@ files:
165
166
  - lib/miga/dataset/hooks.rb
166
167
  - lib/miga/dataset/result.rb
167
168
  - lib/miga/json.rb
169
+ - lib/miga/lair.rb
168
170
  - lib/miga/metadata.rb
169
171
  - lib/miga/project.rb
170
172
  - lib/miga/project/base.rb