files/gitlab-ctl-commands/lib/gitlab_ctl/backup.rb (95 lines of code) (raw):
module GitlabCtl
class Backup
attr_reader :etc_backup_path, :etc_path, :backup_keep_time, :remove_timestamp
def initialize(options = {})
backup_path = options[:backup_path].nil? ? '/etc/gitlab/config_backup' : options[:backup_path]
@etc_backup_path = File.expand_path(backup_path)
@etc_path = '/etc/gitlab'
# Earlier, the attribute was named `gitlab-rails`, but it in 15.11, was
# changed to `gitlab_rails`. Hence we try both.
@backup_keep_time = (node_attributes.dig('gitlab', 'gitlab_rails', 'backup_keep_time') || node_attributes.dig('gitlab', 'gitlab-rails', 'backup_keep_time')).to_i
@remove_timestamp = Time.now - @backup_keep_time
@delete_old_backups = options[:delete_old_backups]
@removable_archives = []
end
# attribute methods
def archive_path
@archive_path = File.join(etc_backup_path, archive_name)
end
def archive_name
@archive_name ||= "gitlab_config_#{Time.now.strftime('%s_%Y_%m_%d')}.tar"
end
def node_attributes
@node_attributes ||= GitlabCtl::Util.get_node_attributes
rescue GitlabCtl::Errors::NodeError => e
warn(e.message)
warn("Defaulting to keeping all backups")
{}
end
def wants_pruned
@delete_old_backups.nil? ? true : @delete_old_backups
end
def removable_archives
return @removable_archives unless @removable_archives.empty?
Dir.chdir(@etc_backup_path) do
Dir.glob("gitlab_config_*.tar").map do |file_name|
next unless file_name =~ %r{gitlab_config_(\d{10})_(\d{4}_\d{2}_\d{2}).tar}
file_timestamp = Regexp.last_match(1).to_i
next if @backup_keep_time.zero?
next if Time.at(file_timestamp) >= @remove_timestamp
file_path = File.expand_path(file_name, @etc_backup_path)
@removable_archives.push(file_path)
end
end
@removable_archives
end
# class methods
def self.perform(options = {})
backup = new(options)
backup.perform
backup.prune
end
def prune
if wants_pruned && backup_keep_time.positive?
remove_backups
else
puts "Keeping all older configuration backups"
end
end
def perform(options = {})
abort "Could not find '#{etc_path}' directory. Is your package installed correctly?" unless File.exist?(etc_path)
unless File.exist?(etc_backup_path)
puts "Could not find '#{etc_backup_path}' directory. Creating."
FileUtils.mkdir(etc_backup_path, mode: 0700)
begin
FileUtils.chown('root', 'root', etc_backup_path)
rescue Errno::EPERM
warn("Warning: Could not change owner of #{etc_backup_path} to 'root:root'. As a result your " \
'backups may be accessible to some non-root users.')
end
end
warn("WARNING: #{etc_backup_path} may be read by non-root users") unless secure?(etc_backup_path)
puts "Running configuration backup\nCreating configuration backup archive: #{archive_name}"
command = %W(tar --absolute-names --dereference --verbose --create --file #{archive_path}
--exclude #{etc_backup_path} -- #{etc_path})
status = system(*command)
FileUtils.chmod(0600, archive_path) if File.exist?(archive_path)
exit!(1) unless status
puts "Configuration backup archive complete: #{archive_path}"
end
def remove_backups
# delete old backups
removed_count = 0
puts "Removing configuration backups older than #{@remove_timestamp} ..."
removable_archives.each do |archive_file|
FileUtils.rm(archive_file)
puts " Removed #{archive_file}"
removed_count += 1
rescue StandardError => e
warn("WARNING: Deleting file #{archive_file} failed: #{e.message}")
end
puts "done. Removed #{removed_count} older configuration backups."
end
def secure?(path)
stat_data = File.stat(path)
return false if stat_data.uid != 0
return false unless stat_data.world_readable?.nil?
true
end
end
end