filecluster 0.5.14 → 0.5.15

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