app/services/startup_check.rb
Rama develop · réplica de lectura del repositorio original.
class StartupCheck
Check = Data.define(:name, :message)
class << self
def run
checks = []
checks << check_data_path
checks << check_compose_file
checks << check_compose_project_name
checks << check_env_file
checks << check_data_writable
if Rails.env.production?
checks << check_docker_socket
checks << check_docker_connection
checks << check_docker_version
end
checks.compact
end
private
def data_path
Rails.configuration.data_path
end
def check_data_path
return if File.directory?(data_path)
Check.new(
name: 'Data path',
message:
"Directory `#{data_path}` does not exist. " \
"Add `- .:#{data_path}` to the volumes.",
)
end
def check_compose_file
return unless File.directory?(data_path)
compose_exists =
Compose::FILENAMES.any? do |filename|
File.exist?(File.join(data_path, filename))
end
return if compose_exists
Check.new(
name: 'Compose file',
message:
"No compose file found in `#{data_path}`. " \
"The volume should point to the directory containing #{Compose::FILENAMES.first}.",
)
end
def check_compose_project_name
return unless File.directory?(data_path)
compose_file = existing_compose_file
return unless compose_file
name = (YAML.safe_load_file(compose_file) || {})['name']
return if name == Orchestration::PROJECT_NAME
Check.new(name: 'Compose project name', message: compose_project_name_message(compose_file, name))
rescue Psych::SyntaxError => e
Check.new(name: 'Compose project name', message: "Could not parse compose file: #{e.message}")
end
def existing_compose_file
Compose::FILENAMES.map { |filename| File.join(data_path, filename) }.find { |path| File.exist?(path) }
end
def compose_project_name_message(compose_file, name)
line = "`name: #{Orchestration::PROJECT_NAME}`"
problem, fix =
if name
["`#{compose_file}` sets the project name `#{name}`, but HELIOS requires " \
"`#{Orchestration::PROJECT_NAME}`.", "change the line to #{line}"]
else
["`#{compose_file}` has no top-level project name.", "add #{line} as the first line of the file"]
end
"#{problem} Stop the stack with `docker compose down`, #{fix}, " \
'then start the stack with `docker compose up -d`.'
end
def check_env_file
return unless File.directory?(data_path)
return if File.exist?(File.join(data_path, '.env'))
Check.new(
name: 'Environment file',
message:
"No `.env` file found in `#{data_path}`. " \
'The volume should point to the directory containing the .env file.',
)
end
def check_data_writable
return unless File.directory?(data_path)
return if File.writable?(data_path)
Check.new(
name: 'Data path writable',
message: "Directory `#{data_path}` is not writable.",
)
end
def docker_socket
Orchestration::Connection::SOCKET_PATHS.find { |path| File.exist?(path) }
end
def check_docker_socket
return if docker_socket
Check.new(
name: 'Docker socket',
message:
'Docker socket not found. ' \
'Add `- /var/run/docker.sock:/var/run/docker.sock` to the volumes.',
)
end
def check_docker_connection
return unless docker_socket
Orchestration::Connection.configure!
return if Docker.ping == 'OK'
Check.new(
name: 'Docker connection',
message: 'Docker socket exists but connection failed.',
)
rescue StandardError => e
Check.new(
name: 'Docker connection',
message: "Docker socket exists but connection failed: #{e.message}",
)
end
def check_docker_version
version = Orchestration::Connection.engine_version
return if version.nil? # connection failure is reported by check_docker_connection
return if version >= Orchestration::Connection::MIN_ENGINE_VERSION
Check.new(
name: 'Docker version',
message:
"Docker Engine #{version} is too old. " \
"HELIOS requires #{Orchestration::Connection::MIN_ENGINE_VERSION} or newer.",
)
end
end
end