Files
gitlab-foss/gems/gitlab-backup-cli/lib/gitlab/backup/cli/backup_executor.rb
2024-10-08 06:18:18 +00:00

130 lines
4.2 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Backup
module Cli
# This is responsible for executing a Backup operation
#
# A Backup Executor handles the creation and deletion of
# temporary environment necessary for a backup to happen
#
# It also allows for multiple backups to happen in parallel
# without one overwriting data from another
class BackupExecutor < BaseExecutor
attr_reader :context, :metadata, :workdir, :archive_directory
# @param [Gitlab::Backup::Cli::SourceContext, Context::OmnibusContext] context
def initialize(
context:,
backup_bucket: nil,
wait_for_completion: nil,
registry_bucket: nil,
service_account_file: nil)
@context = context
@metadata = build_metadata
@workdir = create_temporary_workdir!
@archive_directory = context.backup_basedir.join(metadata.backup_id)
super(
backup_bucket: backup_bucket,
wait_for_completion: wait_for_completion,
registry_bucket: registry_bucket,
service_account_file: service_account_file
)
end
def execute
execute_all_tasks
write_metadata!
archive!
end
# At the end of a successful backup, call this to release temporary resources
def release!
FileUtils.rm_rf(workdir)
end
private
def build_metadata
@metadata = Gitlab::Backup::Cli::Metadata::BackupMetadata.build(gitlab_version: context.gitlab_version)
end
def execute_all_tasks
# TODO: when we migrate targets to the new codebase, recreate options to have only what we need here
# https://gitlab.com/gitlab-org/gitlab/-/issues/454906
options = ::Backup::Options.new(
remote_directory: backup_bucket,
container_registry_bucket: registry_bucket,
service_account_file: service_account_file
)
tasks = []
Gitlab::Backup::Cli::Tasks.build_each(context: context, options: options) do |task|
Gitlab::Backup::Cli::Output.info("Executing Backup of #{task.human_name}...")
duration = measure_duration do
task.backup!(workdir, metadata.backup_id)
tasks << task
end
next if task.asynchronous?
Gitlab::Backup::Cli::Output.success("Finished Backup of #{task.human_name}! (#{duration.in_seconds}s)")
end
if wait_for_completion
tasks.each do |task|
wait_for_task(task)
end
else
Gitlab::Backup::Cli::Output.info('Backup tasks completed! Not waiting for object storage tasks to complete')
end
end
# Write the backup_information.json data to disk
def write_metadata!
return if metadata.write!(workdir)
raise Gitlab::Backup::Cli::Error, 'Failed to write metadata to disk'
end
def archive!
# TODO: create a single-file archive instead of moving everything to a directory
# https://gitlab.com/gitlab-org/gitlab/-/issues/454832
FileUtils.mkdir(archive_directory)
workdir.glob('*').each { |entry| FileUtils.mv(entry, archive_directory) }
end
# @return [Pathname] temporary directory
def create_temporary_workdir!
# Ensure base directory exists
FileUtils.mkdir_p(context.backup_basedir)
Pathname(Dir.mktmpdir('backup', context.backup_basedir))
end
def measure_duration
start = Time.now
yield
ActiveSupport::Duration.build(Time.now - start)
end
def wait_for_task(task)
return unless task.asynchronous?
Gitlab::Backup::Cli::Output.info("Waiting for Backup of #{task.human_name} to finish...")
r = task.wait_until_done!
if r.error?
Gitlab::Backup::Cli::Output.error("Backup of #{task.human_name} failed!")
else
Gitlab::Backup::Cli::Output.success("Finished Backup of #{task.human_name}!")
end
end
end
end
end
end