miga-base 0.6.3.2 → 0.6.4.0

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