From 911db940d92192e975c85d077ac754bfd84529fa Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto <gabriel@gitlab.com> Date: Mon, 26 Feb 2024 00:38:05 +0100 Subject: [PATCH] Introduce Tasks migrated from old codebase --- .../lib/gitlab/backup/cli.rb | 1 + .../lib/gitlab/backup/cli/tasks.rb | 40 ++++++++++ .../lib/gitlab/backup/cli/tasks/artifacts.rb | 28 +++++++ .../lib/gitlab/backup/cli/tasks/builds.rb | 28 +++++++ .../backup/cli/tasks/ci_secure_files.rb | 28 +++++++ .../lib/gitlab/backup/cli/tasks/database.rb | 25 ++++++ .../lib/gitlab/backup/cli/tasks/lfs.rb | 28 +++++++ .../lib/gitlab/backup/cli/tasks/packages.rb | 28 +++++++ .../lib/gitlab/backup/cli/tasks/pages.rb | 32 ++++++++ .../lib/gitlab/backup/cli/tasks/registry.rb | 30 +++++++ .../gitlab/backup/cli/tasks/repositories.rb | 42 ++++++++++ .../lib/gitlab/backup/cli/tasks/task.rb | 78 +++++++++++++++++++ .../backup/cli/tasks/terraform_state.rb | 27 +++++++ .../lib/gitlab/backup/cli/tasks/uploads.rb | 27 +++++++ .../spec/gitlab/backup/cli/tasks/task_spec.rb | 57 ++++++++++++++ 15 files changed, 499 insertions(+) create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/artifacts.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/builds.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/ci_secure_files.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/database.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/lfs.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/packages.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/pages.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/registry.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/repositories.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/task.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/terraform_state.rb create mode 100644 gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/uploads.rb create mode 100644 gems/gitlab-backup-cli/spec/gitlab/backup/cli/tasks/task_spec.rb diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb index da939191df40..7259107a9176 100644 --- a/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb @@ -13,6 +13,7 @@ module Cli autoload :Output, 'gitlab/backup/cli/output' autoload :Runner, 'gitlab/backup/cli/runner' autoload :Shell, 'gitlab/backup/cli/shell' + autoload :Tasks, 'gitlab/backup/cli/tasks' autoload :Utils, 'gitlab/backup/cli/utils' autoload :VERSION, 'gitlab/backup/cli/version' diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks.rb new file mode 100644 index 000000000000..d5635c7a5265 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + autoload :Artifacts, 'gitlab/backup/cli/tasks/artifacts' + autoload :Builds, 'gitlab/backup/cli/tasks/builds' + autoload :CiSecureFiles, 'gitlab/backup/cli/tasks/ci_secure_files' + autoload :Database, 'gitlab/backup/cli/tasks/database' + autoload :Lfs, 'gitlab/backup/cli/tasks/lfs' + autoload :Registry, 'gitlab/backup/cli/tasks/registry' + autoload :Repositories, 'gitlab/backup/cli/tasks/repositories' + autoload :Packages, 'gitlab/backup/cli/tasks/packages' + autoload :Pages, 'gitlab/backup/cli/tasks/pages' + autoload :Task, 'gitlab/backup/cli/tasks/task' + autoload :TerraformState, 'gitlab/backup/cli/tasks/terraform_state' + autoload :Uploads, 'gitlab/backup/cli/tasks/uploads' + + TASKS = { + Gitlab::Backup::Cli::Tasks::Database.id => Gitlab::Backup::Cli::Tasks::Database, + Gitlab::Backup::Cli::Tasks::Repositories.id => Gitlab::Backup::Cli::Tasks::Repositories, + Gitlab::Backup::Cli::Tasks::Uploads.id => Gitlab::Backup::Cli::Tasks::Uploads, + Gitlab::Backup::Cli::Tasks::Builds.id => Gitlab::Backup::Cli::Tasks::Builds, + Gitlab::Backup::Cli::Tasks::Artifacts.id => Gitlab::Backup::Cli::Tasks::Artifacts, + Gitlab::Backup::Cli::Tasks::Pages.id => Gitlab::Backup::Cli::Tasks::Pages, + Gitlab::Backup::Cli::Tasks::Lfs.id => Gitlab::Backup::Cli::Tasks::Lfs, + Gitlab::Backup::Cli::Tasks::TerraformState.id => Gitlab::Backup::Cli::Tasks::TerraformState, + Gitlab::Backup::Cli::Tasks::Registry.id => Gitlab::Backup::Cli::Tasks::Registry, + Gitlab::Backup::Cli::Tasks::Packages.id => Gitlab::Backup::Cli::Tasks::Packages, + Gitlab::Backup::Cli::Tasks::CiSecureFiles.id => Gitlab::Backup::Cli::Tasks::CiSecureFiles + }.freeze + + def self.all + TASKS.values + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/artifacts.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/artifacts.rb new file mode 100644 index 000000000000..80620c536d42 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/artifacts.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Artifacts < Task + def self.id = 'artifacts' + + def human_name = _('artifacts') + + def destination_path = 'artifacts.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']) + end + + def storage_path + # TODO: Use configuration solver + JobArtifactUploader.root + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/builds.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/builds.rb new file mode 100644 index 000000000000..f2f75008fa97 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/builds.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Builds < Task + def self.id = 'builds' + + def human_name = _('builds') + + def destination_path = 'builds.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options) + end + + def storage_path + # TODO: Use configuration solver + Settings.gitlab_ci.builds_path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/ci_secure_files.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/ci_secure_files.rb new file mode 100644 index 000000000000..7c4b7d8a2ca6 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/ci_secure_files.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class CiSecureFiles < Task + def self.id = 'ci_secure_files' + + def human_name = _('ci secure files') + + def destination_path = 'ci_secure_files.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']) + end + + def storage_path + # TODO: Use configuration solver + Settings.ci_secure_files.storage_path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/database.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/database.rb new file mode 100644 index 000000000000..b67b2af958ac --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/database.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Database < Task + def self.id = 'db' + + def human_name = _('database') + + def destination_path = 'db' + + def cleanup_path = 'db' + + private + + def target + ::Backup::Targets::Database.new(output, options: options) + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/lfs.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/lfs.rb new file mode 100644 index 000000000000..3ed3d47baec5 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/lfs.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Lfs < Task + def self.id = 'lfs' + + def human_name = _('lfs objects') + + def destination_path = 'lfs.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options) + end + + def storage_path + # TODO: Use configuration solver + Settings.lfs.storage_path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/packages.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/packages.rb new file mode 100644 index 000000000000..398f93af0406 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/packages.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Packages < Task + def self.id = 'packages' + + def human_name = _('packages') + + def destination_path = 'packages.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']) + end + + def storage_path + # TODO: Use configuration solver + Settings.packages.storage_path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/pages.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/pages.rb new file mode 100644 index 000000000000..4c44ab600b32 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/pages.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Pages < Task + # pages used to deploy tmp files to this path + # if some of these files are still there, we don't need them in the backup + LEGACY_PAGES_TMP_PATH = '@pages.tmp' + + def self.id = 'pages' + + def human_name = _('pages') + + def destination_path = 'pages.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: [LEGACY_PAGES_TMP_PATH]) + end + + def storage_path + # TODO: Use configuration solver + Gitlab.config.pages.path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/registry.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/registry.rb new file mode 100644 index 000000000000..ee90782ce54d --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/registry.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Registry < Task + def self.id = 'registry' + + def enabled = Gitlab.config.registry.enabled + + def human_name = _('container registry images') + + def destination_path = 'registry.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options) + end + + def storage_path + # TODO: Use configuration solver + Settings.registry.path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/repositories.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/repositories.rb new file mode 100644 index 000000000000..f4d172d623e5 --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/repositories.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Repositories < Task + def self.id = 'repositories' + + def human_name = _('repositories') + + def destination_path = 'repositories' + + def destination_optional = true + + private + + def target + # TODO: migrate to the new codebase and rewrite portions to format output in a readable way + ::Backup::Targets::Repositories.new($stdout, + strategy: gitaly_strategy, + options: options, + storages: options.repositories_storages, + paths: options.repositories_paths, + skip_paths: options.skip_repositories_paths + ) + end + + def gitaly_strategy + # TODO: migrate to the new codebase and rewrite portions to format output in a readable way + ::Backup::GitalyBackup.new($stdout, + incremental: options.incremental?, + max_parallelism: options.max_parallelism, + storage_parallelism: options.max_storage_parallelism, + server_side: false + ) + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/task.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/task.rb new file mode 100644 index 000000000000..995703ee1f1a --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/task.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Task + attr_reader :options, :context + + # Identifier used as parameter in the CLI to skip from executing + def self.id + raise NotImplementedError + end + + def initialize(context:, options:) + @context = context + @options = options + end + + # Initiate a backup + # + # @param [Pathname] backup_path a path where to store the backups + # @param [String] backup_id + def backup!(backup_path, backup_id) + backup_output = backup_path.join(destination_path) + + target.dump(backup_output, backup_id) + end + + def restore!(backup_path, backup_id) + # TODO: enable this when building the restore functionality + # https://gitlab.com/gitlab-org/gitlab/-/issues/428505 + raise NotImplementedError + end + + # Key string that identifies the task + def id = self.class.id + + # Name of the task used for logging. + def human_name + raise NotImplementedError + end + + # Where the task should put its backup file/dir + def destination_path + raise NotImplementedError + end + + # Path to remove after a successful backup, uses #destination_path when not specified + def cleanup_path + destination_path + end + + # `true` if the destination might not exist on a successful backup + def destination_optional + false + end + + # `true` if the task can be used + def enabled + true + end + + def enabled? + enabled + end + + private + + # The target factory method + def target + raise NotImplementedError + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/terraform_state.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/terraform_state.rb new file mode 100644 index 000000000000..8504fd19bd8e --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/terraform_state.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class TerraformState < Task + def self.id = 'terraform_state' + + def human_name = _('terraform states') + + def destination_path = 'terraform_state.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']) + end + + def storage_path + Settings.terraform_state.storage_path + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/uploads.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/uploads.rb new file mode 100644 index 000000000000..6ca765e3ad2d --- /dev/null +++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/tasks/uploads.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Gitlab + module Backup + module Cli + module Tasks + class Uploads < Task + def self.id = 'uploads' + + def human_name = _('uploads') + + def destination_path = 'uploads.tar.gz' + + private + + def target + ::Backup::Targets::Files.new(nil, storage_path, options: options, excludes: ['tmp']) + end + + def storage_path + File.join(Gitlab.config.uploads.storage_path, 'uploads') + end + end + end + end + end +end diff --git a/gems/gitlab-backup-cli/spec/gitlab/backup/cli/tasks/task_spec.rb b/gems/gitlab-backup-cli/spec/gitlab/backup/cli/tasks/task_spec.rb new file mode 100644 index 000000000000..e578ff11740b --- /dev/null +++ b/gems/gitlab-backup-cli/spec/gitlab/backup/cli/tasks/task_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Backup::Cli::Tasks::Task do + let(:options) { nil } + let(:context) { build_fake_context } + let(:tmpdir) { Pathname.new(Dir.mktmpdir('task', temp_path)) } + let(:metadata) { build(:backup_metadata) } + + subject(:task) { described_class.new(options: options, context: context) } + + after do + FileUtils.rmtree(tmpdir) + end + + context 'with unimplemented methods' do + describe '.id' do + it 'raises an error' do + expect { described_class.id }.to raise_error(NotImplementedError) + end + end + + describe '#id' do + it 'raises an error' do + expect { task.id }.to raise_error(NotImplementedError) + end + end + + describe '#human_name' do + it 'raises an error' do + expect { task.human_name }.to raise_error(NotImplementedError) + end + end + + describe '#destination_path' do + it 'raises an error' do + expect { task.destination_path }.to raise_error(NotImplementedError) + end + end + + describe '#target' do + it 'raises an error' do + expect { task.send(:target) }.to raise_error(NotImplementedError) + end + end + end + + describe '#backup!' do + it 'delegates to target' do + expect(task).to receive(:destination_path).and_return(tmpdir.join('test_task')) + expect(task).to receive_message_chain(:target, :dump) + + task.backup!(tmpdir, metadata.backup_id) + end + end +end -- GitLab