filecluster 0.5.14 → 0.5.15

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
  SHA1:
3
- metadata.gz: df71ee0cd0c4068b96655ad85da5a84de2aa2044
4
- data.tar.gz: f7787796c064db4b2e7f373d94422cb0af708eca
3
+ metadata.gz: 6dc0301108025f48875d13f484a6dda7d1d9be97
4
+ data.tar.gz: 35987690b4cd3f40cf439f1f63e6c78bfa6b98fb
5
5
  SHA512:
6
- metadata.gz: 053ef243b2f1c07f955207af615764909245cdcf5d7b35d76c34f267e3f80a9572f1465208cd2d2bfd34e2095103988609cbba66fb3f8c9d1ad29b27443b5f96
7
- data.tar.gz: 52e8e8ee2cf22ede6b1f31448d3b7233ea3a883938c8a4ec4d27922b08dc124a76b2f74668e146c4cc6d51e45060dd0b563071375e5afc6fc32b0077374855da
6
+ metadata.gz: 8953e38a31a07cddf4912fa30abc6f3897703ca09ff318986ba6158901c434dfca5331dd5ff2a47b6a0f94f5f46008d000e9be15ee0536000bf3bdbcac20f78b
7
+ data.tar.gz: bde827cae587aaeda3b7d8d1259dd1033c2d55549afb7e3faa78ab89d4ce500bb17136a4bf01570320b8b3f3796d8bd095d9b7575359a9154c3cc91f0e8c38c3
data/.gitignore CHANGED
@@ -17,3 +17,6 @@ test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
19
  *.yml
20
+ .byebug_history
21
+ /.ssh/*
22
+ !/.ssh/.keep
File without changes
@@ -0,0 +1,40 @@
1
+ FROM ubuntu:16.04
2
+
3
+ RUN apt-get update \
4
+ && apt-get install -y wget \
5
+ build-essential \
6
+ zlib1g-dev \
7
+ openssl \
8
+ libssl-dev \
9
+ git \
10
+ libreadline-dev \
11
+ libmysqlclient-dev \
12
+ tzdata \
13
+ super \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ RUN wget http://ftp.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz \
17
+ && tar -xzvf ruby-2.3.3.tar.gz \
18
+ && rm ruby-2.3.3.tar.gz \
19
+ && cd ruby-2.3.3/ \
20
+ && ./configure --with-openssl-dir=/usr/bin \
21
+ && make \
22
+ && make install \
23
+ && cd .. \
24
+ && rm -rf ruby-2.3.3 \
25
+ && gem install bundler
26
+
27
+ RUN mkdir -p /app
28
+ WORKDIR /app
29
+
30
+ COPY ./ /app
31
+
32
+ RUN groupadd --gid 1000 filecluster \
33
+ && useradd --uid 1000 --gid 1000 filecluster --shell /bin/bash
34
+
35
+
36
+ COPY ./entrypoint.sh /usr/local/bin/
37
+ RUN bundle install --jobs 20 --retry 6
38
+
39
+ ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
40
+ CMD ["/app/bin/fc-daemon", "-l", "debug"]
data/Gemfile CHANGED
@@ -2,3 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in filecluster.gemspec
4
4
  gemspec
5
+
@@ -0,0 +1,32 @@
1
+ FileCluster
2
+
3
+ Набор скриптов для управления контентом по верх файловой системы
4
+ Уровень регистрации в базе - файл или папка
5
+
6
+
7
+ Принцип работы
8
+
9
+ Информация о файлах/папках записывается в базу данных (mysql)
10
+ Демон на основе записей в базе данных приводит соответствие файлов списку зарегистрированных файлов/папок
11
+
12
+
13
+ Компоненты
14
+
15
+ filecluster - deamon/manager (ruby)
16
+ mysql - мета информация
17
+ sshd + rsync - транспорт
18
+
19
+
20
+
21
+ Быстрый запуск тестов
22
+
23
+ первый запуск mysql -
24
+
25
+ docker-compose run --rm filecluster-db
26
+
27
+ Окончание инициализации можно будет увидеть по сообщению о готовности к работе mysql
28
+ CTRL+C
29
+
30
+ Запуск тестов
31
+
32
+ docker-compose run --rm filecluser1 rake
@@ -90,9 +90,12 @@ while true do
90
90
  storages_check
91
91
  update_tasks
92
92
  run_tasks
93
+ autosync
93
94
  end
94
95
  $log.debug('sleep')
95
- sleep FC::Var.get('daemon_cycle_time', 30).to_i
96
+ sleep_time = FC::Var.get('daemon_cycle_time', 30).to_f
97
+ sleep_time = 0.3 if sleep_time < 0.3
98
+ sleep sleep_time
96
99
  alive_time = Time.new.to_i - start_time
97
100
  if !$exit_signal && alive_time > FC::Var.get('daemon_restart_period', 86400).to_i
98
101
  $log.info("Self restart, #{alive_time} seconds up")
@@ -72,6 +72,14 @@ Command:
72
72
  list show all limits
73
73
  change <host> change current copy speed limit for host
74
74
  add add copy speed limit for host
75
+ }],
76
+ 'autosync' => [
77
+ 'show and change autosync intervals for FC hosts',
78
+ %q{Usage: fc-manage autosync <command>
79
+ Command:
80
+ list show all intervals
81
+ change <host> change sync interval for host
82
+ add add non default sync interval for host
75
83
  }],
76
84
  'item' => [
77
85
  'show and manage items',
@@ -52,9 +52,9 @@ if s == "y" || s == "yes"
52
52
  end
53
53
  unless default_db_config
54
54
  print "Save to config.. "
55
- options.select!{|key, val| descriptions[key][:save]}
55
+ config = options.select { |key, _| descriptions[key][:save] }
56
56
  File.open(File.expand_path(File.dirname(__FILE__))+'/db.yml', 'w') do |f|
57
- f.write(options.to_yaml)
57
+ f.write(config.to_yaml)
58
58
  end
59
59
  puts "ok"
60
60
  end
@@ -0,0 +1,35 @@
1
+ version: '3.5'
2
+
3
+ services:
4
+ filecluster-db:
5
+ image: mysql:5.6
6
+ volumes:
7
+ - filecluster_db56:/var/lib/mysql
8
+ env_file:
9
+ - ./docker/development.env
10
+
11
+ filecluster1:
12
+ build: ./
13
+ depends_on:
14
+ - filecluster-db
15
+ - filecluster1-ssh
16
+ env_file:
17
+ - ./docker/development.env
18
+
19
+ volumes:
20
+ - filetest_1:/tmp/
21
+ - ./:/app/
22
+ - ./.ssh:/home/filecluster/.ssh
23
+
24
+ filecluster1-ssh:
25
+ image: asigatchov/ubuntu16-sshd
26
+ volumes:
27
+ - filetest_1:/tmp/
28
+ - ./.ssh:/home/filecluster/.ssh
29
+
30
+
31
+ volumes:
32
+ filecluster_db56:
33
+ driver: local
34
+ filetest_1:
35
+ driver: local
@@ -0,0 +1,35 @@
1
+ version: '3.5'
2
+
3
+ services:
4
+ filecluster-db:
5
+ image: mysql:5.7.21
6
+ volumes:
7
+ - filecluster_db:/var/lib/mysql
8
+ env_file:
9
+ - ./docker/development.env
10
+
11
+ filecluster1:
12
+ build: ./
13
+ depends_on:
14
+ - filecluster-db
15
+ - filecluster1-ssh
16
+ volumes:
17
+ - filetest_1:/tmp/
18
+ - ./:/app/
19
+ - ./.ssh:/home/filecluster/.ssh
20
+ hostname: filecluster1
21
+ env_file:
22
+ - ./docker/development.env
23
+
24
+
25
+ filecluster1-ssh:
26
+ image: asigatchov/ubuntu16-sshd
27
+ volumes:
28
+ - filetest_1:/tmp/
29
+ - ./.ssh:/home/filecluster/.ssh
30
+
31
+ volumes:
32
+ filecluster_db:
33
+ driver: local
34
+ filetest_1:
35
+ driver: local
@@ -0,0 +1,17 @@
1
+ FROM ubuntu:16.04
2
+
3
+ RUN apt-get update && apt-get install -y openssh-server rsync
4
+ RUN mkdir /var/run/sshd
5
+
6
+ #RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
7
+
8
+ # SSH login fix. Otherwise user is kicked off after login
9
+ RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
10
+
11
+ ENV NOTVISIBLE "in users profile"
12
+ RUN echo "export VISIBLE=now" >> /etc/profile
13
+ RUN groupadd --gid 1000 filecluster \
14
+ && useradd --uid 1000 --gid 1000 filecluster --shell /bin/bash
15
+
16
+ EXPOSE 22
17
+ CMD ["/usr/sbin/sshd", "-D"]
@@ -0,0 +1,7 @@
1
+ MYSQL_ALLOW_EMPTY_PASSWORD=1
2
+ MYSQL_USER=filecluster
3
+ MYSQL_DATABASE =filecluser
4
+ MYSQL_PASSWORD=clusterfile
5
+ MYSQL_HOST=filecluster-db
6
+ SSH_HOST=filecluster1-ssh
7
+ FILECLUSTE_ENV=development
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+
3
+
4
+ if [ ! -f /app/bin/db.yml ] ; then
5
+ ./bin/fc-setup-db -h $MYSQL_HOST -u $MYSQL_USER -p $MYSQL_PASSWORD -d $MYSQL_DATABASE -f -i -m
6
+ fi
7
+
8
+ if [ ! -f /home/filecluster/.ssh/id_rsa ]; then
9
+ mkdir /home/filecluster/.ssh -p
10
+ chown filecluster:filecluster /home/filecluster/.ssh
11
+ chmod 0700 /home/filecluster/.ssh
12
+ ssh-keygen -t rsa -b 2048 -N "" -C "filecluster key" -f /home/filecluster/.ssh/id_rsa
13
+ cp /home/filecluster/.ssh/{id_rsa.pub,authorized_keys}
14
+ chown -R filecluster:filecluster /home/filecluster
15
+ echo "ID_RSA - generate"
16
+ fi
17
+
18
+ export HOME=/home/filecluster/
19
+ exec setuid 1000 "$@"
@@ -22,4 +22,6 @@ Gem::Specification.new do |gem|
22
22
  gem.add_development_dependency "rake"
23
23
  gem.add_development_dependency "shoulda-context"
24
24
  gem.add_development_dependency "mocha", ">= 0.13.3"
25
+ gem.add_development_dependency "byebug"
26
+ gem.add_development_dependency "rubocop"
25
27
  end
@@ -0,0 +1,149 @@
1
+ require 'iostat'
2
+
3
+ class Autosync
4
+ attr_accessor :files_to_delete, :items_to_delete
5
+ def initialize(storage, dry_run = false)
6
+ @dry_run = dry_run
7
+ @start_time = Time.now.to_i - 3600
8
+ @storage = storage
9
+ @files_to_delete = []
10
+ @items_to_delete = []
11
+ @removed_files_size = 0
12
+ @io_stat = Iostat.new(@storage.path)
13
+ end
14
+
15
+ def self.error(msg, options = {})
16
+ $log.error(msg) if $log
17
+ FC::Error.new(options.merge(:host => FC::Storage.curr_host, :message => msg)).save
18
+ end
19
+
20
+ def run
21
+ @db_struct = fill_db
22
+ $log.debug("Autosync: Scanning disk #{@storage.name} (#{@storage.path})") if $log
23
+ scan_disk(@db_struct, '')
24
+ return if $exit_signal
25
+ $log.debug("Autosync: Scanning DB items #{@storage.name}") if $log
26
+ scan_db(@db_struct, '')
27
+ return if $exit_signal
28
+ delete_diffs unless @dry_run
29
+ ensure
30
+ @io_stat.stop
31
+ end
32
+
33
+ def relax_drive
34
+ sleep 1 while @io_stat.util > 50
35
+ end
36
+
37
+ def fill_db
38
+ $log.debug("Autosync: Reading DB items for #{@storage.name} ...") if $log
39
+
40
+ db_struct = {}
41
+ last_item_storage_id = 0
42
+ items_count = 0
43
+ loop do
44
+ items = FC::DB.connect.query(%(
45
+ SELECT its.id, itm.name
46
+ FROM #{FC::Item.table_name} itm
47
+ JOIN #{FC::ItemStorage.table_name} its ON its.item_id = itm.id
48
+ WHERE its.storage_name = '#{@storage.name}'
49
+ AND its.status = 'ready'
50
+ AND its.id > #{last_item_storage_id}
51
+ ORDER BY its.id
52
+ LIMIT 10000
53
+ ), cache_rows: false, symbolize_keys: true)
54
+ break if $exit_signal
55
+
56
+ # make tree structure with array of values (items) on leafs
57
+ items.each do |i|
58
+ items_count += 1
59
+ last_item_storage_id = i[:id]
60
+ ref = db_struct
61
+ path = i[:name].split('/')
62
+ last_idx = path.size - 1
63
+ path.each_with_index do |part, idx|
64
+ if idx == last_idx
65
+ ref[part] = [false, i[:id]]
66
+ else
67
+ ref[part] ||= {}
68
+ ref = ref[part]
69
+ end
70
+ end
71
+ end
72
+ break unless items.size == 10_000
73
+ end
74
+ $log.debug("Autosync: Reading DB items for #{@storage.name} done. Items: #{items_count}") if $log
75
+ db_struct
76
+ end
77
+
78
+ def scan_disk(db_path, relative_path)
79
+ return if $exit_signal
80
+ sleep 0.001
81
+ relax_drive
82
+ Dir.glob("#{@storage.path}#{relative_path}*").each do |disk_entry|
83
+ next if disk_entry == "#{@storage.path}healthcheck"
84
+ db_item = db_path[disk_entry.split('/').last]
85
+ case
86
+ when db_item.is_a?(Array) # tree leaf
87
+ db_item[0] = true # mark db_item as exists on disk
88
+ when db_item.is_a?(Hash) && File.directory?(disk_entry) # tree node
89
+ scan_disk(db_item, "#{disk_entry[@storage.path.size..-1]}/")
90
+ else # not found in db
91
+ mtime = File.stat(disk_entry).mtime.to_i rescue Time.now.to_i
92
+ @files_to_delete << disk_entry if @start_time > mtime # older than 1 hour
93
+ end
94
+ end
95
+ end
96
+
97
+ def scan_db(db_item, node_path)
98
+ return if $exit_signal
99
+ db_item.each do |item_name, item_data|
100
+ if item_data.is_a?(Array) # tree leaf
101
+ @items_to_delete << item_data[1] unless item_data[0]
102
+ else # tree node
103
+ scan_db(item_data, "#{node_path}#{item_name}/")
104
+ end
105
+ end
106
+ end
107
+
108
+ def delete_disk_entry(entry)
109
+ return false if $exit_signal
110
+ return true unless File.exist?(entry)
111
+ remove = true
112
+ stat = File.stat(entry) rescue nil
113
+ if File.directory?(entry)
114
+ Dir.glob("#{entry}/*").each do |sub_entry|
115
+ relax_drive
116
+ remove = false unless delete_disk_entry(sub_entry)
117
+ end
118
+ else
119
+ mtime = stat ? stat.mtime.to_i : Time.now.to_i
120
+ remove = @start_time > mtime
121
+ end
122
+ if remove
123
+ @removed_files_size += stat.size if stat
124
+ $log.debug("deleting disk entry #{entry}") if $log
125
+ FileUtils.rm_rf(entry)
126
+ end
127
+ remove
128
+ end
129
+
130
+ def delete_diffs
131
+ $log.debug("Removing #{@files_to_delete.size} disk entries") if $log
132
+ @files_to_delete.each do |f|
133
+ break if $exit_signal
134
+ delete_disk_entry(f)
135
+ end
136
+ return if $exit_signal
137
+ self.class.error("Autosync removed #{@files_to_delete.size} files/dirs from #{@storage.name}. Size: #{@removed_files_size} bytes") if @removed_files_size > 0
138
+ $log.debug("Removing items #{@items_to_delete.size} from DB for #{@storage.name}") if $log
139
+ counter = 0
140
+ @items_to_delete.each do |item_storage_id|
141
+ its = FC::ItemStorage.where('id = ?', item_storage_id).first
142
+ next unless its
143
+ its.status = 'error'
144
+ its.save
145
+ self.class.error("item does not exist on storage #{@storage.name}", item_storage_id: item_storage_id.to_i) rescue nil
146
+ sleep 10 if (counter += 1) % 1000 == 0
147
+ end
148
+ end
149
+ end
@@ -6,6 +6,7 @@ require "daemon/run_tasks_thread"
6
6
  require "daemon/update_tasks_thread"
7
7
  require "daemon/copy_task_thread"
8
8
  require "daemon/delete_task_thread"
9
+ require "daemon/autosync_thread"
9
10
 
10
11
  def error(msg, options = {})
11
12
  $log.error(msg)
@@ -20,7 +21,8 @@ end
20
21
 
21
22
  def run_global_daemon
22
23
  $log.debug('Run global daemon check')
23
- timeout = FC::Var.get('daemon_global_wait_time', 120).to_i
24
+ timeout = FC::Var.get('daemon_global_wait_time', 120).to_f
25
+ timeout = 0.3 if timeout < 0.3
24
26
  r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
25
27
  if !r || r['curr_time'].to_i - r['time'].to_i > timeout
26
28
  $log.debug('Set global daemon host to current')
@@ -72,3 +74,17 @@ def run_tasks
72
74
  $run_tasks_thread = RunTasksThread.new
73
75
  end
74
76
  end
77
+
78
+ def autosync
79
+ if !$autosync_thread || !$autosync_thread.alive?
80
+ intervals = FC::Var.get_autosync
81
+ storage_interval = intervals[FC::Storage.curr_host] || intervals['all']
82
+ return if storage_interval.zero? # do not run aytosync
83
+ storages = $storages.select do |s|
84
+ s.autosync_at.to_i + storage_interval < Time.now.to_i
85
+ end
86
+ return unless storages.any?
87
+ $log.debug("spawn AutosyncThread for storages #{storages.map(&:name).join(', ')}")
88
+ $autosync_thread = AutosyncThread.new(storages)
89
+ end
90
+ end