Merge branch '28080-system-checks' into 'master'
SystemCheck library for executing checks from a rake task See merge request !9173
This commit is contained in:
commit
d2f149539e
31 changed files with 1197 additions and 479 deletions
4
changelogs/unreleased/28080-system-checks.yml
Normal file
4
changelogs/unreleased/28080-system-checks.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Refactored gitlab:app:check into SystemCheck liberary and improve some checks
|
||||
merge_request: 9173
|
||||
author:
|
21
lib/system_check.rb
Normal file
21
lib/system_check.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Library to perform System Checks
|
||||
#
|
||||
# Every Check is implemented as its own class inherited from SystemCheck::BaseCheck
|
||||
# Execution coordination and boilerplate output is done by the SystemCheck::SimpleExecutor
|
||||
#
|
||||
# This structure decouples checks from Rake tasks and facilitates unit-testing
|
||||
module SystemCheck
|
||||
# Executes a bunch of checks for specified component
|
||||
#
|
||||
# @param [String] component name of the component relative to the checks being executed
|
||||
# @param [Array<BaseCheck>] checks classes of corresponding checks to be executed in the same order
|
||||
def self.run(component, checks = [])
|
||||
executor = SimpleExecutor.new(component)
|
||||
|
||||
checks.each do |check|
|
||||
executor << check
|
||||
end
|
||||
|
||||
executor.execute
|
||||
end
|
||||
end
|
17
lib/system_check/app/active_users_check.rb
Normal file
17
lib/system_check/app/active_users_check.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class ActiveUsersCheck < SystemCheck::BaseCheck
|
||||
set_name 'Active users:'
|
||||
|
||||
def multi_check
|
||||
active_users = User.active.count
|
||||
|
||||
if active_users > 0
|
||||
$stdout.puts active_users.to_s.color(:green)
|
||||
else
|
||||
$stdout.puts active_users.to_s.color(:red)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
lib/system_check/app/database_config_exists_check.rb
Normal file
25
lib/system_check/app/database_config_exists_check.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class DatabaseConfigExistsCheck < SystemCheck::BaseCheck
|
||||
set_name 'Database config exists?'
|
||||
|
||||
def check?
|
||||
database_config_file = Rails.root.join('config', 'database.yml')
|
||||
|
||||
File.exist?(database_config_file)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'Copy config/database.yml.<your db> to config/database.yml',
|
||||
'Check that the information in config/database.yml is correct'
|
||||
)
|
||||
for_more_information(
|
||||
'doc/install/databases.md',
|
||||
'http://guides.rubyonrails.org/getting_started.html#configuring-a-database'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
lib/system_check/app/git_config_check.rb
Normal file
42
lib/system_check/app/git_config_check.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class GitConfigCheck < SystemCheck::BaseCheck
|
||||
OPTIONS = {
|
||||
'core.autocrlf' => 'input'
|
||||
}.freeze
|
||||
|
||||
set_name 'Git configured correctly?'
|
||||
|
||||
def check?
|
||||
correct_options = OPTIONS.map do |name, value|
|
||||
run_command(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
|
||||
end
|
||||
|
||||
correct_options.all?
|
||||
end
|
||||
|
||||
# Tries to configure git itself
|
||||
#
|
||||
# Returns true if all subcommands were successful (according to their exit code)
|
||||
# Returns false if any or all subcommands failed.
|
||||
def repair!
|
||||
return false unless is_gitlab_user?
|
||||
|
||||
command_success = OPTIONS.map do |name, value|
|
||||
system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
|
||||
end
|
||||
|
||||
command_success.all?
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{OPTIONS['core.autocrlf']}\"")
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
lib/system_check/app/git_version_check.rb
Normal file
29
lib/system_check/app/git_version_check.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class GitVersionCheck < SystemCheck::BaseCheck
|
||||
set_name -> { "Git version >= #{self.required_version} ?" }
|
||||
set_check_pass -> { "yes (#{self.current_version})" }
|
||||
|
||||
def self.required_version
|
||||
@required_version ||= Gitlab::VersionInfo.new(2, 7, 3)
|
||||
end
|
||||
|
||||
def self.current_version
|
||||
@current_version ||= Gitlab::VersionInfo.parse(run_command(%W(#{Gitlab.config.git.bin_path} --version)))
|
||||
end
|
||||
|
||||
def check?
|
||||
self.class.current_version.valid? && self.class.required_version <= self.class.current_version
|
||||
end
|
||||
|
||||
def show_error
|
||||
$stdout.puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
|
||||
|
||||
try_fixing_it(
|
||||
"Update your git to a version >= #{self.class.required_version} from #{self.class.current_version}"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
24
lib/system_check/app/gitlab_config_exists_check.rb
Normal file
24
lib/system_check/app/gitlab_config_exists_check.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class GitlabConfigExistsCheck < SystemCheck::BaseCheck
|
||||
set_name 'GitLab config exists?'
|
||||
|
||||
def check?
|
||||
gitlab_config_file = Rails.root.join('config', 'gitlab.yml')
|
||||
|
||||
File.exist?(gitlab_config_file)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'Copy config/gitlab.yml.example to config/gitlab.yml',
|
||||
'Update config/gitlab.yml to match your setup'
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
lib/system_check/app/gitlab_config_up_to_date_check.rb
Normal file
30
lib/system_check/app/gitlab_config_up_to_date_check.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class GitlabConfigUpToDateCheck < SystemCheck::BaseCheck
|
||||
set_name 'GitLab config up to date?'
|
||||
set_skip_reason "can't check because of previous errors"
|
||||
|
||||
def skip?
|
||||
gitlab_config_file = Rails.root.join('config', 'gitlab.yml')
|
||||
!File.exist?(gitlab_config_file)
|
||||
end
|
||||
|
||||
def check?
|
||||
# omniauth or ldap could have been deleted from the file
|
||||
!Gitlab.config['git_host']
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'Back-up your config/gitlab.yml',
|
||||
'Copy config/gitlab.yml.example to config/gitlab.yml',
|
||||
'Update config/gitlab.yml to match your setup'
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
lib/system_check/app/init_script_exists_check.rb
Normal file
27
lib/system_check/app/init_script_exists_check.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class InitScriptExistsCheck < SystemCheck::BaseCheck
|
||||
set_name 'Init script exists?'
|
||||
set_skip_reason 'skipped (omnibus-gitlab has no init script)'
|
||||
|
||||
def skip?
|
||||
omnibus_gitlab?
|
||||
end
|
||||
|
||||
def check?
|
||||
script_path = '/etc/init.d/gitlab'
|
||||
File.exist?(script_path)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'Install the init script'
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'Install Init Script'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
43
lib/system_check/app/init_script_up_to_date_check.rb
Normal file
43
lib/system_check/app/init_script_up_to_date_check.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class InitScriptUpToDateCheck < SystemCheck::BaseCheck
|
||||
SCRIPT_PATH = '/etc/init.d/gitlab'.freeze
|
||||
|
||||
set_name 'Init script up-to-date?'
|
||||
set_skip_reason 'skipped (omnibus-gitlab has no init script)'
|
||||
|
||||
def skip?
|
||||
omnibus_gitlab?
|
||||
end
|
||||
|
||||
def multi_check
|
||||
recipe_path = Rails.root.join('lib/support/init.d/', 'gitlab')
|
||||
|
||||
unless File.exist?(SCRIPT_PATH)
|
||||
$stdout.puts "can't check because of previous errors".color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
recipe_content = File.read(recipe_path)
|
||||
script_content = File.read(SCRIPT_PATH)
|
||||
|
||||
if recipe_content == script_content
|
||||
$stdout.puts 'yes'.color(:green)
|
||||
else
|
||||
$stdout.puts 'no'.color(:red)
|
||||
show_error
|
||||
end
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'Re-download the init script'
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'Install Init Script'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
lib/system_check/app/log_writable_check.rb
Normal file
28
lib/system_check/app/log_writable_check.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class LogWritableCheck < SystemCheck::BaseCheck
|
||||
set_name 'Log directory writable?'
|
||||
|
||||
def check?
|
||||
File.writable?(log_path)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"sudo chown -R gitlab #{log_path}",
|
||||
"sudo chmod -R u+rwX #{log_path}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_path
|
||||
Rails.root.join('log')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
20
lib/system_check/app/migrations_are_up_check.rb
Normal file
20
lib/system_check/app/migrations_are_up_check.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class MigrationsAreUpCheck < SystemCheck::BaseCheck
|
||||
set_name 'All migrations up?'
|
||||
|
||||
def check?
|
||||
migration_status, _ = Gitlab::Popen.popen(%w(bundle exec rake db:migrate:status))
|
||||
|
||||
migration_status !~ /down\s+\d{14}/
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
sudo_gitlab('bundle exec rake db:migrate RAILS_ENV=production')
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
20
lib/system_check/app/orphaned_group_members_check.rb
Normal file
20
lib/system_check/app/orphaned_group_members_check.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class OrphanedGroupMembersCheck < SystemCheck::BaseCheck
|
||||
set_name 'Database contains orphaned GroupMembers?'
|
||||
set_check_pass 'no'
|
||||
set_check_fail 'yes'
|
||||
|
||||
def check?
|
||||
!GroupMember.where('user_id not in (select id from users)').exists?
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
'You can delete the orphaned records using something along the lines of:',
|
||||
sudo_gitlab("bundle exec rails runner -e production 'GroupMember.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
37
lib/system_check/app/projects_have_namespace_check.rb
Normal file
37
lib/system_check/app/projects_have_namespace_check.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class ProjectsHaveNamespaceCheck < SystemCheck::BaseCheck
|
||||
set_name 'Projects have namespace:'
|
||||
set_skip_reason "can't check, you have no projects"
|
||||
|
||||
def skip?
|
||||
!Project.exists?
|
||||
end
|
||||
|
||||
def multi_check
|
||||
$stdout.puts ''
|
||||
|
||||
Project.find_each(batch_size: 100) do |project|
|
||||
$stdout.print sanitized_message(project)
|
||||
|
||||
if project.namespace
|
||||
$stdout.puts 'yes'.color(:green)
|
||||
else
|
||||
$stdout.puts 'no'.color(:red)
|
||||
show_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"Migrate global projects"
|
||||
)
|
||||
for_more_information(
|
||||
"doc/update/5.4-to-6.0.md in section \"#global-projects\""
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
lib/system_check/app/redis_version_check.rb
Normal file
25
lib/system_check/app/redis_version_check.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class RedisVersionCheck < SystemCheck::BaseCheck
|
||||
MIN_REDIS_VERSION = '2.8.0'.freeze
|
||||
set_name "Redis version >= #{MIN_REDIS_VERSION}?"
|
||||
|
||||
def check?
|
||||
redis_version = run_command(%w(redis-cli --version))
|
||||
redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
|
||||
|
||||
redis_version && (Gem::Version.new(redis_version[1]) > Gem::Version.new(MIN_REDIS_VERSION))
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"Update your redis server to a version >= #{MIN_REDIS_VERSION}"
|
||||
)
|
||||
for_more_information(
|
||||
'gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
27
lib/system_check/app/ruby_version_check.rb
Normal file
27
lib/system_check/app/ruby_version_check.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class RubyVersionCheck < SystemCheck::BaseCheck
|
||||
set_name -> { "Ruby version >= #{self.required_version} ?" }
|
||||
set_check_pass -> { "yes (#{self.current_version})" }
|
||||
|
||||
def self.required_version
|
||||
@required_version ||= Gitlab::VersionInfo.new(2, 3, 3)
|
||||
end
|
||||
|
||||
def self.current_version
|
||||
@current_version ||= Gitlab::VersionInfo.parse(run_command(%w(ruby --version)))
|
||||
end
|
||||
|
||||
def check?
|
||||
self.class.current_version.valid? && self.class.required_version <= self.class.current_version
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"Update your ruby to a version >= #{self.class.required_version} from #{self.class.current_version}"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
lib/system_check/app/tmp_writable_check.rb
Normal file
28
lib/system_check/app/tmp_writable_check.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class TmpWritableCheck < SystemCheck::BaseCheck
|
||||
set_name 'Tmp directory writable?'
|
||||
|
||||
def check?
|
||||
File.writable?(tmp_path)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"sudo chown -R gitlab #{tmp_path}",
|
||||
"sudo chmod -R u+rwX #{tmp_path}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tmp_path
|
||||
Rails.root.join('tmp')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
lib/system_check/app/uploads_directory_exists_check.rb
Normal file
21
lib/system_check/app/uploads_directory_exists_check.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class UploadsDirectoryExistsCheck < SystemCheck::BaseCheck
|
||||
set_name 'Uploads directory exists?'
|
||||
|
||||
def check?
|
||||
File.directory?(Rails.root.join('public/uploads'))
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
lib/system_check/app/uploads_path_permission_check.rb
Normal file
36
lib/system_check/app/uploads_path_permission_check.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class UploadsPathPermissionCheck < SystemCheck::BaseCheck
|
||||
set_name 'Uploads directory has correct permissions?'
|
||||
set_skip_reason 'skipped (no uploads folder found)'
|
||||
|
||||
def skip?
|
||||
!File.directory?(rails_uploads_path)
|
||||
end
|
||||
|
||||
def check?
|
||||
File.stat(uploads_fullpath).mode == 040700
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"sudo chmod 700 #{uploads_fullpath}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rails_uploads_path
|
||||
Rails.root.join('public/uploads')
|
||||
end
|
||||
|
||||
def uploads_fullpath
|
||||
File.realpath(rails_uploads_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
40
lib/system_check/app/uploads_path_tmp_permission_check.rb
Normal file
40
lib/system_check/app/uploads_path_tmp_permission_check.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
module SystemCheck
|
||||
module App
|
||||
class UploadsPathTmpPermissionCheck < SystemCheck::BaseCheck
|
||||
set_name 'Uploads directory tmp has correct permissions?'
|
||||
set_skip_reason 'skipped (no tmp uploads folder yet)'
|
||||
|
||||
def skip?
|
||||
!File.directory?(uploads_fullpath) || !Dir.exist?(upload_path_tmp)
|
||||
end
|
||||
|
||||
def check?
|
||||
# If tmp upload dir has incorrect permissions, assume others do as well
|
||||
# Verify drwx------ permissions
|
||||
File.stat(upload_path_tmp).mode == 040700 && File.owned?(upload_path_tmp)
|
||||
end
|
||||
|
||||
def show_error
|
||||
try_fixing_it(
|
||||
"sudo chown -R #{gitlab_user} #{uploads_fullpath}",
|
||||
"sudo find #{uploads_fullpath} -type f -exec chmod 0644 {} \\;",
|
||||
"sudo find #{uploads_fullpath} -type d -not -path #{uploads_fullpath} -exec chmod 0700 {} \\;"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section 'GitLab'
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def upload_path_tmp
|
||||
File.join(uploads_fullpath, 'tmp')
|
||||
end
|
||||
|
||||
def uploads_fullpath
|
||||
File.realpath(Rails.root.join('public/uploads'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
129
lib/system_check/base_check.rb
Normal file
129
lib/system_check/base_check.rb
Normal file
|
@ -0,0 +1,129 @@
|
|||
module SystemCheck
|
||||
# Base class for Checks. You must inherit from here
|
||||
# and implement the methods below when necessary
|
||||
class BaseCheck
|
||||
include ::SystemCheck::Helpers
|
||||
|
||||
# Define a custom term for when check passed
|
||||
#
|
||||
# @param [String] term used when check passed (default: 'yes')
|
||||
def self.set_check_pass(term)
|
||||
@check_pass = term
|
||||
end
|
||||
|
||||
# Define a custom term for when check failed
|
||||
#
|
||||
# @param [String] term used when check failed (default: 'no')
|
||||
def self.set_check_fail(term)
|
||||
@check_fail = term
|
||||
end
|
||||
|
||||
# Define the name of the SystemCheck that will be displayed during execution
|
||||
#
|
||||
# @param [String] name of the check
|
||||
def self.set_name(name)
|
||||
@name = name
|
||||
end
|
||||
|
||||
# Define the reason why we skipped the SystemCheck
|
||||
#
|
||||
# This is only used if subclass implements `#skip?`
|
||||
#
|
||||
# @param [String] reason to be displayed
|
||||
def self.set_skip_reason(reason)
|
||||
@skip_reason = reason
|
||||
end
|
||||
|
||||
# Term to be displayed when check passed
|
||||
#
|
||||
# @return [String] term when check passed ('yes' if not re-defined in a subclass)
|
||||
def self.check_pass
|
||||
call_or_return(@check_pass) || 'yes'
|
||||
end
|
||||
|
||||
## Term to be displayed when check failed
|
||||
#
|
||||
# @return [String] term when check failed ('no' if not re-defined in a subclass)
|
||||
def self.check_fail
|
||||
call_or_return(@check_fail) || 'no'
|
||||
end
|
||||
|
||||
# Name of the SystemCheck defined by the subclass
|
||||
#
|
||||
# @return [String] the name
|
||||
def self.display_name
|
||||
call_or_return(@name) || self.name
|
||||
end
|
||||
|
||||
# Skip reason defined by the subclass
|
||||
#
|
||||
# @return [String] the reason
|
||||
def self.skip_reason
|
||||
call_or_return(@skip_reason) || 'skipped'
|
||||
end
|
||||
|
||||
# Does the check support automatically repair routine?
|
||||
#
|
||||
# @return [Boolean] whether check implemented `#repair!` method or not
|
||||
def can_repair?
|
||||
self.class.instance_methods(false).include?(:repair!)
|
||||
end
|
||||
|
||||
def can_skip?
|
||||
self.class.instance_methods(false).include?(:skip?)
|
||||
end
|
||||
|
||||
def is_multi_check?
|
||||
self.class.instance_methods(false).include?(:multi_check)
|
||||
end
|
||||
|
||||
# Execute the check routine
|
||||
#
|
||||
# This is where you should implement the main logic that will return
|
||||
# a boolean at the end
|
||||
#
|
||||
# You should not print any output to STDOUT here, use the specific methods instead
|
||||
#
|
||||
# @return [Boolean] whether check passed or failed
|
||||
def check?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Execute a custom check that cover multiple unities
|
||||
#
|
||||
# When using multi_check you have to provide the output yourself
|
||||
def multi_check
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Prints troubleshooting instructions
|
||||
#
|
||||
# This is where you should print detailed information for any error found during #check?
|
||||
#
|
||||
# You may use helper methods to help format the output:
|
||||
#
|
||||
# @see #try_fixing_it
|
||||
# @see #fix_and_rerun
|
||||
# @see #for_more_infromation
|
||||
def show_error
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# When implemented by a subclass, will attempt to fix the issue automatically
|
||||
def repair!
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# When implemented by a subclass, will evaluate whether check should be skipped or not
|
||||
#
|
||||
# @return [Boolean] whether or not this check should be skipped
|
||||
def skip?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.call_or_return(input)
|
||||
input.respond_to?(:call) ? input.call : input
|
||||
end
|
||||
private_class_method :call_or_return
|
||||
end
|
||||
end
|
75
lib/system_check/helpers.rb
Normal file
75
lib/system_check/helpers.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
require 'tasks/gitlab/task_helpers'
|
||||
|
||||
module SystemCheck
|
||||
module Helpers
|
||||
include ::Gitlab::TaskHelpers
|
||||
|
||||
# Display a message telling to fix and rerun the checks
|
||||
def fix_and_rerun
|
||||
$stdout.puts ' Please fix the error above and rerun the checks.'.color(:red)
|
||||
end
|
||||
|
||||
# Display a formatted list of references (documentation or links) where to find more information
|
||||
#
|
||||
# @param [Array<String>] sources one or more references (documentation or links)
|
||||
def for_more_information(*sources)
|
||||
$stdout.puts ' For more information see:'.color(:blue)
|
||||
sources.each do |source|
|
||||
$stdout.puts " #{source}"
|
||||
end
|
||||
end
|
||||
|
||||
def see_installation_guide_section(section)
|
||||
"doc/install/installation.md in section \"#{section}\""
|
||||
end
|
||||
|
||||
# @deprecated This will no longer be used when all checks were executed using SystemCheck
|
||||
def finished_checking(component)
|
||||
$stdout.puts ''
|
||||
$stdout.puts "Checking #{component.color(:yellow)} ... #{'Finished'.color(:green)}"
|
||||
$stdout.puts ''
|
||||
end
|
||||
|
||||
# @deprecated This will no longer be used when all checks were executed using SystemCheck
|
||||
def start_checking(component)
|
||||
$stdout.puts "Checking #{component.color(:yellow)} ..."
|
||||
$stdout.puts ''
|
||||
end
|
||||
|
||||
# Display a formatted list of instructions on how to fix the issue identified by the #check?
|
||||
#
|
||||
# @param [Array<String>] steps one or short sentences with help how to fix the issue
|
||||
def try_fixing_it(*steps)
|
||||
steps = steps.shift if steps.first.is_a?(Array)
|
||||
|
||||
$stdout.puts ' Try fixing it:'.color(:blue)
|
||||
steps.each do |step|
|
||||
$stdout.puts " #{step}"
|
||||
end
|
||||
end
|
||||
|
||||
def sanitized_message(project)
|
||||
if should_sanitize?
|
||||
"#{project.namespace_id.to_s.color(:yellow)}/#{project.id.to_s.color(:yellow)} ... "
|
||||
else
|
||||
"#{project.name_with_namespace.color(:yellow)} ... "
|
||||
end
|
||||
end
|
||||
|
||||
def should_sanitize?
|
||||
if ENV['SANITIZE'] == 'true'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def omnibus_gitlab?
|
||||
Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails'
|
||||
end
|
||||
|
||||
def sudo_gitlab(command)
|
||||
"sudo -u #{gitlab_user} -H #{command}"
|
||||
end
|
||||
end
|
||||
end
|
99
lib/system_check/simple_executor.rb
Normal file
99
lib/system_check/simple_executor.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
module SystemCheck
|
||||
# Simple Executor is current default executor for GitLab
|
||||
# It is a simple port from display logic in the old check.rake
|
||||
#
|
||||
# There is no concurrency level and the output is progressively
|
||||
# printed into the STDOUT
|
||||
#
|
||||
# @attr_reader [Array<BaseCheck>] checks classes of corresponding checks to be executed in the same order
|
||||
# @attr_reader [String] component name of the component relative to the checks being executed
|
||||
class SimpleExecutor
|
||||
attr_reader :checks
|
||||
attr_reader :component
|
||||
|
||||
# @param [String] component name of the component relative to the checks being executed
|
||||
def initialize(component)
|
||||
raise ArgumentError unless component.is_a? String
|
||||
|
||||
@component = component
|
||||
@checks = Set.new
|
||||
end
|
||||
|
||||
# Add a check to be executed
|
||||
#
|
||||
# @param [BaseCheck] check class
|
||||
def <<(check)
|
||||
raise ArgumentError unless check < BaseCheck
|
||||
@checks << check
|
||||
end
|
||||
|
||||
# Executes defined checks in the specified order and outputs confirmation or error information
|
||||
def execute
|
||||
start_checking(component)
|
||||
|
||||
@checks.each do |check|
|
||||
run_check(check)
|
||||
end
|
||||
|
||||
finished_checking(component)
|
||||
end
|
||||
|
||||
# Executes a single check
|
||||
#
|
||||
# @param [SystemCheck::BaseCheck] check_klass
|
||||
def run_check(check_klass)
|
||||
$stdout.print "#{check_klass.display_name} ... "
|
||||
|
||||
check = check_klass.new
|
||||
|
||||
# When implements skip method, we run it first, and if true, skip the check
|
||||
if check.can_skip? && check.skip?
|
||||
$stdout.puts check_klass.skip_reason.color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
# When implements a multi check, we don't control the output
|
||||
if check.is_multi_check?
|
||||
check.multi_check
|
||||
return
|
||||
end
|
||||
|
||||
if check.check?
|
||||
$stdout.puts check_klass.check_pass.color(:green)
|
||||
else
|
||||
$stdout.puts check_klass.check_fail.color(:red)
|
||||
|
||||
if check.can_repair?
|
||||
$stdout.print 'Trying to fix error automatically. ...'
|
||||
if check.repair!
|
||||
$stdout.puts 'Success'.color(:green)
|
||||
return
|
||||
else
|
||||
$stdout.puts 'Failed'.color(:red)
|
||||
end
|
||||
end
|
||||
|
||||
check.show_error
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Prints header content for the series of checks to be executed for this component
|
||||
#
|
||||
# @param [String] component name of the component relative to the checks being executed
|
||||
def start_checking(component)
|
||||
$stdout.puts "Checking #{component.color(:yellow)} ..."
|
||||
$stdout.puts ''
|
||||
end
|
||||
|
||||
# Prints footer content for the series of checks executed for this component
|
||||
#
|
||||
# @param [String] component name of the component relative to the checks being executed
|
||||
def finished_checking(component)
|
||||
$stdout.puts ''
|
||||
$stdout.puts "Checking #{component.color(:yellow)} ... #{'Finished'.color(:green)}"
|
||||
$stdout.puts ''
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,9 @@
|
|||
# Temporary hack, until we migrate all checks to SystemCheck format
|
||||
require 'system_check'
|
||||
require 'system_check/helpers'
|
||||
|
||||
namespace :gitlab do
|
||||
desc "GitLab | Check the configuration of GitLab and its environment"
|
||||
desc 'GitLab | Check the configuration of GitLab and its environment'
|
||||
task check: %w{gitlab:gitlab_shell:check
|
||||
gitlab:sidekiq:check
|
||||
gitlab:incoming_email:check
|
||||
|
@ -7,331 +11,38 @@ namespace :gitlab do
|
|||
gitlab:app:check}
|
||||
|
||||
namespace :app do
|
||||
desc "GitLab | Check the configuration of the GitLab Rails app"
|
||||
desc 'GitLab | Check the configuration of the GitLab Rails app'
|
||||
task check: :environment do
|
||||
warn_user_is_not_gitlab
|
||||
start_checking "GitLab"
|
||||
|
||||
check_git_config
|
||||
check_database_config_exists
|
||||
check_migrations_are_up
|
||||
check_orphaned_group_members
|
||||
check_gitlab_config_exists
|
||||
check_gitlab_config_not_outdated
|
||||
check_log_writable
|
||||
check_tmp_writable
|
||||
check_uploads
|
||||
check_init_script_exists
|
||||
check_init_script_up_to_date
|
||||
check_projects_have_namespace
|
||||
check_redis_version
|
||||
check_ruby_version
|
||||
check_git_version
|
||||
check_active_users
|
||||
checks = [
|
||||
SystemCheck::App::GitConfigCheck,
|
||||
SystemCheck::App::DatabaseConfigExistsCheck,
|
||||
SystemCheck::App::MigrationsAreUpCheck,
|
||||
SystemCheck::App::OrphanedGroupMembersCheck,
|
||||
SystemCheck::App::GitlabConfigExistsCheck,
|
||||
SystemCheck::App::GitlabConfigUpToDateCheck,
|
||||
SystemCheck::App::LogWritableCheck,
|
||||
SystemCheck::App::TmpWritableCheck,
|
||||
SystemCheck::App::UploadsDirectoryExistsCheck,
|
||||
SystemCheck::App::UploadsPathPermissionCheck,
|
||||
SystemCheck::App::UploadsPathTmpPermissionCheck,
|
||||
SystemCheck::App::InitScriptExistsCheck,
|
||||
SystemCheck::App::InitScriptUpToDateCheck,
|
||||
SystemCheck::App::ProjectsHaveNamespaceCheck,
|
||||
SystemCheck::App::RedisVersionCheck,
|
||||
SystemCheck::App::RubyVersionCheck,
|
||||
SystemCheck::App::GitVersionCheck,
|
||||
SystemCheck::App::ActiveUsersCheck
|
||||
]
|
||||
|
||||
finished_checking "GitLab"
|
||||
end
|
||||
|
||||
# Checks
|
||||
########################
|
||||
|
||||
def check_git_config
|
||||
print "Git configured with autocrlf=input? ... "
|
||||
|
||||
options = {
|
||||
"core.autocrlf" => "input"
|
||||
}
|
||||
|
||||
correct_options = options.map do |name, value|
|
||||
run_command(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
|
||||
end
|
||||
|
||||
if correct_options.all?
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
print "Trying to fix Git error automatically. ..."
|
||||
|
||||
if auto_fix_git_config(options)
|
||||
puts "Success".color(:green)
|
||||
else
|
||||
puts "Failed".color(:red)
|
||||
try_fixing_it(
|
||||
sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"")
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_database_config_exists
|
||||
print "Database config exists? ... "
|
||||
|
||||
database_config_file = Rails.root.join("config", "database.yml")
|
||||
|
||||
if File.exist?(database_config_file)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Copy config/database.yml.<your db> to config/database.yml",
|
||||
"Check that the information in config/database.yml is correct"
|
||||
)
|
||||
for_more_information(
|
||||
see_database_guide,
|
||||
"http://guides.rubyonrails.org/getting_started.html#configuring-a-database"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_gitlab_config_exists
|
||||
print "GitLab config exists? ... "
|
||||
|
||||
gitlab_config_file = Rails.root.join("config", "gitlab.yml")
|
||||
|
||||
if File.exist?(gitlab_config_file)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Copy config/gitlab.yml.example to config/gitlab.yml",
|
||||
"Update config/gitlab.yml to match your setup"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_gitlab_config_not_outdated
|
||||
print "GitLab config outdated? ... "
|
||||
|
||||
gitlab_config_file = Rails.root.join("config", "gitlab.yml")
|
||||
unless File.exist?(gitlab_config_file)
|
||||
puts "can't check because of previous errors".color(:magenta)
|
||||
end
|
||||
|
||||
# omniauth or ldap could have been deleted from the file
|
||||
unless Gitlab.config['git_host']
|
||||
puts "no".color(:green)
|
||||
else
|
||||
puts "yes".color(:red)
|
||||
try_fixing_it(
|
||||
"Backup your config/gitlab.yml",
|
||||
"Copy config/gitlab.yml.example to config/gitlab.yml",
|
||||
"Update config/gitlab.yml to match your setup"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_init_script_exists
|
||||
print "Init script exists? ... "
|
||||
|
||||
if omnibus_gitlab?
|
||||
puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
script_path = "/etc/init.d/gitlab"
|
||||
|
||||
if File.exist?(script_path)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Install the init script"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "Install Init Script"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_init_script_up_to_date
|
||||
print "Init script up-to-date? ... "
|
||||
|
||||
if omnibus_gitlab?
|
||||
puts 'skipped (omnibus-gitlab has no init script)'.color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
recipe_path = Rails.root.join("lib/support/init.d/", "gitlab")
|
||||
script_path = "/etc/init.d/gitlab"
|
||||
|
||||
unless File.exist?(script_path)
|
||||
puts "can't check because of previous errors".color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
recipe_content = File.read(recipe_path)
|
||||
script_content = File.read(script_path)
|
||||
|
||||
if recipe_content == script_content
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Redownload the init script"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "Install Init Script"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_migrations_are_up
|
||||
print "All migrations up? ... "
|
||||
|
||||
migration_status, _ = Gitlab::Popen.popen(%w(bundle exec rake db:migrate:status))
|
||||
|
||||
unless migration_status =~ /down\s+\d{14}/
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production")
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_orphaned_group_members
|
||||
print "Database contains orphaned GroupMembers? ... "
|
||||
if GroupMember.where("user_id not in (select id from users)").count > 0
|
||||
puts "yes".color(:red)
|
||||
try_fixing_it(
|
||||
"You can delete the orphaned records using something along the lines of:",
|
||||
sudo_gitlab("bundle exec rails runner -e production 'GroupMember.where(\"user_id NOT IN (SELECT id FROM users)\").delete_all'")
|
||||
)
|
||||
else
|
||||
puts "no".color(:green)
|
||||
end
|
||||
end
|
||||
|
||||
def check_log_writable
|
||||
print "Log directory writable? ... "
|
||||
|
||||
log_path = Rails.root.join("log")
|
||||
|
||||
if File.writable?(log_path)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"sudo chown -R gitlab #{log_path}",
|
||||
"sudo chmod -R u+rwX #{log_path}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_tmp_writable
|
||||
print "Tmp directory writable? ... "
|
||||
|
||||
tmp_path = Rails.root.join("tmp")
|
||||
|
||||
if File.writable?(tmp_path)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"sudo chown -R gitlab #{tmp_path}",
|
||||
"sudo chmod -R u+rwX #{tmp_path}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_uploads
|
||||
print "Uploads directory setup correctly? ... "
|
||||
|
||||
unless File.directory?(Rails.root.join('public/uploads'))
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"sudo -u #{gitlab_user} mkdir #{Rails.root}/public/uploads"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
return
|
||||
end
|
||||
|
||||
upload_path = File.realpath(Rails.root.join('public/uploads'))
|
||||
upload_path_tmp = File.join(upload_path, 'tmp')
|
||||
|
||||
if File.stat(upload_path).mode == 040700
|
||||
unless Dir.exist?(upload_path_tmp)
|
||||
puts 'skipped (no tmp uploads folder yet)'.color(:magenta)
|
||||
return
|
||||
end
|
||||
|
||||
# If tmp upload dir has incorrect permissions, assume others do as well
|
||||
# Verify drwx------ permissions
|
||||
if File.stat(upload_path_tmp).mode == 040700 && File.owned?(upload_path_tmp)
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"sudo chown -R #{gitlab_user} #{upload_path}",
|
||||
"sudo find #{upload_path} -type f -exec chmod 0644 {} \\;",
|
||||
"sudo find #{upload_path} -type d -not -path #{upload_path} -exec chmod 0700 {} \\;"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"sudo chmod 700 #{upload_path}"
|
||||
)
|
||||
for_more_information(
|
||||
see_installation_guide_section "GitLab"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_redis_version
|
||||
min_redis_version = "2.8.0"
|
||||
print "Redis version >= #{min_redis_version}? ... "
|
||||
|
||||
redis_version = run_command(%w(redis-cli --version))
|
||||
redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
|
||||
if redis_version &&
|
||||
(Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version))
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Update your redis server to a version >= #{min_redis_version}"
|
||||
)
|
||||
for_more_information(
|
||||
"gitlab-public-wiki/wiki/Trouble-Shooting-Guide in section sidekiq"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
SystemCheck.run('GitLab', checks)
|
||||
end
|
||||
end
|
||||
|
||||
namespace :gitlab_shell do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
desc "GitLab | Check the configuration of GitLab Shell"
|
||||
task check: :environment do
|
||||
warn_user_is_not_gitlab
|
||||
|
@ -513,33 +224,6 @@ namespace :gitlab do
|
|||
end
|
||||
end
|
||||
|
||||
def check_projects_have_namespace
|
||||
print "projects have namespace: ... "
|
||||
|
||||
unless Project.count > 0
|
||||
puts "can't check, you have no projects".color(:magenta)
|
||||
return
|
||||
end
|
||||
puts ""
|
||||
|
||||
Project.find_each(batch_size: 100) do |project|
|
||||
print sanitized_message(project)
|
||||
|
||||
if project.namespace
|
||||
puts "yes".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Migrate global projects"
|
||||
)
|
||||
for_more_information(
|
||||
"doc/update/5.4-to-6.0.md in section \"#global-projects\""
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Helper methods
|
||||
########################
|
||||
|
||||
|
@ -565,6 +249,8 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :sidekiq do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
desc "GitLab | Check the configuration of Sidekiq"
|
||||
task check: :environment do
|
||||
warn_user_is_not_gitlab
|
||||
|
@ -623,6 +309,8 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :incoming_email do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
desc "GitLab | Check the configuration of Reply by email"
|
||||
task check: :environment do
|
||||
warn_user_is_not_gitlab
|
||||
|
@ -757,6 +445,8 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :ldap do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
task :check, [:limit] => :environment do |_, args|
|
||||
# Only show up to 100 results because LDAP directories can be very big.
|
||||
# This setting only affects the `rake gitlab:check` script.
|
||||
|
@ -812,6 +502,8 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :repo do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
desc "GitLab | Check the integrity of the repositories managed by GitLab"
|
||||
task check: :environment do
|
||||
Gitlab.config.repositories.storages.each do |name, repository_storage|
|
||||
|
@ -826,6 +518,8 @@ namespace :gitlab do
|
|||
end
|
||||
|
||||
namespace :user do
|
||||
include SystemCheck::Helpers
|
||||
|
||||
desc "GitLab | Check the integrity of a specific user's repositories"
|
||||
task :check_repos, [:username] => :environment do |t, args|
|
||||
username = args[:username] || prompt("Check repository integrity for fsername? ".color(:blue))
|
||||
|
@ -848,55 +542,6 @@ namespace :gitlab do
|
|||
# Helper methods
|
||||
##########################
|
||||
|
||||
def fix_and_rerun
|
||||
puts " Please fix the error above and rerun the checks.".color(:red)
|
||||
end
|
||||
|
||||
def for_more_information(*sources)
|
||||
sources = sources.shift if sources.first.is_a?(Array)
|
||||
|
||||
puts " For more information see:".color(:blue)
|
||||
sources.each do |source|
|
||||
puts " #{source}"
|
||||
end
|
||||
end
|
||||
|
||||
def finished_checking(component)
|
||||
puts ""
|
||||
puts "Checking #{component.color(:yellow)} ... #{"Finished".color(:green)}"
|
||||
puts ""
|
||||
end
|
||||
|
||||
def see_database_guide
|
||||
"doc/install/databases.md"
|
||||
end
|
||||
|
||||
def see_installation_guide_section(section)
|
||||
"doc/install/installation.md in section \"#{section}\""
|
||||
end
|
||||
|
||||
def sudo_gitlab(command)
|
||||
"sudo -u #{gitlab_user} -H #{command}"
|
||||
end
|
||||
|
||||
def gitlab_user
|
||||
Gitlab.config.gitlab.user
|
||||
end
|
||||
|
||||
def start_checking(component)
|
||||
puts "Checking #{component.color(:yellow)} ..."
|
||||
puts ""
|
||||
end
|
||||
|
||||
def try_fixing_it(*steps)
|
||||
steps = steps.shift if steps.first.is_a?(Array)
|
||||
|
||||
puts " Try fixing it:".color(:blue)
|
||||
steps.each do |step|
|
||||
puts " #{step}"
|
||||
end
|
||||
end
|
||||
|
||||
def check_gitlab_shell
|
||||
required_version = Gitlab::VersionInfo.new(gitlab_shell_major_version, gitlab_shell_minor_version, gitlab_shell_patch_version)
|
||||
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
|
||||
|
@ -909,65 +554,6 @@ namespace :gitlab do
|
|||
end
|
||||
end
|
||||
|
||||
def check_ruby_version
|
||||
required_version = Gitlab::VersionInfo.new(2, 1, 0)
|
||||
current_version = Gitlab::VersionInfo.parse(run_command(%w(ruby --version)))
|
||||
|
||||
print "Ruby version >= #{required_version} ? ... "
|
||||
|
||||
if current_version.valid? && required_version <= current_version
|
||||
puts "yes (#{current_version})".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Update your ruby to a version >= #{required_version} from #{current_version}"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_git_version
|
||||
required_version = Gitlab::VersionInfo.new(2, 7, 3)
|
||||
current_version = Gitlab::VersionInfo.parse(run_command(%W(#{Gitlab.config.git.bin_path} --version)))
|
||||
|
||||
puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
|
||||
print "Git version >= #{required_version} ? ... "
|
||||
|
||||
if current_version.valid? && required_version <= current_version
|
||||
puts "yes (#{current_version})".color(:green)
|
||||
else
|
||||
puts "no".color(:red)
|
||||
try_fixing_it(
|
||||
"Update your git to a version >= #{required_version} from #{current_version}"
|
||||
)
|
||||
fix_and_rerun
|
||||
end
|
||||
end
|
||||
|
||||
def check_active_users
|
||||
puts "Active users: #{User.active.count}"
|
||||
end
|
||||
|
||||
def omnibus_gitlab?
|
||||
Dir.pwd == '/opt/gitlab/embedded/service/gitlab-rails'
|
||||
end
|
||||
|
||||
def sanitized_message(project)
|
||||
if should_sanitize?
|
||||
"#{project.namespace_id.to_s.color(:yellow)}/#{project.id.to_s.color(:yellow)} ... "
|
||||
else
|
||||
"#{project.name_with_namespace.color(:yellow)} ... "
|
||||
end
|
||||
end
|
||||
|
||||
def should_sanitize?
|
||||
if ENV['SANITIZE'] == "true"
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def check_repo_integrity(repo_dir)
|
||||
puts "\nChecking repo at #{repo_dir.color(:yellow)}"
|
||||
|
||||
|
|
|
@ -98,37 +98,33 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def warn_user_is_not_gitlab
|
||||
unless @warned_user_not_gitlab
|
||||
gitlab_user = Gitlab.config.gitlab.user
|
||||
def gitlab_user
|
||||
Gitlab.config.gitlab.user
|
||||
end
|
||||
|
||||
def is_gitlab_user?
|
||||
return @is_gitlab_user unless @is_gitlab_user.nil?
|
||||
|
||||
current_user = run_command(%w(whoami)).chomp
|
||||
unless current_user == gitlab_user
|
||||
@is_gitlab_user = current_user == gitlab_user
|
||||
end
|
||||
|
||||
def warn_user_is_not_gitlab
|
||||
return if @warned_user_not_gitlab
|
||||
|
||||
unless is_gitlab_user?
|
||||
current_user = run_command(%w(whoami)).chomp
|
||||
|
||||
puts " Warning ".color(:black).background(:yellow)
|
||||
puts " You are running as user #{current_user.color(:magenta)}, we hope you know what you are doing."
|
||||
puts " Things may work\/fail for the wrong reasons."
|
||||
puts " For correct results you should run this as user #{gitlab_user.color(:magenta)}."
|
||||
puts ""
|
||||
end
|
||||
|
||||
@warned_user_not_gitlab = true
|
||||
end
|
||||
end
|
||||
|
||||
# Tries to configure git itself
|
||||
#
|
||||
# Returns true if all subcommands were successfull (according to their exit code)
|
||||
# Returns false if any or all subcommands failed.
|
||||
def auto_fix_git_config(options)
|
||||
if !@warned_user_not_gitlab
|
||||
command_success = options.map do |name, value|
|
||||
system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
|
||||
end
|
||||
|
||||
command_success.all?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def all_repos
|
||||
Gitlab.config.repositories.storages.each_value do |repository_storage|
|
||||
IO.popen(%W(find #{repository_storage['path']} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'spec_helper'
|
||||
require 'rainbow/ext/string'
|
||||
|
||||
describe 'seed production settings', lib: true do
|
||||
include StubENV
|
||||
|
|
223
spec/lib/system_check/simple_executor_spec.rb
Normal file
223
spec/lib/system_check/simple_executor_spec.rb
Normal file
|
@ -0,0 +1,223 @@
|
|||
require 'spec_helper'
|
||||
require 'rake_helper'
|
||||
|
||||
describe SystemCheck::SimpleExecutor, lib: true do
|
||||
class SimpleCheck < SystemCheck::BaseCheck
|
||||
set_name 'my simple check'
|
||||
|
||||
def check?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class OtherCheck < SystemCheck::BaseCheck
|
||||
set_name 'other check'
|
||||
|
||||
def check?
|
||||
false
|
||||
end
|
||||
|
||||
def show_error
|
||||
$stdout.puts 'this is an error text'
|
||||
end
|
||||
end
|
||||
|
||||
class SkipCheck < SystemCheck::BaseCheck
|
||||
set_name 'skip check'
|
||||
set_skip_reason 'this is a skip reason'
|
||||
|
||||
def skip?
|
||||
true
|
||||
end
|
||||
|
||||
def check?
|
||||
raise 'should not execute this'
|
||||
end
|
||||
end
|
||||
|
||||
class MultiCheck < SystemCheck::BaseCheck
|
||||
set_name 'multi check'
|
||||
|
||||
def multi_check
|
||||
$stdout.puts 'this is a multi output check'
|
||||
end
|
||||
|
||||
def check?
|
||||
raise 'should not execute this'
|
||||
end
|
||||
end
|
||||
|
||||
class SkipMultiCheck < SystemCheck::BaseCheck
|
||||
set_name 'skip multi check'
|
||||
|
||||
def skip?
|
||||
true
|
||||
end
|
||||
|
||||
def multi_check
|
||||
raise 'should not execute this'
|
||||
end
|
||||
end
|
||||
|
||||
class RepairCheck < SystemCheck::BaseCheck
|
||||
set_name 'repair check'
|
||||
|
||||
def check?
|
||||
false
|
||||
end
|
||||
|
||||
def repair!
|
||||
true
|
||||
end
|
||||
|
||||
def show_error
|
||||
$stdout.puts 'this is an error message'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#component' do
|
||||
it 'returns stored component name' do
|
||||
expect(subject.component).to eq('Test')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#checks' do
|
||||
before do
|
||||
subject << SimpleCheck
|
||||
end
|
||||
|
||||
it 'returns a set of classes' do
|
||||
expect(subject.checks).to include(SimpleCheck)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#<<' do
|
||||
before do
|
||||
subject << SimpleCheck
|
||||
end
|
||||
|
||||
it 'appends a new check to the Set' do
|
||||
subject << OtherCheck
|
||||
stored_checks = subject.checks.to_a
|
||||
|
||||
expect(stored_checks.first).to eq(SimpleCheck)
|
||||
expect(stored_checks.last).to eq(OtherCheck)
|
||||
end
|
||||
|
||||
it 'inserts unique itens only' do
|
||||
subject << SimpleCheck
|
||||
|
||||
expect(subject.checks.size).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
subject { described_class.new('Test') }
|
||||
|
||||
describe '#execute' do
|
||||
before do
|
||||
silence_output
|
||||
|
||||
subject << SimpleCheck
|
||||
subject << OtherCheck
|
||||
end
|
||||
|
||||
it 'runs included checks' do
|
||||
expect(subject).to receive(:run_check).with(SimpleCheck)
|
||||
expect(subject).to receive(:run_check).with(OtherCheck)
|
||||
|
||||
subject.execute
|
||||
end
|
||||
end
|
||||
|
||||
describe '#run_check' do
|
||||
it 'prints check name' do
|
||||
expect(SimpleCheck).to receive(:display_name).and_call_original
|
||||
expect { subject.run_check(SimpleCheck) }.to output(/my simple check/).to_stdout
|
||||
end
|
||||
|
||||
context 'when check pass' do
|
||||
it 'prints yes' do
|
||||
expect_any_instance_of(SimpleCheck).to receive(:check?).and_call_original
|
||||
expect { subject.run_check(SimpleCheck) }.to output(/ \.\.\. yes/).to_stdout
|
||||
end
|
||||
end
|
||||
|
||||
context 'when check fails' do
|
||||
it 'prints no' do
|
||||
expect_any_instance_of(OtherCheck).to receive(:check?).and_call_original
|
||||
expect { subject.run_check(OtherCheck) }.to output(/ \.\.\. no/).to_stdout
|
||||
end
|
||||
|
||||
it 'displays error message from #show_error' do
|
||||
expect_any_instance_of(OtherCheck).to receive(:show_error).and_call_original
|
||||
expect { subject.run_check(OtherCheck) }.to output(/this is an error text/).to_stdout
|
||||
end
|
||||
|
||||
context 'when check implements #repair!' do
|
||||
it 'executes #repair!' do
|
||||
expect_any_instance_of(RepairCheck).to receive(:repair!)
|
||||
|
||||
subject.run_check(RepairCheck)
|
||||
end
|
||||
|
||||
context 'when repair succeeds' do
|
||||
it 'does not execute #show_error' do
|
||||
expect_any_instance_of(RepairCheck).to receive(:repair!).and_call_original
|
||||
expect_any_instance_of(RepairCheck).not_to receive(:show_error)
|
||||
|
||||
subject.run_check(RepairCheck)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when repair fails' do
|
||||
it 'does not execute #show_error' do
|
||||
expect_any_instance_of(RepairCheck).to receive(:repair!) { false }
|
||||
expect_any_instance_of(RepairCheck).to receive(:show_error)
|
||||
|
||||
subject.run_check(RepairCheck)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when check implements skip?' do
|
||||
it 'executes #skip? method' do
|
||||
expect_any_instance_of(SkipCheck).to receive(:skip?).and_call_original
|
||||
|
||||
subject.run_check(SkipCheck)
|
||||
end
|
||||
|
||||
it 'displays #skip_reason' do
|
||||
expect { subject.run_check(SkipCheck) }.to output(/this is a skip reason/).to_stdout
|
||||
end
|
||||
|
||||
it 'does not execute #check when #skip? is true' do
|
||||
expect_any_instance_of(SkipCheck).not_to receive(:check?)
|
||||
|
||||
subject.run_check(SkipCheck)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when implements a #multi_check' do
|
||||
it 'executes #multi_check method' do
|
||||
expect_any_instance_of(MultiCheck).to receive(:multi_check)
|
||||
|
||||
subject.run_check(MultiCheck)
|
||||
end
|
||||
|
||||
it 'does not execute #check method' do
|
||||
expect_any_instance_of(MultiCheck).not_to receive(:check)
|
||||
|
||||
subject.run_check(MultiCheck)
|
||||
end
|
||||
|
||||
context 'when check implements #skip?' do
|
||||
it 'executes #skip? method' do
|
||||
expect_any_instance_of(SkipMultiCheck).to receive(:skip?).and_call_original
|
||||
|
||||
subject.run_check(SkipMultiCheck)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
spec/lib/system_check_spec.rb
Normal file
36
spec/lib/system_check_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
require 'spec_helper'
|
||||
require 'rake_helper'
|
||||
|
||||
describe SystemCheck, lib: true do
|
||||
class SimpleCheck < SystemCheck::BaseCheck
|
||||
def check?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class OtherCheck < SystemCheck::BaseCheck
|
||||
def check?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
silence_output
|
||||
end
|
||||
|
||||
describe '.run' do
|
||||
subject { SystemCheck }
|
||||
|
||||
it 'detects execution of SimpleCheck' do
|
||||
is_expected.to execute_check(SimpleCheck)
|
||||
|
||||
subject.run('Test', [SimpleCheck])
|
||||
end
|
||||
|
||||
it 'detects exclusion of OtherCheck in execution' do
|
||||
is_expected.not_to execute_check(OtherCheck)
|
||||
|
||||
subject.run('Test', [SimpleCheck])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,6 +26,9 @@ if ENV['CI'] && !ENV['NO_KNAPSACK']
|
|||
Knapsack::Adapters::RSpecAdapter.bind
|
||||
end
|
||||
|
||||
# require rainbow gem String monkeypatch, so we can test SystemChecks
|
||||
require 'rainbow/ext/string'
|
||||
|
||||
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||
# in spec/support/ and its subdirectories.
|
||||
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
|
||||
|
|
23
spec/support/matchers/execute_check.rb
Normal file
23
spec/support/matchers/execute_check.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
RSpec::Matchers.define :execute_check do |expected|
|
||||
match do |actual|
|
||||
expect(actual).to eq(SystemCheck)
|
||||
expect(actual).to receive(:run) do |*args|
|
||||
expect(args[1]).to include(expected)
|
||||
end
|
||||
end
|
||||
|
||||
match_when_negated do |actual|
|
||||
expect(actual).to eq(SystemCheck)
|
||||
expect(actual).to receive(:run) do |*args|
|
||||
expect(args[1]).not_to include(expected)
|
||||
end
|
||||
end
|
||||
|
||||
failure_message do |actual|
|
||||
'This matcher must be used with SystemCheck' unless actual == SystemCheck
|
||||
end
|
||||
|
||||
failure_message_when_negated do |actual|
|
||||
'This matcher must be used with SystemCheck' unless actual == SystemCheck
|
||||
end
|
||||
end
|
|
@ -7,4 +7,9 @@ module RakeHelpers
|
|||
def stub_warn_user_is_not_gitlab
|
||||
allow_any_instance_of(Object).to receive(:warn_user_is_not_gitlab)
|
||||
end
|
||||
|
||||
def silence_output
|
||||
allow($stdout).to receive(:puts)
|
||||
allow($stdout).to receive(:print)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue