2010-04-20 23:05:11 +00:00
|
|
|
module Mirrors
|
|
|
|
class MirrorError < Exception; end
|
|
|
|
|
2014-08-23 16:44:43 +09:00
|
|
|
def ssh_open_pipe(mirror, command, timeout = 30)
|
2010-04-20 23:05:11 +00:00
|
|
|
remote_user_host = "#{mirror[:user]}@#{mirror[:host]}"
|
|
|
|
ret = nil
|
2012-03-17 01:04:51 +00:00
|
|
|
IO.popen("/usr/bin/ssh -o ServerAliveInterval=30 -o Compression=no -o BatchMode=yes -o ConnectTimeout=#{timeout} #{remote_user_host} '#{command}'") do |f|
|
2010-04-20 23:05:11 +00:00
|
|
|
ret = yield(f)
|
|
|
|
end
|
2014-11-08 22:57:37 +09:00
|
|
|
if ($CHILD_STATUS & 0xFF) != 0
|
2024-01-08 19:39:01 +09:00
|
|
|
raise MirrorError, "Command \"%s\" to %s exited with signal %i" % [ command, mirror[:host], $CHILD_STATUS & 0xFF ]
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
2014-11-08 22:57:37 +09:00
|
|
|
if ($CHILD_STATUS >> 8) != 0
|
2024-01-08 19:39:01 +09:00
|
|
|
raise MirrorError, "Command \"%s\" to %s exited with status %i" % [ command, mirror[:host], $CHILD_STATUS >> 8 ]
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
2014-08-23 16:56:00 +09:00
|
|
|
ret
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
module_function :ssh_open_pipe
|
|
|
|
|
2011-01-31 01:43:14 +00:00
|
|
|
def filter_mirror_list(options)
|
|
|
|
results = []
|
2014-08-23 18:10:14 +09:00
|
|
|
CONFIG["mirrors"].each do |mirror|
|
2011-01-31 02:09:38 +00:00
|
|
|
next if options[:previews_only] != mirror[:previews_only]
|
2011-01-31 01:43:14 +00:00
|
|
|
results << mirror
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2011-01-31 01:43:14 +00:00
|
|
|
|
2014-08-23 16:56:00 +09:00
|
|
|
results
|
2011-01-31 01:43:14 +00:00
|
|
|
end
|
2011-01-31 02:09:38 +00:00
|
|
|
module_function :filter_mirror_list
|
2011-01-31 01:43:14 +00:00
|
|
|
|
2014-08-23 16:44:43 +09:00
|
|
|
def create_mirror_paths(dirs, options = {})
|
2011-01-31 01:43:14 +00:00
|
|
|
dirs = dirs.uniq
|
|
|
|
|
|
|
|
target_mirrors = filter_mirror_list(options)
|
2014-08-23 18:10:14 +09:00
|
|
|
target_mirrors.each do |mirror|
|
2011-01-31 01:29:48 +00:00
|
|
|
remote_user_host = "#{mirror[:user]}@#{mirror[:host]}"
|
|
|
|
remote_dirs = []
|
2014-08-23 18:10:14 +09:00
|
|
|
dirs.each do |dir|
|
2011-01-31 01:29:48 +00:00
|
|
|
remote_dirs << mirror[:data_dir] + "/" + dir
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2011-01-31 01:29:48 +00:00
|
|
|
|
|
|
|
# Create all directories in one go.
|
|
|
|
system("/usr/bin/ssh", "-o", "Compression=no", "-o", "BatchMode=yes",
|
|
|
|
remote_user_host, "mkdir -p #{remote_dirs.uniq.join(" ")}")
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2011-01-31 01:29:48 +00:00
|
|
|
end
|
|
|
|
module_function :create_mirror_paths
|
|
|
|
|
2010-04-20 23:05:11 +00:00
|
|
|
# Copy a file to all mirrors. file is an absolute path which must be
|
|
|
|
# located in public/data; the files will land in the equivalent public/data
|
|
|
|
# on each mirror.
|
|
|
|
#
|
2011-01-31 01:43:14 +00:00
|
|
|
# If options[:previews_only] is true, copy only to :previews_only mirrors; otherwise
|
|
|
|
# copy only to others.
|
|
|
|
#
|
2010-04-20 23:05:11 +00:00
|
|
|
# Because we have no mechanism for indicating that a file is only available on
|
|
|
|
# certain mirrors, if any mirror fails to upload, MirrorError will be thrown
|
|
|
|
# and the file should be treated as completely unwarehoused.
|
2014-08-23 16:44:43 +09:00
|
|
|
def copy_file_to_mirrors(file, options = {})
|
2010-04-20 23:05:11 +00:00
|
|
|
# CONFIG[:data_dir] is equivalent to our local_base.
|
2012-04-29 10:56:41 -07:00
|
|
|
local_base = "#{Rails.root}/public/data/"
|
2024-01-08 19:39:01 +09:00
|
|
|
options = { timeout: 30 }.merge(options)
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-11-08 22:57:37 +09:00
|
|
|
if file[0, local_base.length] != local_base
|
2010-04-20 23:05:11 +00:00
|
|
|
raise "Invalid filename to mirror: \"%s" % file
|
|
|
|
end
|
|
|
|
|
2020-05-05 05:54:33 +09:00
|
|
|
expected_md5 = Moebooru::Hasher.compute_one(file, :md5)
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2011-01-31 01:43:14 +00:00
|
|
|
target_mirrors = filter_mirror_list(options)
|
2014-08-23 18:10:14 +09:00
|
|
|
target_mirrors.each do |mirror|
|
2010-04-20 23:05:11 +00:00
|
|
|
remote_user_host = "#{mirror[:user]}@#{mirror[:host]}"
|
|
|
|
remote_filename = "#{mirror[:data_dir]}/#{file[local_base.length, file.length]}"
|
|
|
|
|
|
|
|
# Tolerate a few errors in case of communication problems.
|
|
|
|
retry_count = 0
|
|
|
|
|
|
|
|
begin
|
|
|
|
# Check if the file is already mirrored before we spend time uploading it.
|
|
|
|
# Linux needs md5sum; FreeBSD needs md5 -q.
|
|
|
|
actual_md5 = Mirrors.ssh_open_pipe(mirror,
|
2014-08-23 20:14:24 +09:00
|
|
|
"if [ -f #{remote_filename} ]; then (which md5sum >/dev/null) && md5sum #{remote_filename} || md5 -q #{remote_filename}; fi",
|
|
|
|
timeout = options[:timeout]) { |f| f.gets }
|
2010-04-20 23:05:11 +00:00
|
|
|
if actual_md5 =~ /^[0-9a-f]{32}/
|
|
|
|
actual_md5 = actual_md5.slice(0, 32)
|
|
|
|
if expected_md5 == actual_md5
|
|
|
|
next
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-08-23 18:28:59 +09:00
|
|
|
unless system("/usr/bin/scp", "-pq", "-o", "Compression no", "-o", "BatchMode=yes",
|
2014-08-23 20:14:24 +09:00
|
|
|
"-o", "ConnectTimeout=%i" % timeout,
|
2014-11-08 22:57:37 +09:00
|
|
|
file, "#{remote_user_host}:#{remote_filename}")
|
2010-04-20 23:05:11 +00:00
|
|
|
raise MirrorError, "Error copying #{file} to #{remote_user_host}:#{remote_filename}"
|
|
|
|
end
|
|
|
|
|
|
|
|
# Don't trust scp; verify the files.
|
2014-08-23 18:10:14 +09:00
|
|
|
actual_md5 = Mirrors.ssh_open_pipe(mirror, "if [ -f #{remote_filename} ]; then (which md5sum >/dev/null) && md5sum #{remote_filename} || md5 -q #{remote_filename}; fi") { |f| f.gets }
|
2010-04-20 23:05:11 +00:00
|
|
|
if actual_md5 !~ /^[0-9a-f]{32}/
|
|
|
|
raise MirrorError, "Error verifying #{remote_user_host}:#{remote_filename}: #{actual_md5}"
|
|
|
|
end
|
|
|
|
|
|
|
|
actual_md5 = actual_md5.slice(0, 32)
|
|
|
|
|
|
|
|
if expected_md5 != actual_md5
|
|
|
|
raise MirrorError, "Verifying #{remote_user_host}:#{remote_filename} failed: got #{actual_md5}, expected #{expected_md5}"
|
|
|
|
end
|
2014-08-23 19:43:06 +09:00
|
|
|
rescue MirrorError
|
2010-04-20 23:05:11 +00:00
|
|
|
retry_count += 1
|
|
|
|
raise if retry_count == 3
|
|
|
|
|
2012-06-04 17:24:00 +07:00
|
|
|
retry
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
module_function :copy_file_to_mirrors
|
|
|
|
|
|
|
|
# Return a URL prefix for a file. If not warehoused, always returns the main
|
|
|
|
# server. If a seed is specified, seeds the server selection; otherwise, each
|
|
|
|
# IP will always use the same server.
|
|
|
|
#
|
|
|
|
# If :zipfile is set, ignore mirrors with the :nozipfile flag.
|
|
|
|
if CONFIG["image_store"] == :remote_hierarchy
|
2014-08-23 20:22:31 +09:00
|
|
|
def select_main_image_server
|
|
|
|
return CONFIG["url_base"] if !CONFIG["image_servers"] || CONFIG["image_servers"].empty?
|
|
|
|
raise 'CONFIG["url_base"] is set incorrectly; please see config/default_config.rb' if CONFIG["image_servers"][0].class == String
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
CONFIG["image_servers"][0][:server]
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
def select_image_server(is_warehoused, seed = 0, options = {})
|
|
|
|
return CONFIG["url_base"] if !CONFIG["image_servers"] || CONFIG["image_servers"].empty?
|
|
|
|
raise 'CONFIG["url_base"] is set incorrectly; please see config/default_config.rb' if CONFIG["image_servers"][0].class == String
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
unless is_warehoused
|
|
|
|
# return CONFIG["url_base"]
|
|
|
|
return CONFIG["image_servers"][0][:server]
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
mirrors = CONFIG["image_servers"]
|
2014-11-08 22:57:37 +09:00
|
|
|
if options[:preview]
|
2014-08-23 20:22:31 +09:00
|
|
|
mirrors = mirrors.select do |mirror|
|
|
|
|
mirror[:nopreview] != true
|
|
|
|
end
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-11-08 22:57:37 +09:00
|
|
|
unless options[:preview]
|
2014-08-23 20:22:31 +09:00
|
|
|
mirrors = mirrors.select do |mirror|
|
|
|
|
mirror[:previews_only] != true
|
|
|
|
end
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-11-08 22:57:37 +09:00
|
|
|
if options[:zipfile]
|
2014-08-23 20:22:31 +09:00
|
|
|
mirrors = mirrors.select do |mirror|
|
|
|
|
mirror[:nozipfile] != true
|
|
|
|
end
|
2014-08-23 18:10:14 +09:00
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
raise "No usable mirrors" if mirrors.empty?
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
total_weights = 0
|
|
|
|
mirrors.each { |s| total_weights += s[:traffic] }
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
seed += Thread.current["danbooru-ip_addr_seed"] || 0
|
|
|
|
seed %= total_weights
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
server = nil
|
|
|
|
mirrors.each do |s|
|
|
|
|
w = s[:traffic]
|
|
|
|
if seed < w
|
|
|
|
server = s
|
|
|
|
break
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
seed -= w
|
|
|
|
end
|
|
|
|
server ||= mirrors[0]
|
|
|
|
|
|
|
|
# If this server has aliases, and an ID has been specified, pick a random server alias
|
|
|
|
# with the ID as a seed so we always use the same server name for the same image.
|
2014-11-08 22:57:37 +09:00
|
|
|
if !options[:id].nil? && !server[:aliases].nil? && options[:use_aliases]
|
2014-08-23 20:22:31 +09:00
|
|
|
server_count = server[:aliases].length + 1
|
|
|
|
server_index = options[:id] % server_count
|
2014-11-08 22:57:37 +09:00
|
|
|
if server_index == 0
|
2014-08-23 20:22:31 +09:00
|
|
|
return server[:server]
|
|
|
|
else
|
|
|
|
return server[:aliases][server_index - 1]
|
|
|
|
end
|
2010-10-12 06:27:47 +00:00
|
|
|
end
|
|
|
|
|
2014-08-23 20:22:31 +09:00
|
|
|
server[:server]
|
|
|
|
end
|
2010-04-20 23:05:11 +00:00
|
|
|
else
|
|
|
|
def select_main_image_server
|
2014-08-23 16:56:00 +09:00
|
|
|
CONFIG["url_base"]
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
2014-08-23 20:19:29 +09:00
|
|
|
|
2014-08-23 20:03:11 +09:00
|
|
|
def select_image_server(_is_warehoused, _seed = 0, _options = {})
|
2014-08-23 16:56:00 +09:00
|
|
|
CONFIG["url_base"]
|
2010-04-20 23:05:11 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
module_function :select_main_image_server
|
|
|
|
module_function :select_image_server
|
|
|
|
end
|