From 87a2b6ae207144a5aa017ddd1ef375cb50a92cc0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 29 May 2016 14:43:53 -0700 Subject: [PATCH] Relax version constraints for restoring backups and invoke db:migrate after restore 1. Parse backup and app versions: CE/EE, major/minor/patch 2. Abort if backup is EE and app is CE: this is likely to be an admin mistake. All other CE/EE combinations are OK. 3. Abort if backup major/minor/patch is newer than app Closes #3366 --- CHANGELOG | 1 + lib/backup/manager.rb | 72 +++++++++++++++++++++++---- lib/tasks/gitlab/backup.rake | 1 + spec/lib/backup/manager_spec.rb | 51 +++++++++++++++++++ spec/tasks/gitlab/backup_rake_spec.rb | 3 ++ 5 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 spec/lib/backup/manager_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 3e8b0a2af015..96b5e1d55f63 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ v 8.9.0 (unreleased) - Add DB index on users.state - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database - Changed the Slack build message to use the singular duration if necessary (Aran Koning) + - Relax version constraints for restoring backups - Fix issues filter when ordering by milestone - Todos will display target state if issuable target is 'Closed' or 'Merged' - Remove 'main language' feature diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 3e07096e6cc8..fd7d27c4aef2 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -133,15 +133,7 @@ def unpack ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0 # restoring mismatching backups can lead to unexpected problems - if settings[:gitlab_version] != Gitlab::VERSION - puts "GitLab version mismatch:".red - puts " Your current GitLab version (#{Gitlab::VERSION}) differs from the GitLab version in the backup!".red - puts " Please switch to the following version and try again:".red - puts " version: #{settings[:gitlab_version]}".red - puts - puts "Hint: git checkout v#{settings[:gitlab_version]}" - exit 1 - end + exit 1 unless upgradeable_version?(settings[:gitlab_version]) end def tar_version @@ -153,8 +145,54 @@ def skipped?(item) settings[:skipped] && settings[:skipped].include?(item) || disabled_features.include?(item) end + def upgradeable_version?(backup_text) + # 1. Parse backup and app versions: CE/EE, major/minor/patch + # 2. Abort if backup is EE and app is CE: this is likely to be an admin mistake. + # All other CE/EE combinations are OK. + # 3. Abort if backup major/minor/patch is newer than app + parsed = parse_gitlab_version(backup_text) + + unless parsed + puts "Unable to parse backup version: #{backup_text}".red + return false + end + + current = parse_gitlab_version(gitlab_version) + + unless current + puts "Unable to parse current GitLab version: #{gitlab_version}".red + return false + end + + from_version = Gem::Version.new(parsed[1]) + to_version = Gem::Version.new(current[1]) + + if ee_version?(backup_text) && !ee_version?(gitlab_version) + from_type = gitlab_edition(backup_text) + to_type = gitlab_edition(gitlab_version) + puts "GitLab EE->CE conversion mismatch:".red + puts " It looks like you may be trying to restore a backup from #{from_type} into #{to_type}.".red + puts " Your backup version (#{backup_text}) is while the installed GitLab version is (#{gitlab_version}).".red + puts " Please install #{from_type} and try restoring again.".red + return false + end + + if from_version > to_version + puts "GitLab version error:".red + puts " Your backup version (#{backup_text}) is ahead of the installed GitLab version (#{gitlab_version}).".red + puts " Please upgrade to a more recent version and try restoring again.".red + return false + end + + true + end + private + def gitlab_version + Gitlab::VERSION + end + def backup_contents folders_to_backup + archives_to_backup + ["backup_information.yml"] end @@ -176,5 +214,21 @@ def disabled_features def settings @settings ||= YAML.load_file("backup_information.yml") end + + def ee_version?(text) + text.include?('-ee') + end + + def gitlab_edition(text) + if ee_version?(text) + 'GitLab Enterprise Edition (EE)' + else + 'GitLab Community Edition (CE)' + end + end + + def parse_gitlab_version(version) + version.match('(\d+\.\d+\.\d+)(.*)') + end end end diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 596eaca6d0d6..cdfcd8c3c212 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -49,6 +49,7 @@ namespace :gitlab do Rake::Task['gitlab:db:drop_tables'].invoke $progress.puts 'done'.green Rake::Task['gitlab:backup:db:restore'].invoke + Rake::Task['db:migrate'].invoke end Rake::Task['gitlab:backup:repo:restore'].invoke unless backup.skipped?('repositories') Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads') diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb new file mode 100644 index 000000000000..7fd652a52c41 --- /dev/null +++ b/spec/lib/backup/manager_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Backup::Manager do + before do + # Suppress console output + allow_any_instance_of(Backup::Manager).to receive(:puts) + end + + context 'CE version installed' do + before do + allow_any_instance_of(Backup::Manager).to receive(:gitlab_version).and_return('8.9.1') + end + + it 'permits backup versions older than installed version and disallows EE->CE restore' do + manager = Backup::Manager.new + + expect(manager.upgradeable_version?('7.14.0')).to be_truthy + expect(manager.upgradeable_version?('8.8.0')).to be_truthy + expect(manager.upgradeable_version?('8.9.0')).to be_truthy + expect(manager.upgradeable_version?('8.9.1')).to be_truthy + expect(manager.upgradeable_version?('8.9.2')).to be_falsey + expect(manager.upgradeable_version?('8.10.0')).to be_falsey + expect(manager.upgradeable_version?('9.0.0')).to be_falsey + expect(manager.upgradeable_version?('8.8.0-ee')).to be_falsey + expect(manager.upgradeable_version?('8.9.0-ee')).to be_falsey + expect(manager.upgradeable_version?('8.9.1-ee')).to be_falsey + end + end + + context 'EE version installed' do + before do + allow_any_instance_of(Backup::Manager).to receive(:gitlab_version).and_return('8.9.1-ee') + end + + it 'permits CE->EE upgrades and backup versions older than installed version' do + manager = Backup::Manager.new + expect(manager.upgradeable_version?('8.9.1')).to be_truthy + + expect(manager.upgradeable_version?('7.14.0-ee')).to be_truthy + expect(manager.upgradeable_version?('8.8.0-ee')).to be_truthy + expect(manager.upgradeable_version?('8.9.0-ee')).to be_truthy + expect(manager.upgradeable_version?('8.9.1-ee')).to be_truthy + expect(manager.upgradeable_version?('8.9.2-ee')).to be_falsey + expect(manager.upgradeable_version?('8.10.0-ee')).to be_falsey + expect(manager.upgradeable_version?('9.0.0-ee')).to be_falsey + expect(manager.upgradeable_version?('8.8.0')).to be_truthy + expect(manager.upgradeable_version?('8.9.0')).to be_truthy + expect(manager.upgradeable_version?('8.9.1')).to be_truthy + end + end +end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 25da0917134c..832eb326e484 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -5,6 +5,7 @@ let(:enable_registry) { true } before :all do + Rake.application.rake_require 'active_record/railties/databases' Rake.application.rake_require 'tasks/gitlab/task_helpers' Rake.application.rake_require 'tasks/gitlab/backup' Rake.application.rake_require 'tasks/gitlab/shell' @@ -48,6 +49,8 @@ def reenable_backup_sub_tasks allow(FileUtils).to receive(:mv).and_return(true) allow(Rake::Task["gitlab:shell:setup"]). to receive(:invoke).and_return(true) + allow(Rake::Task['db:migrate']).to receive(:invoke).and_return(true) + ENV['force'] = 'yes' end -- GitLab