evesync 1.0.7 → 1.0.8

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: acd82817da3db48b0d5f142a1a5764f50617e9e56abd55d9ecb20cd92cca0aaf
4
- data.tar.gz: 3dc586adc166118cda1961d6489914cb2193319642ab6c2fde7897db17f9a0a4
3
+ metadata.gz: 32546fdd2bf3681b09b5d76b29a9afe2559f726f328274b8d0cccbe26a6cbc46
4
+ data.tar.gz: b9e529c9972c92dcc3035ef8028600c6ecfe30004dd704dc01c0f45eb8728cce
5
5
  SHA512:
6
- metadata.gz: 4b6b3ab34063bd3f57ac41b2859983f999bc1cdab2a31b7a7da69b750ab564c8e7637c56250a4eca24aa958ae9b578730d2aa8cbffbb2a2acf6112bbd18cbaae
7
- data.tar.gz: 13897826a7bb2aee1e25f17a811c66483268e6a2f78e68a0797bc07ae74d33420c450a1ceb0134180858a72fcb656cc2b67ccad94b65a7471b11bbb5953f1d07
6
+ metadata.gz: 0d467ccd88887d72c85056c0097838cb9e11416e07d1ab2b3b616642a0984cef50c1a60cf0f3595c9c4e6aa806f4bd48fc2581aea56fe374c6f5a1bb669ceb38
7
+ data.tar.gz: 836b9e00da567d26161402c41146b37166d60627e536b0e4a26c6e154ee54fc21f2ed439ee09b0e9280b25be7663119229eab09eb98a2e2ef2e9c18ead41e8b8
data/Rakefile CHANGED
@@ -56,17 +56,20 @@ end
56
56
 
57
57
  ## Docker related targets
58
58
 
59
- task :docker do
60
- sh 'docker-compose build'
61
- end
59
+ namespace :docker do
62
60
 
63
- task :up do
64
- sh 'docker-compose up -d'
65
- end
61
+ task :build do
62
+ sh 'docker-compose build'
63
+ end
66
64
 
67
- task :down do
68
- sh 'docker-compose stop || docker-compose kill ||:'
69
- sh 'docker-compose rm --force ||:'
65
+ task :up do
66
+ sh 'docker-compose up -d node-1 node-2'
67
+ end
68
+
69
+ task :down do
70
+ sh 'docker-compose stop || docker-compose kill ||:'
71
+ sh 'docker-compose rm --force ||:'
72
+ end
70
73
  end
71
74
 
72
75
  namespace :rhel do
@@ -1,34 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require 'evesync/ipc/server'
5
- require 'evesync/log'
4
+ require 'evesync/service'
6
5
  require 'evesync/database'
7
6
 
8
- Evesync::Log.info('Database daemon starting...')
9
-
10
- database = Evesync::Database.new
11
-
12
- # Creating an IPC server to accept data
13
- data_server = Evesync::IPC::Server.new(
14
- port: :evedatad,
15
- proxy: database
16
- ).start
17
-
18
- Signal.trap('TERM') do
19
- data_server.stop
20
- exit(0)
7
+ evedata = Evesync::Service.new(:evedatad) do |config|
8
+ config.proxy = Evesync::Database.new
9
+ # zip db if needed, save backup
21
10
  end
22
11
 
23
- Evesync::Log.info('Database daemon started!')
24
-
25
- begin
26
- loop do
27
- sleep 3600 # FIXME: 1 hour, rewrite better
28
- # zip db if needed, save backup
29
- end
30
- rescue SignalException => e
31
- data_server.stop
32
- Evesync::Log.warn("Database daemon received signal: #{e.signm}(#{e.signo})")
33
- exit(0)
34
- end
12
+ evedata.start
@@ -1,36 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require 'evesync/log'
5
- require 'evesync/ipc/server'
4
+ require 'evesync/service'
6
5
  require 'evesync/handler'
7
6
 
8
- Evesync::Log.info('Handle daemon starting...')
9
-
10
- all_handler = Evesync::Handler.new
11
-
12
- handler_server = Evesync::IPC::Server.new(
13
- port: :evehand,
14
- proxy: all_handler,
15
- ip: '*' # accept remote requests
16
- ).start
17
-
18
- Evesync::Log.info('Handle daemon started!')
19
-
20
- # Signal handling
21
-
22
- Signal.trap('TERM') do
23
- handler_server.stop
24
- exit(0)
7
+ evehand = Evesync::Service.new(:evehand) do |config|
8
+ config.proxy = Evesync::Handler.new
9
+ config.ip = '*'
25
10
  end
26
11
 
27
- begin
28
- loop do
29
- sleep 3600 # FIXME: 1 hour, rewrite better
30
- # FIXME: know what to do
31
- end
32
- rescue SignalException => e
33
- handler_server.stop
34
- Evesync::Log.warn("Received signal: #{e.signm}(#{e.signo})")
35
- exit(0)
36
- end
12
+ evehand.start
@@ -1,42 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require 'evesync/log'
5
- require 'evesync/ipc/server'
4
+ require 'evesync/service'
6
5
  require 'evesync/trigger'
7
6
  require 'evesync/watcher'
8
7
 
9
- Evesync::Log.info 'Monitoring daemon starting...'
10
-
11
8
  queue = Queue.new
12
9
  trigger = Evesync::Trigger.new(queue)
13
10
  watcher = Evesync::Watcher.new(queue)
14
11
 
15
- [trigger, watcher].each(&:start)
16
-
17
- evesync_server = Evesync::IPC::Server.new(
18
- port: :evemond,
19
- proxy: trigger
20
- ).start
21
-
22
- Evesync::Log.info 'Monitoring daemon started!'
23
-
24
- # Signal handling
25
-
26
- Signal.trap('SIGINT') do
27
- [watcher, trigger].each(&:stop)
28
- evesync_server.stop
29
- exit(0)
12
+ evemon = Evesync::Service.new(:evemond) do |config|
13
+ config.proxy = trigger
14
+ config.at_start = lambda { [trigger, watcher].each(&:start) }
15
+ config.at_exit = lambda { [trigger, watcher].each(&:stop) }
30
16
  end
31
17
 
32
- begin
33
- loop do
34
- sleep 100
35
- # FIXME: know what to do
36
- end
37
- rescue SignalException => e
38
- watcher.stop
39
- evesync_server.stop
40
- Evesync::Log.warn("Monitoring daemon received signal: #{e.signm}(#{e.signo})")
41
- exit(0)
42
- end
18
+ evemon.start
@@ -14,28 +14,24 @@ require 'evesync/config'
14
14
  require 'evesync/sync'
15
15
  require 'evesync/ipc/client'
16
16
 
17
- DB_TAG = 'db'
18
- FILES_TAG = 'files'
17
+ DB_TAG = 'db'.freeze
18
+ FILES_TAG = 'files'.freeze
19
19
 
20
- opts = {}
20
+ opts = {}
21
21
  $program = File.basename($0)
22
- #Evesync::Log.level = :warn
23
- Evesync::Log.simple = true
24
22
 
25
23
  def kill_by_pid(daemon)
26
24
  begin
27
- Process.kill('TERM', File.read("/var/run/evesync/#{daemon}.pid").to_i)
28
- rescue Errno::ESRCH
25
+ puts "Killing #{daemon}..."
26
+ pid = File.read("/var/run/evesync/#{daemon}.pid").to_i
27
+ Process.kill('INT', pid)
28
+ rescue Errno::ESRCH, Errno::ENOENT
29
29
  puts("#{daemon} already killed")
30
30
  end
31
31
  end
32
32
 
33
33
  def spawn_daemon(daemon)
34
- pid = spawn(daemon,
35
- :err => "/var/log/evesync/#{daemon}.log",
36
- :out=>:err)
37
- File.write("/var/run/evesync/#{daemon}.pid", pid)
38
- pid
34
+ system(daemon)
39
35
  end
40
36
 
41
37
  OptionParser.new do |parser|
@@ -84,8 +80,6 @@ OptionParser.new do |parser|
84
80
 
85
81
  # TODO: refactor
86
82
  parser.on('-r', '--run', 'Start daemons') do
87
- FileUtils.mkdir_p '/var/log/evesync'
88
- FileUtils.mkdir_p '/var/run/evesync'
89
83
  spawn_daemon 'evedatad'
90
84
  spawn_daemon 'evemond'
91
85
  spawn_daemon 'evehand'
@@ -99,10 +93,8 @@ OptionParser.new do |parser|
99
93
  kill_by_pid 'evesyncd'
100
94
  kill_by_pid 'evedatad'
101
95
  rescue StandardError => e
102
- puts("Error: #{e}")
103
- puts('Something went wrong while killing processes. Do it yourself!')
104
- ensure
105
- FileUtils.rm_f Dir.glob('/var/run/evesync/*.pid')
96
+ puts("(!) Error: #{e} (!)")
97
+ puts('Something went wrong while killing processes...')
106
98
  end
107
99
  end
108
100
  end.parse!
@@ -1,44 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- mode: ruby -*-
3
3
 
4
- require 'evesync/log'
4
+ require 'evesync/service'
5
5
  require 'evesync/sync'
6
- require 'evesync/config'
7
- require 'evesync/ipc/server'
8
-
9
- # Exiting if `auto_discover = false'
10
- unless Evesync::Config['auto_discover']
11
- Evesync::Log.info('Synchronizing not required')
12
- exit(0)
13
- end
14
-
15
- Evesync::Log.info('Synchronizing daemon starting...')
16
6
 
17
7
  sync = Evesync::Sync.new
18
8
 
19
- sync_server = Evesync::IPC::Server.new(
20
- port: :evesyncd,
21
- proxy: sync
22
- ).start
23
-
24
- Signal.trap('TERM') do
25
- sync_server.stop
26
- exit(0)
9
+ evesync = Evesync::Service.new(:evesyncd) do |config|
10
+ config.proxy = sync
11
+ config.at_start = sync.method(:discover)
12
+ config.interval = 15 # TODO: read from config
27
13
  end
28
14
 
29
- Evesync::Log.info('Synchronizing daemon started!')
30
-
31
- few_seconds = Evesync::Config['discover_timeout'].to_i
32
-
33
- sync.discover # discovering nodes
34
- sleep 3 # giving 3 seconds to complete
35
-
36
- begin
37
- loop do
38
- sync.synchronize # updating this node
39
- sleep few_seconds # ... in some interval
40
- end
41
- rescue SignalException
42
- sync_server.stop
43
- exit(0)
15
+ evesync.start do
16
+ sync.synchronize
44
17
  end
data/bin/start CHANGED
@@ -1,16 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- trap 'echo Exiting; \
3
- pkill evedatad; \
4
- pkill evemond; \
5
- pkill evehand; \
6
- pkill evesyncd
7
- exit 1' 2
8
- evedatad &
9
- evemond &
10
- evehand &
11
- sleep 3s
12
- evesyncd &
13
-
14
- while true; do
15
- sleep 1000
16
- done
2
+ evedatad
3
+ evemond
4
+ evehand
5
+ evesyncd
@@ -1,3 +1,3 @@
1
1
  module Evesync
2
- VERSION = '1.0.7'.freeze
2
+ VERSION = '1.0.8'.freeze
3
3
  end
@@ -9,7 +9,7 @@ module Evesync
9
9
  DB_PATH = '/var/lib/evesync/db/'.freeze
10
10
  DB_FILES_PATH = '/var/lib/evesync/files/'.freeze
11
11
 
12
- DEFAULT_LOGLEVEL = 'info'.freeze
12
+ DEFAULT_LOGLEVEL = :info
13
13
 
14
14
  DISCOVER_TIMEOUT = 3600
15
15
  WATCH_INTERVAL = 2
@@ -1,6 +1,9 @@
1
1
  require 'evesync/ipc/client'
2
2
  require 'evesync/log'
3
3
  require 'evesync/config'
4
+ require 'evesync/os'
5
+
6
+ require 'json'
4
7
 
5
8
  module Evesync
6
9
 
@@ -35,14 +38,14 @@ module Evesync
35
38
  # Sending UDP message on broadcast
36
39
  # Discovering our nodes
37
40
 
38
- def send_discovery_message(ip = '<broadcast>', message = DISCOVERY_REQ)
41
+ def send_discovery_message(ip='<broadcast>', message=DISCOVERY_REQ)
39
42
  udp_sock = UDPSocket.new
40
- if ip == '<broadcast>'
43
+ if is_broadcast(ip)
41
44
  udp_sock.setsockopt(
42
45
  Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
43
46
  )
44
47
  end
45
- udp_sock.send(message, 0, ip, @port)
48
+ udp_sock.send(to_discover_msg(message: message), 0, ip, @port)
46
49
  udp_sock.close
47
50
  end
48
51
 
@@ -54,17 +57,18 @@ module Evesync
54
57
 
55
58
  def listen_discovery
56
59
  loop do
57
- data, recvdata = @listen_sock.recvfrom(1024)
60
+ datajson, recvdata = @listen_sock.recvfrom(1024)
58
61
  node_ip = recvdata[-1]
59
62
 
60
63
  next if Utils.local_ip?(node_ip)
61
64
 
62
- if [DISCOVERY_REQ, DISCOVERY_ANS].include? data
65
+ data = from_discover_msg(datajson)
66
+ if fine_node?(data)
63
67
  # Push new node_ip to trigger
64
68
  @evesync.add_remote_node(node_ip)
65
69
  end
66
70
 
67
- case data
71
+ case data['message']
68
72
  when DISCOVERY_REQ
69
73
  Log.info("Discover host request got: #{node_ip}")
70
74
  send_discovery_message(node_ip, DISCOVERY_ANS)
@@ -73,5 +77,29 @@ module Evesync
73
77
  end
74
78
  end
75
79
  end
80
+
81
+ def to_discover_msg(msg)
82
+ {
83
+ evesync: {
84
+ message: msg,
85
+ os: EVESYNC_OS,
86
+ }
87
+ }.to_json.freeze
88
+ end
89
+
90
+ def from_discover_msg(data)
91
+ JSON.parse(data)['evesync'] # TODO: catch error
92
+ end
93
+
94
+ def is_broadcast(ip)
95
+ ip == '<broadcast>'
96
+ end
97
+
98
+ def fine_node?(data)
99
+ [
100
+ [DISCOVERY_ANS, DISCOVERY_REQ].include?(data['message']),
101
+ data['os'] == EVESYNC_OS,
102
+ ].all?
103
+ end
76
104
  end
77
105
  end
@@ -48,7 +48,7 @@ module Evesync
48
48
  end
49
49
 
50
50
  def stop
51
- DRb.thread.exit
51
+ DRb.stop_service
52
52
  self
53
53
  end
54
54
  end
@@ -1,62 +1,105 @@
1
- require 'logger'
2
1
  require 'evesync/config'
3
2
  require 'evesync/constants'
4
3
 
4
+ require 'syslog'
5
+ require 'logger'
6
+ require 'fileutils'
5
7
 
6
8
  module Evesync
7
9
 
8
- # This module is responsible for logging
10
+ # Logging via syslog
9
11
  module Log
10
12
  # Supported levels for logging
11
- LEVELS = %i[debug info warn error fatal].freeze
13
+ LEVELS = %i[debug info notice warn error fatal]
14
+ def LEVELS.less(a, b)
15
+ (self.index(a) <=> self.index(b) or -1) >= 0
16
+ end
17
+
18
+ # Default engine for logging, one of (:io, :syslog)
19
+ DEFAULT_ENGINE = :io
20
+
21
+ # Log level mapping for syslog
22
+ SYSLOG = {
23
+ :debug => Syslog::LOG_DEBUG,
24
+ :info => Syslog::LOG_INFO,
25
+ :notice => Syslog::LOG_NOTICE,
26
+ :warn => Syslog::LOG_WARNING,
27
+ :error => Syslog::LOG_ERR,
28
+ :fatal => Syslog::LOG_CRIT,
29
+ #:alert => Syslog::LOG_ALERT,
30
+ #:emerg => Syslog::LOG_EMERG,
31
+ }
32
+
33
+ SYSLOG_OPTIONS = [
34
+ Syslog::LOG_PID,
35
+ Syslog::LOG_NOWAIT,
36
+ Syslog::LOG_CONS,
37
+ Syslog::LOG_PERROR,
38
+ ].inject(&:|)
39
+
40
+ SYSLOG_FACILITY = [
41
+ Syslog::LOG_DAEMON,
42
+ Syslog::LOG_LOCAL5,
43
+ ].inject(&:|)
12
44
 
45
+ # Public methods available via Log.method
13
46
  class << self
14
- def method_missing(m, *args)
15
- # Unlisted methods are not allowed
16
- raise NoMethodError unless LEVELS.include?(m)
17
47
 
18
- check_logger
19
- @logger.send(m, to_string(*args))
20
- nil
21
- end
48
+ LEVELS.each do |level|
49
+ define_method(level) do |*args|
50
+ check_logger
51
+ return unless LEVELS.less(level.to_sym, @level.to_sym)
22
52
 
23
- def check_logger
24
- init_logger unless @logger
53
+ case @engine
54
+ when :syslog
55
+ @logger.log(SYSLOG[level], to_string(*args))
56
+ when :io
57
+ @logger.send(level, to_string(*args))
58
+ end
59
+
60
+ nil # prevent from being able to access the object
61
+ end
25
62
  end
26
63
 
27
64
  def level=(lvl)
28
65
  check_logger
29
- if lvl.is_a?(Symbol) or lvl.is_a?(String)
30
- @logger.level =
31
- begin
32
- Logger.const_get(lvl.to_s.upcase)
33
- rescue NameError
34
- Logger::DEBUG
35
- end
36
- end
66
+ raise "Unknown level #{lvl}" unless LEVELS.include? lvl
67
+ @level = lvl
37
68
  end
38
69
 
39
70
  def level
40
- check_logger
41
- @logger.level
71
+ @level
42
72
  end
43
73
 
44
- def simple=(bool)
74
+ def check_logger
45
75
  init_logger unless @logger
46
- if bool
47
- @logger.formatter = proc do |_sev, _dt, _prog, msg|
48
- "#{msg}\n"
49
- end
50
- end
51
76
  end
52
77
 
78
+ def engine=(engine)
79
+ raise UnsupportedLogEngine.new(engine) \
80
+ unless [:syslog, :io].member? engine
81
+ @engine = engine
82
+ end
83
+
84
+ # Using syslog implementation
53
85
  def init_logger
54
- @logger = Logger.new(STDERR)
55
- @logger.formatter = proc do |sev, dtime, _prog, msg|
56
- time = dtime.strftime('%Y-%m-%d %H:%M:%S')
57
- prog = File.basename($PROGRAM_NAME)
58
- "[#{time}] #{prog.ljust(8)} #{sev.ljust(5)}: #{msg}\n"
86
+ @engine ||= DEFAULT_ENGINE
87
+ prog = File.basename($PROGRAM_NAME)
88
+
89
+ case @engine
90
+ when :syslog
91
+ @logger = Syslog.open(prog, SYSLOG_OPTIONS, SYSLOG_FACILITY)
92
+
93
+ when :io
94
+ FileUtils.mkdir_p '/var/log/evesync/'
95
+ @logger = Logger.new("/var/log/evesync/#{prog}.log")
96
+ @logger.formatter = proc do |sev, dtime, _prog, msg|
97
+ time = dtime.strftime('%Y-%m-%d %H:%M:%S')
98
+ "[#{time}] #{prog.ljust(8)} #{sev.ljust(5)}: #{msg}\n"
99
+ end
59
100
  end
101
+
102
+ @level = Config[:loglevel] || :info
60
103
  end
61
104
 
62
105
  def to_string(*args)
@@ -2,8 +2,13 @@ text = File.new('/etc/os-release').read
2
2
 
3
3
  if text =~ /^ID.*(rhel|centos|fedora)/
4
4
  require 'evesync/os/linux/rhel'
5
+ EVESYNC_OS = 'rhel'.freeze
5
6
  elsif text =~ /ID.*arch/
6
7
  require 'evesync/os/linux/arch'
8
+ EVESYNC_OS = 'arch'.freeze
7
9
  elsif text =~ /ID.*debian/
8
10
  require 'evesync/os/linux/deb'
11
+ EVESYNC_OS = 'deb'.freeze
12
+ else
13
+ EVESYNC_OS = 'not implemented'
9
14
  end
@@ -0,0 +1,104 @@
1
+ require 'evesync/log'
2
+ require 'evesync/ipc/server'
3
+
4
+ require 'fileutils'
5
+
6
+ module Evesync
7
+ # Class for creating daemons which are DRb servers with
8
+ # proxy object to accept requests.
9
+ #
10
+ # Traps signals to exit.
11
+ #
12
+ # Example:
13
+ # d = Evesync::Service.new(:mydaemond) do |config|
14
+ # config.proxy = SomeProxyObject.new
15
+ # end
16
+ #
17
+ # d.start
18
+ #
19
+ class Service
20
+ def initialize(name)
21
+ @factory = ServiceFactory.new
22
+ @factory.name = name unless name.nil?
23
+ yield @factory
24
+ end
25
+
26
+ def start
27
+ daemonize
28
+
29
+ Log.info("#{@factory.name} daemon starting...")
30
+
31
+ @ipc_server = IPC::Server.new(
32
+ port: @factory.port,
33
+ proxy: @factory.proxy,
34
+ ip: @factory.ip,
35
+ ).start
36
+
37
+ Signal.trap('TERM') do
38
+ @ipc_server.stop
39
+ exit 0
40
+ end
41
+
42
+ Log.info("#{@factory.name} daemon started!")
43
+
44
+ @factory.at_start.call if @factory.at_start.respond_to? :call
45
+
46
+ loop do
47
+ sleep @factory.interval
48
+ yield if block_given?
49
+ end
50
+
51
+ rescue SignalException => e
52
+ Log.warn("#{@factory.name} daemon received signal: " \
53
+ "#{e.signm}(#{e.signo})")
54
+ exit 0
55
+
56
+ # rubocop:disable Lint/RescueException
57
+ rescue Exception => crit
58
+ Log.fatal(crit)
59
+ crit.backtrace.each { |line| Log.fatal(line) }
60
+ exit 1
61
+
62
+ # rubocop:enable Lint/RescueException
63
+ ensure
64
+ @factory.at_exit.call if @factory.at_exit.respond_to? :call
65
+ @ipc_server.stop
66
+ end
67
+
68
+ def daemonize
69
+ Process.daemon
70
+ Process.setproctitle(@factory.name.to_s) \
71
+ if Process.respond_to? :setproctitle
72
+ $0 = @factory.name.to_s
73
+ FileUtils.mkdir_p @factory.pids
74
+ File.open("#{@factory.pids}/#{@factory.name}.pid", 'w') do |f|
75
+ f.puts(Process.pid)
76
+ end
77
+ end
78
+ end
79
+
80
+ class ServiceFactory
81
+ attr_accessor :name, :proxy, :at_start, :at_exit
82
+ attr_writer :interval, :port, :ip, :logs, :pids
83
+
84
+ def logs
85
+ @logs || '/var/log/evesync/'
86
+ end
87
+
88
+ def pids
89
+ @pids || '/var/run/evesync/'
90
+ end
91
+
92
+ def port
93
+ @port || name.to_sym
94
+ end
95
+
96
+ def ip
97
+ @ip || 'localhost'
98
+ end
99
+
100
+ def interval
101
+ @interval || 3600
102
+ end
103
+ end
104
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evesync
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kiselev Valentine
@@ -163,6 +163,7 @@ files:
163
163
  - lib/evesync/os/linux/rhel/package_manager.rb
164
164
  - lib/evesync/os/linux/rhel/package_watcher.rb
165
165
  - lib/evesync/os/linux/rhel/rpm.rb
166
+ - lib/evesync/service.rb
166
167
  - lib/evesync/sync.rb
167
168
  - lib/evesync/trigger.rb
168
169
  - lib/evesync/trigger/base.rb