From 13e88c93956b5b350515b919ef7217a3dccf28ff Mon Sep 17 00:00:00 2001
From: Gabriel Mazetto <brodock@gmail.com>
Date: Thu, 25 May 2017 16:16:25 +0200
Subject: [PATCH] Refactor gitlab:app:checks to use SystemCheck

---
 .../app/database_config_exists_check.rb       |  31 ++
 lib/system_check/app/git_config_check.rb      |  50 ++
 lib/system_check/app/git_version_check.rb     |  29 +
 .../app/gitlab_config_exists_check.rb         |  24 +
 .../app/gitlab_config_not_outdated_check.rb   |  32 ++
 .../app/init_script_exists_check.rb           |  27 +
 .../app/init_script_up_to_date_check.rb       |  44 ++
 lib/system_check/app/log_writable_check.rb    |  28 +
 .../app/migrations_are_up_check.rb            |  20 +
 .../app/orphaned_group_members_check.rb       |  20 +
 .../app/projects_have_namespace_check.rb      |  37 ++
 lib/system_check/app/redis_version_check.rb   |  25 +
 lib/system_check/app/ruby_version_check.rb    |  27 +
 lib/system_check/app/tmp_writable_check.rb    |  28 +
 .../app/uploads_directory_exists_check.rb     |  21 +
 .../app/uploads_path_permission_check.rb      |  36 ++
 .../app/uploads_path_tmp_permission_check.rb  |  40 ++
 lib/system_check/base_check.rb                | 138 +++--
 lib/system_check/helpers.rb                   |  80 +++
 lib/system_check/simple_executor.rb           |  49 +-
 lib/tasks/gitlab/check.rake                   | 495 ++----------------
 lib/tasks/gitlab/task_helpers.rb              |   1 +
 22 files changed, 778 insertions(+), 504 deletions(-)
 create mode 100644 lib/system_check/app/database_config_exists_check.rb
 create mode 100644 lib/system_check/app/git_config_check.rb
 create mode 100644 lib/system_check/app/git_version_check.rb
 create mode 100644 lib/system_check/app/gitlab_config_exists_check.rb
 create mode 100644 lib/system_check/app/gitlab_config_not_outdated_check.rb
 create mode 100644 lib/system_check/app/init_script_exists_check.rb
 create mode 100644 lib/system_check/app/init_script_up_to_date_check.rb
 create mode 100644 lib/system_check/app/log_writable_check.rb
 create mode 100644 lib/system_check/app/migrations_are_up_check.rb
 create mode 100644 lib/system_check/app/orphaned_group_members_check.rb
 create mode 100644 lib/system_check/app/projects_have_namespace_check.rb
 create mode 100644 lib/system_check/app/redis_version_check.rb
 create mode 100644 lib/system_check/app/ruby_version_check.rb
 create mode 100644 lib/system_check/app/tmp_writable_check.rb
 create mode 100644 lib/system_check/app/uploads_directory_exists_check.rb
 create mode 100644 lib/system_check/app/uploads_path_permission_check.rb
 create mode 100644 lib/system_check/app/uploads_path_tmp_permission_check.rb
 create mode 100644 lib/system_check/helpers.rb

diff --git a/lib/system_check/app/database_config_exists_check.rb b/lib/system_check/app/database_config_exists_check.rb
new file mode 100644
index 00000000000..d557cee47b4
--- /dev/null
+++ b/lib/system_check/app/database_config_exists_check.rb
@@ -0,0 +1,31 @@
+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(
+          see_database_guide,
+          'http://guides.rubyonrails.org/getting_started.html#configuring-a-database'
+        )
+        fix_and_rerun
+      end
+
+      private
+
+      def see_database_guide
+        'doc/install/databases.md'
+      end
+    end
+  end
+end
diff --git a/lib/system_check/app/git_config_check.rb b/lib/system_check/app/git_config_check.rb
new file mode 100644
index 00000000000..7f0c792eb35
--- /dev/null
+++ b/lib/system_check/app/git_config_check.rb
@@ -0,0 +1,50 @@
+module SystemCheck
+  module App
+    class GitConfigCheck < SystemCheck::BaseCheck
+      OPTIONS = {
+        'core.autocrlf' => 'input'
+      }.freeze
+
+      set_name 'Git configured with autocrlf=input?'
+
+      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
+
+      def repair!
+        auto_fix_git_config(OPTIONS)
+      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
+
+      private
+
+      # 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
+    end
+  end
+end
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
new file mode 100644
index 00000000000..3a128f00911
--- /dev/null
+++ b/lib/system_check/app/git_version_check.rb
@@ -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
+        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
diff --git a/lib/system_check/app/gitlab_config_exists_check.rb b/lib/system_check/app/gitlab_config_exists_check.rb
new file mode 100644
index 00000000000..247aa0994e4
--- /dev/null
+++ b/lib/system_check/app/gitlab_config_exists_check.rb
@@ -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
diff --git a/lib/system_check/app/gitlab_config_not_outdated_check.rb b/lib/system_check/app/gitlab_config_not_outdated_check.rb
new file mode 100644
index 00000000000..8a4d7b29977
--- /dev/null
+++ b/lib/system_check/app/gitlab_config_not_outdated_check.rb
@@ -0,0 +1,32 @@
+module SystemCheck
+  module App
+    class GitlabConfigNotOutdatedCheck < SystemCheck::BaseCheck
+      set_name 'GitLab config outdated?'
+      set_check_pass 'no'
+      set_check_fail 'yes'
+      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(
+          '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
+  end
+end
diff --git a/lib/system_check/app/init_script_exists_check.rb b/lib/system_check/app/init_script_exists_check.rb
new file mode 100644
index 00000000000..d246e058e86
--- /dev/null
+++ b/lib/system_check/app/init_script_exists_check.rb
@@ -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
diff --git a/lib/system_check/app/init_script_up_to_date_check.rb b/lib/system_check/app/init_script_up_to_date_check.rb
new file mode 100644
index 00000000000..28c1451b8d2
--- /dev/null
+++ b/lib/system_check/app/init_script_up_to_date_check.rb
@@ -0,0 +1,44 @@
+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)
+          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)
+          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
diff --git a/lib/system_check/app/log_writable_check.rb b/lib/system_check/app/log_writable_check.rb
new file mode 100644
index 00000000000..3e0c436d6ee
--- /dev/null
+++ b/lib/system_check/app/log_writable_check.rb
@@ -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
diff --git a/lib/system_check/app/migrations_are_up_check.rb b/lib/system_check/app/migrations_are_up_check.rb
new file mode 100644
index 00000000000..5eedbacce77
--- /dev/null
+++ b/lib/system_check/app/migrations_are_up_check.rb
@@ -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
diff --git a/lib/system_check/app/orphaned_group_members_check.rb b/lib/system_check/app/orphaned_group_members_check.rb
new file mode 100644
index 00000000000..2b46d36fe51
--- /dev/null
+++ b/lib/system_check/app/orphaned_group_members_check.rb
@@ -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
diff --git a/lib/system_check/app/projects_have_namespace_check.rb b/lib/system_check/app/projects_have_namespace_check.rb
new file mode 100644
index 00000000000..d21ae225b0b
--- /dev/null
+++ b/lib/system_check/app/projects_have_namespace_check.rb
@@ -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
+        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)
+            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
diff --git a/lib/system_check/app/redis_version_check.rb b/lib/system_check/app/redis_version_check.rb
new file mode 100644
index 00000000000..a0610e73576
--- /dev/null
+++ b/lib/system_check/app/redis_version_check.rb
@@ -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
diff --git a/lib/system_check/app/ruby_version_check.rb b/lib/system_check/app/ruby_version_check.rb
new file mode 100644
index 00000000000..37b4d24aa55
--- /dev/null
+++ b/lib/system_check/app/ruby_version_check.rb
@@ -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, 1, 0)
+      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
diff --git a/lib/system_check/app/tmp_writable_check.rb b/lib/system_check/app/tmp_writable_check.rb
new file mode 100644
index 00000000000..99a75e57abf
--- /dev/null
+++ b/lib/system_check/app/tmp_writable_check.rb
@@ -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
diff --git a/lib/system_check/app/uploads_directory_exists_check.rb b/lib/system_check/app/uploads_directory_exists_check.rb
new file mode 100644
index 00000000000..7026d0ba075
--- /dev/null
+++ b/lib/system_check/app/uploads_directory_exists_check.rb
@@ -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
diff --git a/lib/system_check/app/uploads_path_permission_check.rb b/lib/system_check/app/uploads_path_permission_check.rb
new file mode 100644
index 00000000000..7df6c060254
--- /dev/null
+++ b/lib/system_check/app/uploads_path_permission_check.rb
@@ -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
diff --git a/lib/system_check/app/uploads_path_tmp_permission_check.rb b/lib/system_check/app/uploads_path_tmp_permission_check.rb
new file mode 100644
index 00000000000..b276a81eac1
--- /dev/null
+++ b/lib/system_check/app/uploads_path_tmp_permission_check.rb
@@ -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
diff --git a/lib/system_check/base_check.rb b/lib/system_check/base_check.rb
index d465711d719..aff830f54d7 100644
--- a/lib/system_check/base_check.rb
+++ b/lib/system_check/base_check.rb
@@ -2,16 +2,103 @@ module SystemCheck
   # Base class for Checks. You must inherit from here
   # and implement the methods below when necessary
   class BaseCheck
+    include ::Gitlab::TaskHelpers
+    include 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 the check passed or not
+    # @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:
@@ -23,50 +110,21 @@ module SystemCheck
       raise NotImplementedError
     end
 
-    # If skip returns true, than no other method on this check will be executed
-    #
-    # @return [Boolean] whether or not this check should be skipped
-    def skip?
-      false
-    end
-
-    # If you enabled #skip? here is where you define a custom message explaining why
-    #
-    # Do not print anything to STDOUT, return a string.
-    #
-    # @return [String] message why this check was skipped
-    def skip_message
+    # When implemented by a subclass, will attempt to fix the issue automatically
+    def repair!
+      raise NotImplementedError
     end
 
-    protected
-
-    # Display a formatted list of instructions on how to fix the issue identified by the #check?
+    # When implemented by a subclass, will evaluate whether check should be skipped or not
     #
-    # @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
-
-    # 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)
+    # @return [Boolean] whether or not this check should be skipped
+    def skip?
+      raise NotImplementedError
     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)
-      sources = sources.shift if sources.first.is_a?(Array)
-
-      $stdout.puts '  For more information see:'.color(:blue)
-      sources.each do |source|
-        $stdout.puts '  #{source}'
-      end
+    def self.call_or_return(input)
+      input.respond_to?(:call) ? input.call : input
     end
+    private_class_method :call_or_return
   end
 end
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
new file mode 100644
index 00000000000..91454c99838
--- /dev/null
+++ b/lib/system_check/helpers.rb
@@ -0,0 +1,80 @@
+module SystemCheck
+  module Helpers
+    # 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)
+      sources = sources.shift if sources.first.is_a?(Array)
+
+      $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 gitlab_user
+      Gitlab.config.gitlab.user
+    end
+
+    def sudo_gitlab(command)
+      "sudo -u #{gitlab_user} -H #{command}"
+    end
+  end
+end
+
+
+
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
index fc07f09dcb2..7a5042047ab 100644
--- a/lib/system_check/simple_executor.rb
+++ b/lib/system_check/simple_executor.rb
@@ -10,20 +10,51 @@ module SystemCheck
       start_checking(component)
 
       @checks.each do |check|
-        $stdout.print "#{check.name}"
-        if check.skip?
-          $stdout.puts "skipped #{'(' + skip_message + ')' if skip_message}".color(:magenta)
-        elsif check.check?
-          $stdout.puts 'yes'.color(:green)
-        else
-          $stdout.puts 'no'.color(:red)
-          check.show_error
-        end
+        run_check(check)
       end
 
       finished_checking(component)
     end
 
+    # Executes a single check
+    #
+    # @param [SystemCheck::BaseCheck] check
+    def run_check(check)
+      $stdout.print "#{check.display_name} ... "
+
+      c = check.new
+
+      # When implements a multi check, we don't control the output
+      if c.is_multi_check?
+        c.multi_check
+        return
+      end
+
+      # When implements skip method, we run it first, and if true, skip the check
+      if c.can_skip? && c.skip?
+        $stdout.puts check.skip_reason.color(:magenta)
+        return
+      end
+
+      if c.check?
+        $stdout.puts check.check_pass.color(:green)
+      else
+        $stdout.puts check.check_fail.color(:red)
+
+        if c.can_repair?
+          $stdout.print 'Trying to fix error automatically. ...'
+          if c.repair!
+            $stdout.puts 'Success'.color(:green)
+            return
+          else
+            $stdout.puts 'Failed'.color(:red)
+          end
+        end
+
+        c.show_error
+      end
+    end
+
     private
 
     # Prints header content for the series of checks to be executed for this component
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 88ec9cbc8cc..0d177663066 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -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
-
-      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
+      checks = [
+        SystemCheck::App::GitConfigCheck,
+        SystemCheck::App::DatabaseConfigExistsCheck,
+        SystemCheck::App::MigrationsAreUpCheck,
+        SystemCheck::App::OrphanedGroupMembersCheck,
+        SystemCheck::App::GitlabConfigExistsCheck,
+        SystemCheck::App::GitlabConfigNotOutdatedCheck,
+        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.run('GitLab', checks)
+      check_active_users
     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,60 +542,6 @@ namespace :gitlab do
   # Helper methods
   ##########################
 
-  # @deprecated Please use SystemChecks
-  def fix_and_rerun
-    puts "  Please fix the error above and rerun the checks.".color(:red)
-  end
-
-  # @deprecated Please use SystemChecks
-  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
-
-  # @deprecated Please use SystemChecks
-  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
-
-  # @deprecated Please use SystemChecks
-  def start_checking(component)
-    puts "Checking #{component.color(:yellow)} ..."
-    puts ""
-  end
-
-  # @deprecated Please use SystemChecks
-  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)
@@ -914,65 +554,10 @@ 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)}"
 
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
index e3c9d3b491c..e38e21b149f 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -113,6 +113,7 @@ module Gitlab
       end
     end
 
+    # TODO: MIGRATED
     # Tries to configure git itself
     #
     # Returns true if all subcommands were successfull (according to their exit code)
-- 
2.30.9