evesync 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +25 -0
  3. data/Rakefile +83 -0
  4. data/bin/evedatad +34 -0
  5. data/bin/evehand +36 -0
  6. data/bin/evemond +42 -0
  7. data/bin/evesync +164 -0
  8. data/bin/evesyncd +44 -0
  9. data/bin/start +16 -0
  10. data/config/example.conf +58 -0
  11. data/lib/evesync/config.rb +63 -0
  12. data/lib/evesync/constants.rb +17 -0
  13. data/lib/evesync/database.rb +107 -0
  14. data/lib/evesync/discover.rb +77 -0
  15. data/lib/evesync/err.rb +25 -0
  16. data/lib/evesync/handler/file.rb +28 -0
  17. data/lib/evesync/handler/package.rb +30 -0
  18. data/lib/evesync/handler.rb +89 -0
  19. data/lib/evesync/ipc/client.rb +37 -0
  20. data/lib/evesync/ipc/data/file.rb +64 -0
  21. data/lib/evesync/ipc/data/hashable.rb +74 -0
  22. data/lib/evesync/ipc/data/ignore.rb +22 -0
  23. data/lib/evesync/ipc/data/package.rb +58 -0
  24. data/lib/evesync/ipc/data/utils.rb +14 -0
  25. data/lib/evesync/ipc/data.rb +50 -0
  26. data/lib/evesync/ipc/ipc.rb +42 -0
  27. data/lib/evesync/ipc/server.rb +56 -0
  28. data/lib/evesync/log.rb +66 -0
  29. data/lib/evesync/ntp.rb +16 -0
  30. data/lib/evesync/os/linux/arch/package_manager.rb +29 -0
  31. data/lib/evesync/os/linux/arch/package_watcher.rb +59 -0
  32. data/lib/evesync/os/linux/arch.rb +2 -0
  33. data/lib/evesync/os/linux/base_package_manager.rb +80 -0
  34. data/lib/evesync/os/linux/deb/dpkg.rb +30 -0
  35. data/lib/evesync/os/linux/deb/package_manager.rb +38 -0
  36. data/lib/evesync/os/linux/deb/package_watcher.rb +32 -0
  37. data/lib/evesync/os/linux/deb.rb +2 -0
  38. data/lib/evesync/os/linux/rhel/package_manager.rb +42 -0
  39. data/lib/evesync/os/linux/rhel/package_watcher.rb +32 -0
  40. data/lib/evesync/os/linux/rhel/rpm.rb +41 -0
  41. data/lib/evesync/os/linux/rhel.rb +3 -0
  42. data/lib/evesync/os/linux.rb +9 -0
  43. data/lib/evesync/os.rb +2 -0
  44. data/lib/evesync/sync.rb +209 -0
  45. data/lib/evesync/trigger/base.rb +56 -0
  46. data/lib/evesync/trigger/file.rb +20 -0
  47. data/lib/evesync/trigger/package.rb +20 -0
  48. data/lib/evesync/trigger.rb +106 -0
  49. data/lib/evesync/utils.rb +51 -0
  50. data/lib/evesync/watcher/file.rb +156 -0
  51. data/lib/evesync/watcher/interface.rb +21 -0
  52. data/lib/evesync/watcher/package.rb +8 -0
  53. data/lib/evesync/watcher.rb +68 -0
  54. data/lib/evesync.rb +3 -0
  55. metadata +198 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9a1973a7b7ed425602bbb593496479cfb6ce24e57d5f490ee2d54c52e76375cb
4
+ data.tar.gz: a593092487578200014b495d75a77c20a9798525d00fe9e9ab94ed26b4eba8fd
5
+ SHA512:
6
+ metadata.gz: b06baf168fac9f60cc20995b586712b1de0cf1f13ab32be858d916686d4a770ea67eba82f9b0967bee7b6cc1d664f788d5c4717489320d224a1f2e724ac1ad4c
7
+ data.tar.gz: 7178b8ffc3f5a120438ffd5a0ea1a61fbb3c1a2b9b4b55a90242fadd5b2abcc61379af372bd1575551076b59583d3472faab46214f4c9b3c5687cacf83cfd424
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2018, Valentine Kiselev
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'find'
3
+ require 'mkmf'
4
+ require_relative 'lib/evesync'
5
+ VERSION = Evesync::VERSION
6
+ GEMSPEC = 'evesync.gemspec'.freeze
7
+ GEMFILE = "evesync-#{VERSION}.gem".freeze
8
+
9
+ task default: %i[lint build]
10
+
11
+ task :lint do
12
+ sh 'rubocop -l lib' if find_executable 'rubocop'
13
+ end
14
+
15
+ task :rdoc do
16
+ sh 'rdoc --ri'
17
+ sh 'rdoc'
18
+ end
19
+
20
+ task install: [GEMFILE] do
21
+ sh "gem install --local #{GEMFILE}"
22
+ end
23
+
24
+ task build: GEMFILE
25
+ file GEMFILE do
26
+ sh "gem build #{GEMSPEC}"
27
+ end
28
+
29
+ RSpec::Core::RakeTask.new(:spec)
30
+
31
+ task test: :spec
32
+
33
+ task :clean, [:remove_rpm] do |_t, args|
34
+ args.with_defaults(:remove_rpm => 1)
35
+ rm_rf('tmp')
36
+ rm_rf('doc')
37
+ rm_rf('mkmf.log')
38
+ rm_rf(Dir['*.zip'])
39
+ rm_rf('RPM') if args[:remove_rpm] == 1
40
+ rm_rf("evesync-#{VERSION}")
41
+ rm_rf(GEMFILE)
42
+ end
43
+
44
+ task todos: :todo
45
+ task :todo do
46
+ puts "==== \033[0;31mTODOs\033[0m in code"
47
+ puts `find bin lib dockerfiles -type f -exec grep --color=always TODO \{} \+ ||:`
48
+ puts "==== \033[0;31mFIXMEs\033[0m in code"
49
+ puts `find bin lib dockerfiles -type f -exec grep --color=always FIXME \{} \+ ||:`
50
+ end
51
+
52
+ task :lines do
53
+ sh 'find . -name "*.rb" -exec grep -v -E "^\s*#|^\s*$" \{} \+ | wc -l'
54
+ end
55
+
56
+ ## Docker related targets
57
+
58
+ task :docker do
59
+ sh 'docker-compose build'
60
+ end
61
+
62
+ task :up do
63
+ sh 'docker-compose up -d'
64
+ end
65
+
66
+ task :down do
67
+ sh 'docker-compose stop || docker-compose kill ||:'
68
+ sh 'docker-compose rm --force ||:'
69
+ end
70
+
71
+ task :rpm do
72
+ sh("rpmbuild -bb "\
73
+ "--define 'VERSION #{VERSION}' "\
74
+ "--define 'RELEASE `git rev-list HEAD master --count`' "\
75
+ "--define '_builddir #{Dir.pwd}' "\
76
+ "--define '_rpmdir #{Dir.pwd}/RPM/' "\
77
+ "evesync.spec")
78
+ sh('chown -R 1000:1000 RPM')
79
+ end
80
+
81
+ task :'build-rpm' do
82
+ sh('echo rake rpm | docker-compose run build-centos')
83
+ end
data/bin/evedatad ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'evesync/ipc/server'
5
+ require 'evesync/log'
6
+ require 'evesync/database'
7
+
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)
21
+ end
22
+
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
data/bin/evehand ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'evesync/log'
5
+ require 'evesync/ipc/server'
6
+ require 'evesync/handler'
7
+
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)
25
+ end
26
+
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
data/bin/evemond ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'evesync/log'
5
+ require 'evesync/ipc/server'
6
+ require 'evesync/trigger'
7
+ require 'evesync/watcher'
8
+
9
+ Evesync::Log.info 'Monitoring daemon starting...'
10
+
11
+ queue = Queue.new
12
+ trigger = Evesync::Trigger.new(queue)
13
+ watcher = Evesync::Watcher.new(queue)
14
+
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)
30
+ end
31
+
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
data/bin/evesync ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'optparse'
5
+ require 'socket'
6
+ require 'find'
7
+ require 'zip'
8
+ require 'evesync'
9
+ require 'evesync/log'
10
+ require 'evesync/utils'
11
+ require 'evesync/database'
12
+ require 'evesync/config'
13
+ require 'evesync/sync'
14
+ require 'evesync/ipc/client'
15
+
16
+ DB_TAG = 'db'
17
+ FILES_TAG = 'files'
18
+
19
+ opts = {}
20
+ $program = File.basename($0)
21
+ #Evesync::Log.level = :warn
22
+ Evesync::Log.simple = true
23
+
24
+ OptionParser.new do |parser|
25
+ parser.banner = "#{$program} [OPTIONS...]
26
+
27
+ "
28
+ parser.summary_width = 20
29
+
30
+ parser.on('-l', '--load FILE', 'Load events from file') do |f|
31
+ unless File.file? f
32
+ puts "File is absent: #{f}"
33
+ exit 1
34
+ end
35
+ opts['load_file'] = f
36
+ end
37
+
38
+ parser.on('-z', '--zip [DEST]', 'Zip current database') do |d|
39
+
40
+ # Pretty default name
41
+ hostname = Socket.gethostname
42
+ hostname = hostname == 'localhost' \
43
+ ? Evesync::Utils::local_ip : hostname
44
+ time_now = Time.now.strftime('%Y.%m.%d-%H:%M:%S')
45
+ opts['zip'] = d || "#{$program}_#{hostname}_#{time_now}.zip"
46
+
47
+ # Validation
48
+ unless File.directory? File.dirname opts['zip']
49
+ puts "Directory doesn't exist: #{File.dirname opts['zip']}"
50
+ exit 1
51
+ end
52
+ end
53
+
54
+ parser.on('-f', '--force', 'Enforce synchronization') do
55
+ opts['sync'] = true
56
+ end
57
+
58
+ parser.on_tail('-v', '--version', "Version of #{$program}") do
59
+ puts Evesync::VERSION
60
+ exit
61
+ end
62
+
63
+ parser.on_tail('-h', '--help', 'Help message') do
64
+ puts parser
65
+ exit
66
+ end
67
+ end.parse!
68
+
69
+ def force_sync
70
+ sync_client = Evesync::IPC::Client.new(
71
+ port: :evesyncd
72
+ )
73
+ sync_client.synchronize
74
+ end
75
+
76
+ def files_in(folder)
77
+ begin
78
+ Find.find(folder).collect { |file| file }
79
+ rescue StandardError
80
+ puts "No files in #{folder}"
81
+ []
82
+ end
83
+ end
84
+
85
+ def zip(out)
86
+ databases = {
87
+ DB_TAG => {
88
+ Evesync::Config[:evedatad]['db_path'] =>
89
+ files_in(Evesync::Config[:evedatad]['db_path'])
90
+ },
91
+ FILES_TAG => {
92
+ Evesync::Config[:evedatad]['db_files_path'] =>
93
+ files_in(Evesync::Config[:evedatad]['db_files_path'])
94
+ }
95
+ }
96
+
97
+ Zip::File.open(out, Zip::File::CREATE) do |zip|
98
+ databases.each do |key, entries|
99
+ entries.each do |base_path, files|
100
+ files.each do |file|
101
+ relative_path = Pathname.new(file)
102
+ .relative_path_from(Pathname.new(base_path))
103
+ zip.add(File.join(key, relative_path.to_s), file)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def load_file(filename)
111
+
112
+ Dir.mktmpdir($program) do |tmpdir|
113
+ # Unpacking...
114
+ Zip::File.open(filename) do |zip|
115
+ zip.each do |entry|
116
+ entry.extract(File.join(tmpdir, entry.name))
117
+ end
118
+ end
119
+
120
+ db_path = File.join(tmpdir, DB_TAG)
121
+ db_files_path = File.join(tmpdir, FILES_TAG)
122
+
123
+ loaded_db = Evesync::Database.new(
124
+ db_path,
125
+ db_files_path
126
+ )
127
+
128
+ local_db = Evesync::IPC::Client.new(
129
+ port: :evedatad
130
+ )
131
+
132
+ diff = Evesync::Sync::diff_missed(
133
+ v1: local_db.events,
134
+ v2: loaded_db.events
135
+ )
136
+
137
+ new_messages = loaded_db.messages(diff)
138
+
139
+ sync_client = Evesync::IPC::Client.new(
140
+ port: :evesyncd
141
+ )
142
+
143
+ sync_client.apply_events(new_messages)
144
+ end
145
+
146
+ end
147
+
148
+ def evesync(opts)
149
+ if opts['sync']
150
+ puts 'Synchronization enforced'
151
+ force_sync
152
+ end
153
+
154
+ if opts['load_file']
155
+ load_file opts['load_file']
156
+ end
157
+
158
+ if opts['zip']
159
+ zip(opts['zip'])
160
+ puts(opts['zip'])
161
+ end
162
+ end
163
+
164
+ evesync opts
data/bin/evesyncd ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require 'evesync/log'
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
+
17
+ sync = Evesync::Sync.new
18
+
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)
27
+ end
28
+
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)
44
+ end
data/bin/start ADDED
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,58 @@
1
+ # This is a configuration file example
2
+ # It contains all information about daemons that can be changed
3
+ # It's TOML formatted file, so feel free to use this format
4
+ # without any restrictions.
5
+ #
6
+ # For more information about TOML format see:
7
+ # - https://github.com/toml-lang/toml
8
+ # - https://en.wikipedia.org/wiki/TOML
9
+
10
+ # Available log levels:
11
+ # debug info warn error fatal
12
+ loglevel = 'debug'
13
+ # Delay in seconds for discovery package sending
14
+ discover_timeout = 600
15
+ # Automate discovering
16
+ auto_discover = true
17
+
18
+ ntp = 'us.pool.ntp.org'
19
+
20
+ [evemond] # System Monitor daemon
21
+ port = 55443
22
+
23
+ # A list of remote nodes ips to keep connected with
24
+ remotes = [ ]
25
+
26
+ # Files to watch and synchronize
27
+ watch = [
28
+ '/etc/pam.d/login',
29
+ '/etc/pam.d/other',
30
+ ]
31
+
32
+ # Interval for gathering files' changes
33
+ watch_interval = 3
34
+
35
+ # TODO:
36
+ ignore_packages = [
37
+ 'ruby',
38
+ ]
39
+
40
+ # TODO:
41
+ ignore_files = [
42
+ '/etc/hosts',
43
+ ]
44
+
45
+
46
+ [evehand] # System Handler daemon
47
+ port = 55432
48
+
49
+
50
+ [evedatad] # System Data daemon
51
+ port = 54321
52
+
53
+ [evesyncd]
54
+ port = 64653
55
+ broadcast_port = 64564
56
+
57
+
58
+ # Copyright (c) Kiselev Valentin 2019
@@ -0,0 +1,63 @@
1
+ require 'toml-rb'
2
+ require 'evesync/utils'
3
+ require 'evesync/constants'
4
+ require 'evesync/log'
5
+
6
+ module Evesync
7
+ module Config
8
+ class << self
9
+
10
+ DEFAULTS = {
11
+ 'ntp' => '',
12
+ 'evemond' => {
13
+ 'port' => Constants::MOOND_PORT,
14
+ 'remotes' => [],
15
+ 'watch' => [],
16
+ 'watch_interval' => Constants::WATCH_INTERVAL
17
+ },
18
+ 'evedatad' => {
19
+ 'port' => Constants::DATAD_PORT,
20
+ 'db_path' => Constants::DB_PATH,
21
+ 'db_files_path' => Constants::DB_FILES_PATH
22
+ },
23
+ 'evehand' => {
24
+ 'port' => Constants::HAND_PORT
25
+ },
26
+ 'evesyncd' => {
27
+ 'port' => Constants::SYNC_PORT
28
+ },
29
+ 'discover_timeout' => Constants::DISCOVER_TIMEOUT
30
+ }
31
+
32
+
33
+ def [](daemon)
34
+ read_config if needs_reading
35
+
36
+ @@config[daemon.to_s]
37
+ end
38
+
39
+ def reread
40
+ read_config
41
+ end
42
+
43
+ private
44
+
45
+ def read_config
46
+ config = begin
47
+ TomlRB.load_file(Constants::CONFIG_FILE)
48
+ rescue StandardError => e
49
+ Log.error("Config ERROR: Couldn't parse file #{Constants::CONFIG_FILE}")
50
+ Log.error("Config ERROR MESSAGE: #{e}")
51
+ Log.error("Config ERROR: Using default configuration")
52
+ {}
53
+ end
54
+ @@config = DEFAULTS.deep_merge(config)
55
+ Log.info("Config initialized!")
56
+ end
57
+
58
+ def needs_reading
59
+ ! defined? @@config
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,17 @@
1
+ module Evesync
2
+ module Constants
3
+ MOOND_PORT = '55443'.freeze
4
+ DATAD_PORT = '54321'.freeze
5
+ HAND_PORT = '55432'.freeze
6
+ SYNC_PORT = '64653'.freeze
7
+
8
+ CONFIG_FILE = '/etc/evesync.conf'.freeze
9
+ DB_PATH = '/var/lib/evesync/db/'.freeze
10
+ DB_FILES_PATH = '/var/lib/evesync/files/'.freeze
11
+
12
+ DEFAULT_LOGLEVEL = 'debug'.freeze
13
+
14
+ DISCOVER_TIMEOUT = 3600
15
+ WATCH_INTERVAL = 2
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+ require 'lmdb'
4
+ require 'evesync/config'
5
+ require 'evesync/constants'
6
+ require 'evesync/ipc/data/package'
7
+ require 'evesync/ipc/data/file'
8
+ require 'evesync/ipc/data/ignore'
9
+
10
+ module Evesync
11
+
12
+ ##
13
+ # *Database* class is a proxy for *evedatad* daemon
14
+ # implements at least one method: +save+. Allows
15
+ # Local +evemond+ save messages about changes
16
+ #
17
+ # Messages should be serializable (JSON)
18
+ #
19
+ # TODO:
20
+ # * Think about how it can be widened
21
+
22
+ class Database
23
+ def initialize(db_path=nil, db_files_path=nil)
24
+ path = db_path || Config[:evedatad]['db_path']
25
+
26
+ unless ::File.exist? path
27
+ # FIXME: only root. handle exception
28
+ FileUtils.mkdir_p(path)
29
+ end
30
+ @env = LMDB.new(path)
31
+ @db = @env.database
32
+ @files_path = db_files_path || Config[:evedatad]['db_files_path']
33
+ end
34
+
35
+ # Save message to database, key is timestamp+object
36
+ def save(message)
37
+ Log.debug("Database save: #{message}")
38
+ db_add_entry(message)
39
+
40
+ if message.is_a? Evesync::IPC::Data::File
41
+ Log.debug("Database save file action: #{message.action}")
42
+ unless message.action ==
43
+ IPC::Data::File::Action::DELETE
44
+ save_file(message)
45
+ end
46
+ end
47
+ true
48
+ end
49
+
50
+ # Events simplified: object => [timestamp...]
51
+ def events
52
+ events = {}
53
+ @db.each do |key, _|
54
+ timestamp, object = parse_event(key)
55
+ events[object] ||= []
56
+ events[object].push(timestamp)
57
+ end
58
+ events
59
+ end
60
+
61
+ # Messages for events: object => {timestamp => message}
62
+ def messages(events)
63
+ ev_msgs = {}
64
+ @db.each do |key, message|
65
+ timestamp, object = parse_event(key)
66
+ next unless events.include?(object)
67
+
68
+ ev_msgs[object] ||= {}
69
+ if events[object].include?(timestamp)
70
+ ev_msgs[object][timestamp] = message
71
+ end
72
+ end
73
+ ev_msgs
74
+ end
75
+
76
+ private
77
+
78
+ def db_add_entry(message)
79
+ Log.debug('Database adding entry...')
80
+ key = create_key(message)
81
+ value = create_value(message)
82
+ @db[key] = value
83
+ Log.debug('Database adding entry done!')
84
+ end
85
+
86
+ def create_key(message)
87
+ "#{message.timestamp}_#{message.name}"
88
+ end
89
+
90
+ def create_value(message)
91
+ message.to_hash.to_json
92
+ end
93
+
94
+ def parse_event(key)
95
+ key.split('_', 2)
96
+ end
97
+
98
+ def save_file(file)
99
+ Log.debug('Database saving file...')
100
+ fulldest = File.join(@files_path,
101
+ file.name + ".#{file.timestamp}")
102
+ FileUtils.mkdir_p(File.dirname(fulldest))
103
+ FileUtils.cp(file.name, fulldest)
104
+ Log.debug('Database saving file done!')
105
+ end
106
+ end
107
+ end